diff --git a/.github/workflows/release-snap.yml b/.github/workflows/release-snap.yml index ef343a6f4..d13b5cf8e 100644 --- a/.github/workflows/release-snap.yml +++ b/.github/workflows/release-snap.yml @@ -1,19 +1,21 @@ name: Release Snap (Slint, all-hosted) -# All-hosted Snap pipeline for the native (Slint) qbz. NOTHING runs on the dev -# box, so the lack of systemd/snapd there does not matter: -# job 1 (build-binary): a 16 GB Ubuntu runner compiles the qbz binary using -# the same MIN-tier + 18 GB swap recipe as build-slint.yml (the ~30 GB -# qbz_ui peak fits in 16 GB RAM + swap), and uploads it as an artifact. -# job 2 (snap): a second Ubuntu runner (which HAS snapd) downloads that -# prebuilt binary and packages it with `plugin: dump` (NO compilation, runs -# in seconds), then publishes to the Snap Store. -# So the heavy compile is on a free public runner, and the snap packaging is on -# a runner that has snapd — neither needs your OpenRC box. +# All-hosted Snap pipeline for the native (Slint) qbz — BOTH arches. NOTHING +# runs on the dev box, so the lack of systemd/snapd there does not matter. Two +# independent tracks, each = build-binary -> dump-pack -> publish: +# amd64: build-binary (ubuntu-24.04, MIN-tier + 18 GB swap) -> snap +# (packaging/snap-slint/snapcraft.yaml, plugin: dump). +# arm64: build-binary-arm64 (native ubuntu-24.04-arm, free for public repos, +# reuses release-linux-aarch64.yml's cargo cache) -> snap-arm64 +# (packaging/snap-slint/snapcraft-arm64.yaml, plugin: dump). +# The heavy compile is on free public runners; the snap pack (dump, no compile) +# runs in seconds on a runner that has snapd. This CI upload is the SOLE publish +# path — no Launchpad / "Build from GitHub" is used (the root snapcraft.yaml is +# reference/local-dev only and is removed from the CI working copy below). # -# NOTE: build-binary duplicates build-slint.yml's build. When the release +# NOTE: build-binary* duplicates the deb/rpm builders' compile. When the release # pipeline is unified (build-once -> fan out to deb/rpm/appimage/snap/flatpak), -# this job becomes a `download-artifact` of the shared binary instead. +# these jobs become `download-artifact` of the shared per-arch binaries instead. on: workflow_dispatch: @@ -104,6 +106,75 @@ jobs: path: crates/target/release/qbz if-no-files-found: error + build-binary-arm64: + # Native arm64 build (mirrors release-linux-aarch64.yml). Reuses that + # workflow's cargo cache (shared-key slint-linux-aarch64) so it does not + # recompile qbz_ui from cold when the aarch64 build already ran. + runs-on: ubuntu-24.04-arm + timeout-minutes: 180 + steps: + - uses: actions/checkout@v4 + + - name: Free disk + swap (sized for the ~30 GB qbz_ui peak) + run: | + sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android \ + /opt/hostedtoolcache /usr/local/share/boost /usr/share/swift \ + /usr/local/lib/node_modules || true + sudo docker image prune --all --force || true + ( sudo fallocate -l 18G /swap_extra \ + && sudo chmod 600 /swap_extra \ + && sudo mkswap /swap_extra \ + && sudo swapon /swap_extra ) || echo "extra swap skipped (non-fatal)" + free -h; df -h / + + - name: System dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + build-essential pkg-config cmake clang libclang-dev mold nasm \ + libasound2-dev libjack-jackd2-dev \ + libfontconfig1-dev libfreetype-dev \ + libxkbcommon-dev libwayland-dev libxcb1-dev \ + libgl1-mesa-dev libegl1-mesa-dev \ + libdbus-1-dev libssl-dev \ + libharfbuzz-dev libjbig2dec0-dev libopenjp2-7-dev \ + libjpeg-dev zlib1g-dev libgumbo-dev + + - name: Rust toolchain (pinned dated nightly) + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2026-06-23 + + - name: Cargo cache (Swatinem/rust-cache) + uses: Swatinem/rust-cache@v2 + with: + workspaces: crates -> target + shared-key: slint-linux-aarch64 + cache-on-failure: true + # reuse release-linux-aarch64.yml's cache (same shared-key); do not + # clobber it from here. + save-if: false + + - name: Build qbz (memory-tuned MIN tier, swap-backed) + env: + CARGO_INCREMENTAL: '0' + CARGO_BUILD_JOBS: '1' + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '256' + CARGO_PROFILE_RELEASE_OPT_LEVEL: '2' + # No x86 target-features here (aarch64 baseline has NEON). + RUSTFLAGS: '-C link-arg=-fuse-ld=mold -Z threads=1' + run: | + cargo build --release --manifest-path crates/Cargo.toml -p qbz + strip crates/target/release/qbz || true + ls -lh crates/target/release/qbz + + - name: Upload binary artifact + uses: actions/upload-artifact@v4 + with: + name: qbz-bin-arm64 + path: crates/target/release/qbz + if-no-files-found: error + snap: needs: build-binary runs-on: ubuntu-24.04 @@ -161,3 +232,56 @@ jobs: with: snap: ${{ steps.build.outputs.snap }} release: ${{ github.event.inputs.channel || 'edge' }} + + snap-arm64: + needs: build-binary-arm64 + runs-on: ubuntu-24.04-arm + # No compilation here (plugin: dump); the headroom is for snapcraft's LXD + # bootstrap, which is variable on hosted runners. + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + + - name: Download prebuilt binary + uses: actions/download-artifact@v4 + with: + name: qbz-bin-arm64 + path: dl + + - name: Stage snap payload (binary + desktop + icon) and recipe + run: | + set -eux + TAG="${{ github.event.inputs.tag || github.ref_name }}" + VER="${TAG#v}" + mkdir -p snap-payload + install -m755 dl/qbz snap-payload/qbz + cp packaging/snap/snap/gui/qbz.desktop snap-payload/qbz.desktop + cp packaging/snap/snap/gui/qbz.png snap-payload/qbz.png + # Place the arm64 dump recipe where snapcraft looks for it, stamp version. + mkdir -p snap + cp packaging/snap-slint/snapcraft-arm64.yaml snap/snapcraft.yaml + sed -i "s/^version:.*/version: '${VER}'/" snap/snapcraft.yaml + # Remove the root source-build recipe from the CI working copy so + # snapcraft resolves ONLY the dump recipe (see the amd64 job). + rm -f snapcraft.yaml .snapcraft.yaml + echo "Built snap version: ${VER} (arm64)" + cat snap/snapcraft.yaml | head -20 + + - name: Build the snap (no compilation — dump of the prebuilt binary) + id: build + uses: snapcore/action-build@v1 + + - name: Upload .snap artifact + uses: actions/upload-artifact@v4 + with: + name: qbz-snap-arm64 + path: ${{ steps.build.outputs.snap }} + if-no-files-found: error + + - name: Publish to the Snap Store + uses: snapcore/action-publish@v1 + env: + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} + with: + snap: ${{ steps.build.outputs.snap }} + release: ${{ github.event.inputs.channel || 'edge' }} diff --git a/packaging/snap-slint/snapcraft-arm64.yaml b/packaging/snap-slint/snapcraft-arm64.yaml new file mode 100644 index 000000000..cad032324 --- /dev/null +++ b/packaging/snap-slint/snapcraft-arm64.yaml @@ -0,0 +1,122 @@ +# QBZ (Slint) — arm64 Snap recipe that PACKAGES A PRE-BUILT BINARY (plugin: dump). +# +# arm64 sibling of snapcraft.yaml (amd64). Differences vs the amd64 recipe: +# - platforms: arm64 +# - the alsa-lib layout bind points at the aarch64 multiarch dir. +# Everything else (apps/plugs/slots/stage-packages) is identical; stage-packages +# resolve to arm64 automatically on the arm64 runner. Keep the runtime sections +# in sync with snapcraft.yaml (amd64) and the root snapcraft.yaml. +# +# release-snap.yml's arm64 track stages the prebuilt aarch64 qbz binary under +# ./snap-payload/, copies THIS file to snap/snapcraft.yaml, stamps `version:` +# from the tag, and runs `snapcraft` on a native ubuntu-24.04-arm runner +# (plugin: dump → no compilation). + +name: qbz-player # keep the existing registered Snap Store name +title: "QBZ — Native Qobuz Client for Linux" +version: '0.0.0' # OVERWRITTEN by release-snap.yml from the tag (sed) +summary: Native Qobuz player for Linux with bit-perfect audio and DAC control +description: | + QBZ is a native Linux desktop client for Qobuz(TM), built for high-fidelity + playback with a native audio pipeline (direct ALSA and PipeWire), bit-perfect + output, exclusive device access, and per-track sample-rate switching for + external DACs — up to 24-bit / 192 kHz. + + This is the native (Slint) build: a single self-contained binary, no web + runtime. Features: native Qobuz(TM) streaming, bit-perfect ALSA exclusive + mode, DAC passthrough, Hi-Res FLAC, queue/playlists/favorites/offline cache, + local library, Chromecast and DLNA, MPRIS media keys and desktop integration. + + QBZ uses the Qobuz(TM) API but is not affiliated with, endorsed by, or + certified by Qobuz. + +license: MIT +contact: https://github.com/vicrodh/qbz/discussions +website: https://qbz.lol +source-code: https://github.com/vicrodh/qbz +issues: https://github.com/vicrodh/qbz/issues + +grade: stable +confinement: strict +base: core24 +icon: packaging/snap/snap/gui/qbz.png + +platforms: + arm64: + +# ALSA config/plugin binds: without these the snap's alsa-lib cannot find the +# host's ALSA configuration and plugin set, and the direct hw: (bit-perfect) +# path breaks. aarch64 multiarch dir here (vs x86_64 in the amd64 recipe). +layout: + /usr/share/alsa: + bind: $SNAP/usr/share/alsa + /etc/alsa: + bind: $SNAP/etc/alsa + /usr/lib/aarch64-linux-gnu/alsa-lib: + bind: $SNAP/usr/lib/aarch64-linux-gnu/alsa-lib + +apps: + qbz: + command: bin/qbz + desktop: usr/share/applications/qbz.desktop + # The gnome extension provides the graphics/font platform (Mesa GL, EGL, + # fontconfig, wayland) that a femtovg-wgpu + winit app needs — it is NOT + # about GTK here, just the runtime platform. It wires desktop/wayland/x11/ + # gsettings plugs automatically. + extensions: [gnome] + plugs: + - network + - network-status + - audio-playback + - home + - removable-media + - alsa + - pipewire + - opengl + slots: + - mpris + +slots: + mpris: + interface: mpris + name: qbz + +parts: + qbz: + plugin: dump + # release-snap.yml writes the prebuilt aarch64 binary + desktop + icon here: + source: ./snap-payload + organize: + qbz: bin/qbz + qbz.desktop: usr/share/applications/qbz.desktop + qbz.png: usr/share/icons/hicolor/256x256/apps/qbz.png + stage-packages: + # Audio (the bit-perfect path) + - libasound2t64 + - libasound2-plugins + - libpipewire-0.3-0 + - libjack-jackd2-0 + # TLS / HTTP + - libssl3t64 + # GPU stack for femtovg-wgpu (Vulkan first, GL fallback). The gnome + # extension ships Mesa GL; add the Vulkan ICD + loader explicitly since + # wgpu prefers Vulkan and the platform may not expose the ICD. + - libvulkan1 + - mesa-vulkan-drivers + - libegl1 + - libgl1 + # winit windowing (Wayland + X11) + - libwayland-client0 + - libwayland-cursor0 + - libwayland-egl1 + - libxkbcommon0 + - libxcb1 + - libx11-6 + - libxcursor1 + - libxi6 + - libxrandr2 + # Fonts / text shaping (femtovg) + - libfontconfig1 + - libfreetype6 + # Tray (ksni) + portals + media controls over D-Bus + - libdbus-1-3