Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions .github/scripts/fetch-deps-bundle.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
#!/usr/bin/env bash
#
# Fetch one platform's dependency bundle for a CI run and print the path of the
# Fetch ONE dependency release asset for a CI run and print the path of the
# downloaded .zip on stdout. Everything else goes to stderr, so the caller can
# do:
#
# ZIP=$(bash .github/scripts/fetch-deps-bundle.sh macos-arm64 deps-v1.9.0 1.9.0)
#
# The first argument is an asset id as it appears in the file name: a platform
# (`linux-x64`), or a v2 delta (`linux-x64-v2-delta`). Workflows should not
# call this directly — install-deps-bundle.sh knows which assets make up a
# platform's bundle (a v2 tier is two of them) and extracts them in order.
#
# Two sources, chosen by whether $DEPS_RUN_ID is set:
#
# (default) the published release named by app/assets/deps-version.json. This
Expand Down Expand Up @@ -45,7 +50,10 @@ TAG="${2:?missing release tag}"
VER="${3:?missing version}"

if [ -n "${DEPS_RUN_ID:-}" ]; then
rm -rf .deps-artifact
# One directory per asset: installing a v2 tier fetches two, and the second
# must not wipe the first.
ARTIFACT_DIR=".deps-artifact/$PLATFORM"
rm -rf "$ARTIFACT_DIR"
ZIP=""
FROM_RUN=""

Expand All @@ -55,13 +63,13 @@ if [ -n "${DEPS_RUN_ID:-}" ]; then
for RUN in $(echo "$DEPS_RUN_ID" | tr ',' ' '); do
echo "looking for a $PLATFORM artifact in run $RUN" >&2
if gh run download "$RUN" \
--pattern "VapourBox-deps-*-${PLATFORM}" --dir .deps-artifact >&2 2>/dev/null; then
--pattern "VapourBox-deps-*-${PLATFORM}" --dir "$ARTIFACT_DIR" >&2 2>/dev/null; then
# gh nests each artifact in a directory named after it, so glob rather
# than assuming a layout.
ZIP=$(find .deps-artifact -name "VapourBox-deps-*-${PLATFORM}.zip" | head -1)
ZIP=$(find "$ARTIFACT_DIR" -name "VapourBox-deps-*-${PLATFORM}.zip" | head -1)
if [ -n "$ZIP" ]; then FROM_RUN="$RUN"; break; fi
fi
rm -rf .deps-artifact
rm -rf "$ARTIFACT_DIR"
done

if [ -z "$ZIP" ]; then
Expand Down
53 changes: 53 additions & 0 deletions .github/scripts/install-deps-bundle.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
#
# Fetch a platform's dependency bundle and extract it into a directory:
#
# bash .github/scripts/install-deps-bundle.sh linux-x64-v2 deps-v1.13.0 1.13.0 deps/linux-x64
#
# The first argument names the bundle the way the test matrix does: a platform
# (`linux-x64`, `macos-arm64`), optionally suffixed `-v2` for the older-CPU tier
# of an x64 platform (issue #92).
#
# A v2 tier is not one download. From deps 1.13.0 (`"tierFormat": "delta"` in
# app/assets/deps-version.json) it is the platform's full bundle plus a small
# delta extracted over it — exactly what DependencyManager does on a user's
# machine, so CI installs it the same way rather than from some full v2 zip no
# user ever receives. This script is the one place in CI that knows that.
#
# Sources and $DEPS_RUN_ID are as documented in fetch-deps-bundle.sh.
set -euo pipefail

BUNDLE="${1:?usage: install-deps-bundle.sh <platform>[-v2] <release-tag> <version> <dest-dir>}"
TAG="${2:?missing release tag}"
VER="${3:?missing version}"
DEST="${4:?missing destination directory}"
HERE="$(cd "$(dirname "$0")" && pwd)"
POINTER="$HERE/../../app/assets/deps-version.json"

ASSETS=("$BUNDLE")
if [[ "$BUNDLE" == *-v2 ]] && grep -q '"tierFormat": *"delta"' "$POINTER"; then
ASSETS=("${BUNDLE%-v2}" "$BUNDLE-delta")
fi

mkdir -p "$DEST"
for ASSET in "${ASSETS[@]}"; do
ZIP=$(bash "$HERE/fetch-deps-bundle.sh" "$ASSET" "$TAG" "$VER")
[ -f "$ZIP" ] || { echo "::error::no zip for $ASSET" >&2; exit 1; }
echo "extracting $ZIP into $DEST" >&2
if [ "${RUNNER_OS:-}" = Windows ]; then
# The Windows bundle uses backslash separators; 7-Zip copes, info-zip
# `unzip` warns and exits non-zero.
7z x "$ZIP" -o"$DEST" -y >/dev/null
else
unzip -q -o "$ZIP" -d "$DEST"
fi
rm -f "$ZIP"
done
rm -rf .deps-artifact

# The delta carries the version.json that marks the tree as the v2 tier; if it
# did not land last, the tests would silently run the v3 bundle.
if [[ "$BUNDLE" == *-v2 ]] && ! grep -q '"tier": *"v2"' "$DEST/version.json"; then
echo "::error::$DEST/version.json does not say tier v2 after installing $BUNDLE" >&2
exit 1
fi
73 changes: 61 additions & 12 deletions .github/workflows/build-deps-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ permissions:

jobs:
# x64 ships in two CPU tiers (issue #92): v3 for x86-64-v3 CPUs, v2 for
# anything older. Same script, one --tier switch.
# anything older. Same script, one --tier switch. Both are built in full, but
# only v3 is published as a bundle: delta-x64 below reduces the v2 build to
# the few files that differ (Scripts/make-deps-delta.py).
build-x64:
if: ${{ inputs.arch == 'x64' || inputs.arch == 'both' }}
runs-on: ubuntu-24.04
Expand Down Expand Up @@ -62,45 +64,92 @@ jobs:
- name: Package dependencies
run: ./Scripts/package-deps-linux.sh --version "${{ inputs.version }}" --arch x64

# The v2 build is an intermediate ("-v2-build"): nothing downstream may
# mistake it for a publishable bundle.
- uses: actions/upload-artifact@v5
with:
name: ${{ env.ASSET }}
name: ${{ env.ASSET }}${{ matrix.tier == 'v2' && '-build' || '' }}
retention-days: ${{ matrix.tier == 'v2' && 3 || 90 }}
path: |
dist/${{ env.ASSET }}.zip
dist/${{ env.ASSET }}.zip.sha256.json

# The v2 bundle's promise is that it runs on CPUs without AVX. Every hosted
# Cut the v2 delta against the v3 bundle, and compose the tree a v2 user
# ends up with (v3 bundle + delta) for the gate to test.
delta-x64:
needs: build-x64
runs-on: ubuntu-24.04
env:
STEM: VapourBox-deps-${{ inputs.version }}-linux-x64
steps:
- uses: actions/checkout@v5
- uses: actions/download-artifact@v5
with:
name: ${{ env.STEM }}
path: base
- uses: actions/download-artifact@v5
with:
name: ${{ env.STEM }}-v2-build
path: v2-build
- name: Make the v2 delta
run: |
python3 Scripts/make-deps-delta.py --platform linux-x64 \
--version "${{ inputs.version }}" \
--base "base/$STEM.zip" --v2 "v2-build/$STEM-v2.zip" \
--out dist --composed composed
- uses: actions/upload-artifact@v5
with:
name: ${{ env.STEM }}-v2-delta
path: |
dist/${{ env.STEM }}-v2-delta.zip
dist/${{ env.STEM }}-v2-delta.zip.sha256.json
# What the gate probes. Named as the gate expects a v2 bundle to be; never
# uploaded to a release.
- uses: actions/upload-artifact@v5
with:
name: ${{ env.STEM }}-v2
retention-days: 3
path: composed/${{ env.STEM }}-v2.zip

# The v2 tier's promise is that it runs on CPUs without AVX. Every hosted
# runner has AVX2, so prove it by running every plugin under Intel SDE
# emulating Westmere (the Mac Pro 5,1 from issue #92).
# emulating Westmere (the Mac Pro 5,1 from issue #92) — against the v3 bundle
# with the delta applied, which is what such a machine actually installs.
gate-x64-v2:
needs: build-x64
needs: delta-x64
uses: ./.github/workflows/probe-cpu-compat.yml
with:
artifact_prefix: VapourBox-deps-${{ inputs.version }}-
tier: v2
gate: true
matrix_json: '[{"platform":"linux-x64","runner":"ubuntu-24.04","sde":"sde64","python":"python/bin/python3","chip":"wsm"}]'

# Published only once the v2 gate has passed, so a bundle that would crash on
# an older CPU never reaches a release.
# Published only once the v2 gate has passed, so a delta that would crash on
# an older CPU never reaches a release. Exactly four files: the v3 bundle,
# the v2 delta, and a sidecar each.
upload-x64:
needs: [build-x64, gate-x64-v2]
needs: [build-x64, delta-x64, gate-x64-v2]
if: inputs.release_tag != ''
runs-on: ubuntu-24.04
env:
STEM: VapourBox-deps-${{ inputs.version }}-linux-x64
steps:
- uses: actions/download-artifact@v5
with:
pattern: VapourBox-deps-${{ inputs.version }}-linux-x64*
name: ${{ env.STEM }}
path: dist
- uses: actions/download-artifact@v5
with:
name: ${{ env.STEM }}-v2-delta
path: dist
merge-multiple: true
- name: Upload to release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release upload "${{ inputs.release_tag }}" \
--repo "${{ github.repository }}" \
dist/VapourBox-deps-${{ inputs.version }}-linux-x64*.zip \
dist/VapourBox-deps-${{ inputs.version }}-linux-x64*.zip.sha256.json --clobber
"dist/$STEM.zip" "dist/$STEM.zip.sha256.json" \
"dist/$STEM-v2-delta.zip" "dist/$STEM-v2-delta.zip.sha256.json" --clobber

build-arm64:
if: ${{ inputs.arch == 'arm64' || inputs.arch == 'both' }}
Expand Down
62 changes: 55 additions & 7 deletions .github/workflows/build-deps-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,16 @@ jobs:
"dist/VapourBox-deps-${{ inputs.version }}-macos-arm64.zip.sha256.json" --clobber

# x64 ships in two CPU tiers (issue #92): v3 for x86-64-v3 CPUs, v2 for
# anything older. Same script, one --tier switch. The v2 bundle is gated by
# package-deps-macos.sh itself (Scripts/check-load-time-simd.py): macOS has
# no Intel SDE, so it proves statically that no plugin runs AVX while it
# loads — and fails before anything below can upload it.
# anything older. Same script, one --tier switch. Both are built in full, but
# only v3 is published as a bundle: delta-x64 below reduces the v2 build to
# the few files that differ (Scripts/make-deps-delta.py).
#
# The v2 build is gated by package-deps-macos.sh itself
# (Scripts/check-load-time-simd.py): macOS has no Intel SDE, so it proves
# statically that no plugin runs AVX while it loads — and fails this job, so
# nothing below can publish a delta cut from it. The check covers every
# plugin the v2 build holds; the ones outside the delta are the same sources
# and binaries the v3 bundle was built from.
build-x64:
if: ${{ inputs.arch == 'x64' || inputs.arch == 'both' }}
runs-on: macos-15-intel # only hosted Intel image (macos-13 retired; last one until ~Aug 2027)
Expand All @@ -87,18 +93,60 @@ jobs:
- name: Package dependencies
run: ./Scripts/package-deps-macos.sh --version "${{ inputs.version }}" --arch x64

# The v2 build is an intermediate ("-v2-build"): nothing downstream may
# mistake it for a publishable bundle.
- uses: actions/upload-artifact@v5
with:
name: ${{ env.ASSET }}
name: ${{ env.ASSET }}${{ matrix.tier == 'v2' && '-build' || '' }}
retention-days: ${{ matrix.tier == 'v2' && 3 || 90 }}
path: |
dist/${{ env.ASSET }}.zip
dist/${{ env.ASSET }}.zip.sha256.json

# Cut the v2 delta against the v3 bundle, then publish both. Runs on macOS so
# the composed tree can be put through the same load-time check the v2 build
# passed, this time on exactly what a v2 user ends up with.
delta-x64:
needs: build-x64
runs-on: macos-15
env:
STEM: VapourBox-deps-${{ inputs.version }}-macos-x64
steps:
- uses: actions/checkout@v5
- uses: actions/download-artifact@v5
with:
name: ${{ env.STEM }}
path: dist
- uses: actions/download-artifact@v5
with:
name: ${{ env.STEM }}-v2-build
path: v2-build
- name: Make the v2 delta
run: |
python3 Scripts/make-deps-delta.py --platform macos-x64 \
--version "${{ inputs.version }}" \
--base "dist/$STEM.zip" --v2 "v2-build/$STEM-v2.zip" \
--out dist --composed composed

- name: Gate the composed v2 tree (no AVX at load time)
run: |
mkdir composed-tree
unzip -q "composed/$STEM-v2.zip" -d composed-tree
python3 Scripts/check-load-time-simd.py composed-tree/vapoursynth/plugins/*.dylib

- uses: actions/upload-artifact@v5
with:
name: ${{ env.STEM }}-v2-delta
path: |
dist/${{ env.STEM }}-v2-delta.zip
dist/${{ env.STEM }}-v2-delta.zip.sha256.json

- name: Upload to release
if: inputs.release_tag != ''
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release upload "${{ inputs.release_tag }}" \
"dist/${{ env.ASSET }}.zip" \
"dist/${{ env.ASSET }}.zip.sha256.json" --clobber
--repo "${{ github.repository }}" \
"dist/$STEM.zip" "dist/$STEM.zip.sha256.json" \
"dist/$STEM-v2-delta.zip" "dist/$STEM-v2-delta.zip.sha256.json" --clobber
Loading
Loading