Repository navigation
ci(macos): reuse pinned Qt and load-test preview Vulkan libraries #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: MoltenVK arm64 preview | |
| on: | |
| push: | |
| branches: [feature/desktop-embedded-hdr] | |
| paths: [.github/workflows/moltenvk-preview.yml] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: moltenvk-preview-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| macos-dmg: | |
| name: macOS DMG (${{ matrix.arch }}) | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: arm64 | |
| runner: macos-14 | |
| steps: | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| submodules: recursive | |
| - uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34 | |
| with: | |
| nix_conf: | | |
| experimental-features = nix-command flakes | |
| keep-env-derivations = true | |
| keep-outputs = true | |
| # Keep the pinned dependency graph throughout the preview build. | |
| connect-timeout = 30 | |
| download-attempts = 5 | |
| - name: Resolve tailored Qt cache key | |
| id: qt-cache-inputs | |
| run: | | |
| qt_derivation="$(nix eval --raw .#native-qt-cache.drvPath)" | |
| echo "key=${qt_derivation##*/}" >>"$GITHUB_OUTPUT" | |
| - uses: nix-community/cache-nix-action@7df957e333c1e5da7721f60227dbba6d06080569 # v7.0.1 | |
| with: | |
| primary-key: nix-native-${{ runner.os }}-${{ matrix.arch }}-${{ steps.qt-cache-inputs.outputs.key }} | |
| restore-prefixes-first-match: nix-native-${{ runner.os }}-${{ matrix.arch }}- | |
| gc-max-store-size-macos: 8G | |
| # Cache hygiene: see the lint job above. | |
| save: true | |
| purge: true | |
| purge-prefixes: nix-native-${{ runner.os }}-${{ matrix.arch }}- | |
| purge-created: P7D | |
| purge-primary-key: never | |
| - uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16 | |
| with: | |
| name: spool | |
| skipPush: true | |
| - name: Build tailored Qt | |
| run: | | |
| nix build --accept-flake-config .#native-qt-cache \ | |
| --out-link "$RUNNER_TEMP/spool-native-qt" --print-build-logs | |
| - name: Publish tailored Qt | |
| env: | |
| CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} | |
| run: | | |
| if [[ -n "$CACHIX_AUTH_TOKEN" ]]; then | |
| cachix push spool "$(readlink "$RUNNER_TEMP/spool-native-qt")" | |
| fi | |
| - uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.0.3 | |
| with: | |
| path: | | |
| build/macos/mpv | |
| build/macos/mpv-prefix | |
| build/macos/app | |
| ~/Library/Caches/ccache | |
| key: macos-${{ matrix.arch }}-${{ hashFiles('flake.nix', 'flake.lock', 'mpv/meson.build', 'tools/manifests/mpv-native.json', 'tools/manifests/ffmpeg-capabilities.json', 'tools/manifests/toolchain.json', 'tools/ffmpeg-capabilities.py', 'tools/package-audit.py', 'tools/build-macos.sh', 'tools/package-macos-dmg.sh', 'tools/sign-macos.sh', 'tools/lib/build-common.sh', 'tools/lib/qt-deploy.sh', 'CMakeLists.txt') }} | |
| restore-keys: | | |
| macos-${{ matrix.arch }}- | |
| # Older Nix releases reject the relative path input recorded by newer | |
| # lock files when the root flake comes from a shallow Git checkout. | |
| # Override only the evaluator input with the exact checked-out submodule | |
| # revision; explicitly retain its shallow status rather than asking Nix | |
| # to treat the CI checkout as a complete repository. The build scripts | |
| # still compile the local mpv/ checkout. | |
| - name: Resolve checked-out mpv input | |
| env: | |
| ARCH: ${{ matrix.arch }} | |
| run: | | |
| revision="$(git -C mpv rev-parse HEAD)" | |
| overrides=() | |
| # `nix develop` builds bashInteractive from the flake's own `nixpkgs` | |
| # input. On Intel that input evaluates to a release which dropped | |
| # x86_64-darwin, and nix answers the failure by silently falling back | |
| # to the system bash 3.2, which cannot parse the environment script | |
| # it generates. Point the input at the same pin this system's shell | |
| # already uses, read from the lock so the two cannot drift. | |
| if [[ "$ARCH" == "x86_64" ]]; then | |
| url="$(jq -r '.nodes["nixpkgs-x86-darwin"].locked | |
| | "github:\(.owner)/\(.repo)/\(.rev)"' flake.lock)" | |
| printf 'overriding nixpkgs with %s\n' "$url" | |
| overrides=(--override-input nixpkgs "$url") | |
| fi | |
| # Retain unrelated pins so the app uses the Qt we just cached. | |
| for attempt in 1 2 3; do | |
| if nix flake lock --override-input mpv-src "git+file:$PWD/mpv?rev=$revision&shallow=1" \ | |
| ${overrides[@]+"${overrides[@]}"}; then | |
| exit 0 | |
| fi | |
| printf 'flake lock attempt %d failed; retrying\n' "$attempt" >&2 | |
| sleep $((attempt * 20)) | |
| done | |
| exit 1 | |
| - name: Build macOS app | |
| env: | |
| SPOOL_MACOS_CREDENTIAL_SERVICE: '' | |
| run: nix develop .#native --no-write-lock-file -c bash tools/build-macos.sh | |
| - name: Run native tests | |
| run: | | |
| nix develop .#native --no-write-lock-file -c ctest --test-dir build/macos/app \ | |
| --parallel "$(sysctl -n hw.ncpu)" --output-on-failure | |
| - name: Lint compiled QML | |
| run: nix develop .#native --no-write-lock-file -c cmake --build build/macos/app --target jellyfin-native_qmllint_strict | |
| - name: Load bundled Vulkan runtime | |
| run: | | |
| python3 - <<'PY' | |
| import ctypes | |
| import os | |
| from pathlib import Path | |
| contents = Path("build/macos/install/Spool.app/Contents").resolve() | |
| os.environ["VK_DRIVER_FILES"] = str(contents / "Resources/vulkan/icd.d/MoltenVK_icd.json") | |
| # Exercise dyld, not merely the symbolic dependency audit. Neither | |
| # load may fall back to a Nix/Homebrew library outside the bundle. | |
| ctypes.CDLL(str(contents / "Frameworks/libMoltenVK.dylib")) | |
| loader = ctypes.CDLL(str(contents / "Frameworks/libvulkan.1.dylib")) | |
| class Extension(ctypes.Structure): | |
| _fields_ = [("name", ctypes.c_char * 256), ("version", ctypes.c_uint32)] | |
| enumerate_extensions = loader.vkEnumerateInstanceExtensionProperties | |
| enumerate_extensions.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(Extension)] | |
| enumerate_extensions.restype = ctypes.c_int32 | |
| count = ctypes.c_uint32() | |
| result = enumerate_extensions(None, ctypes.byref(count), None) | |
| if result != 0: | |
| raise SystemExit(f"Vulkan extension enumeration failed: {result}") | |
| extensions = (Extension * count.value)() | |
| result = enumerate_extensions(None, ctypes.byref(count), extensions) | |
| if result != 0: | |
| raise SystemExit(f"Vulkan extension enumeration failed: {result}") | |
| names = {entry.name.decode() for entry in extensions} | |
| if "VK_EXT_metal_surface" not in names: | |
| raise SystemExit(f"MoltenVK Metal surface extension missing: {sorted(names)}") | |
| print("Bundled Vulkan loader and MoltenVK load successfully; Metal surface extension available") | |
| PY | |
| - name: Launch-test macOS app UI | |
| env: | |
| JELLYFIN_LAUNCH_TEST_QPA_PLATFORM: cocoa | |
| run: nix develop .#native --no-write-lock-file -c bash tools/launch-native-app.sh build/macos/install/Spool.app | |
| - name: Package DMG | |
| run: nix develop .#native --no-write-lock-file -c bash tools/package-macos-dmg.sh | |
| - name: Audit mounted DMG | |
| run: | | |
| set -euo pipefail | |
| mountpoint="$RUNNER_TEMP/spool-dmg" | |
| mkdir -p "$mountpoint" | |
| hdiutil attach -readonly -nobrowse -mountpoint "$mountpoint" dist/*.dmg | |
| trap 'bash tools/detach-dmg.sh "$mountpoint"' EXIT | |
| app="$(find "$mountpoint" -maxdepth 1 -name '*.app' -print -quit)" | |
| test -n "$app" | |
| python3 tools/package-audit.py inventory "$app" --output "$RUNNER_TEMP/macos-dmg.inventory.tsv" | |
| python3 tools/package-audit.py macho "$app" \ | |
| --root Contents/Frameworks/libvulkan.1.dylib \ | |
| --root Contents/Frameworks/libMoltenVK.dylib | |
| test -z "$(find "$app" -name '*.qmltypes' -print -quit)" | |
| JELLYFIN_LAUNCH_TEST_QPA_PLATFORM=cocoa \ | |
| bash tools/launch-native-app.sh "$app" | |
| bash tools/detach-dmg.sh "$mountpoint" | |
| trap - EXIT | |
| - name: Package macOS analysis input | |
| run: | | |
| dmg="$(printf '%s\n' dist/*.dmg)" | |
| tar -czf "${dmg}.app.tar.gz" -C build/macos/install Spool.app | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 | |
| with: | |
| name: spool-for-jellyfin-macos-dmg-${{ matrix.arch }} | |
| path: | | |
| dist/*.dmg | |
| dist/*.dmg.app.tar.gz | |
| retention-days: 7 |