Files
qbz/.github/workflows/release-linux-aarch64.yml
T
vicrodh efa486ba4f feat(packaging+log): standalone qbzd packages, IP masking in logs
- nfpm-qbzd.yaml (deb/rpm, libasound-only dep) + release workflows build
  the standalone qbzd packages per arch; dual dispatch to the future
  qbz-rpm repo (continue-on-error until token scope lands).
- Gentoo overlay templates: qbzd (source build) + qbzd-bin (tarball
  repack) with systemd user unit + completions.
- Log redaction layer 3: IPv4 -> *.*.*.<last>, IPv6 -> *:*:<last>, with
  version/timestamp false-positive guards (qbz-log 10/10 tests).

km
2026-07-25 12:05:56 -06:00

349 lines
15 KiB
YAML

# Linux aarch64 release — Slint binary (crates/ workspace).
#
# Rewritten 2026-07-02 for the Tauri -> Slint migration (v2.0). Same
# build-once-package-many shape as release-linux.yml (read that file's
# header); differences: native arm64 runner, no x86 target-features, its
# own rust-cache key (this workflow is the only writer), and it ATTACHES
# to the Release that release-linux.yml creates.
#
# CONTRACT: keeps publishing qbz_${VERSION}_aarch64.tar.gz,
# QBZ_${VERSION}_arm64.deb, QBZ-${VERSION}-1.aarch64.rpm,
# QBZ_${VERSION}_aarch64.AppImage — AUR/Gentoo/APT/website names.
name: "Linux ARM64 (aarch64) Build"
on:
workflow_dispatch:
inputs:
tag:
description: "Optional release tag to build (e.g. v2.0.0). Empty = build HEAD with the Cargo.toml version, no Release publish."
required: false
type: string
push:
tags:
- "v*"
concurrency:
group: release-linux-aarch64-${{ github.ref }}
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-24.04-arm
timeout-minutes: 300
continue-on-error: true
outputs:
version: ${{ steps.release.outputs.version }}
prerelease: ${{ steps.release.outputs.prerelease }}
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Resolve release version
id: release
run: |
VERSION=""
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
VERSION="${GITHUB_REF#refs/tags/v}"
elif [[ -n "${{ inputs.tag }}" ]]; then
VERSION="${{ inputs.tag }}"
VERSION="${VERSION#v}"
else
VERSION="$(grep -m1 '^version = ' crates/Cargo.toml | sed 's/version = \"\(.*\)\"/\1/')"
fi
PRERELEASE=false
if [[ "$VERSION" =~ -(rc|beta|alpha|pre) ]]; then
PRERELEASE=true
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "prerelease=$PRERELEASE" >> "$GITHUB_OUTPUT"
- name: Verify tag is on main
if: startsWith(github.ref, 'refs/tags/')
run: |
git fetch origin main
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
echo "::error::Tag ${GITHUB_REF#refs/tags/} is not on main. Releases are cut from main only."
exit 1
fi
- name: Free disk space and add swap
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 system prune -af || true
if sudo fallocate -l 18G /swap_extra; then
sudo chmod 600 /swap_extra
sudo mkswap /swap_extra
sudo swapon /swap_extra
else
echo "::warning::could not allocate extra swap; build may OOM"
fi
free -h
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
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
# Pinned nightly: matches build-slint.yml / release-linux.yml.
- name: Setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly-2026-06-23
- name: Rust cache
uses: Swatinem/rust-cache@v2
with:
workspaces: crates -> target
shared-key: slint-linux-aarch64
cache-on-failure: true
# This workflow is the only aarch64 builder, so it owns its cache.
save-if: true
- name: Build (release, MIN memory tier)
env:
CARGO_INCREMENTAL: "0"
CARGO_BUILD_JOBS: "1"
CARGO_PROFILE_RELEASE_CODEGEN_UNITS: "256"
CARGO_PROFILE_RELEASE_OPT_LEVEL: "2"
CARGO_PROFILE_RELEASE_STRIP: "none"
# 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
cp crates/target/release/qbz qbz-unstripped
strip -o qbz crates/target/release/qbz
ls -la qbz qbz-unstripped
- name: Upload binary
uses: actions/upload-artifact@v7
with:
name: qbz-linux-aarch64
path: qbz
if-no-files-found: error
- name: Upload unstripped binary (symbolication)
uses: actions/upload-artifact@v7
with:
name: qbz-linux-aarch64-unstripped
path: qbz-unstripped
retention-days: 90
# T15: aarch64 mirror of release-linux.yml's build-qbzd job — see that
# file's header comment for the glibc-floor rationale. Uses the native
# ubuntu-22.04-arm runner label rather than cross-compiling.
#
# Fallback note (NOT implemented — flagged for pre-flight per the T15
# brief): if ubuntu-22.04-arm is ever unavailable to this repo, cross-build
# instead via the existing Cross.toml pattern (crates/Cross.toml,
# scripts/build-aarch64-linux.sh's CROSS mode) targeting a 2.35-floor
# image; the objdump gate below stays the acceptance test either way.
build-qbzd:
runs-on: ubuntu-22.04-arm
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: system deps
run: sudo apt-get update && sudo apt-get install -y libasound2-dev libjack-jackd2-dev libdbus-1-dev libssl-dev
- name: slint-free dep-graph gate
run: |
set -euo pipefail
hits=$(cargo tree --manifest-path crates/Cargo.toml -p qbzd -e normal \
| grep -E '\b(slint|qbz-ui|qbz-slint-common|qbz-dac-wizard) v' || true)
[ -z "$hits" ] || { echo "::error::qbzd graph resolves Slint:"; echo "$hits"; exit 1; }
- name: build
run: cargo build --release --manifest-path crates/Cargo.toml -p qbzd
- name: glibc floor gate (objdump — the empirical-verification rule)
run: |
set -euo pipefail
floor=$(objdump -T crates/target/release/qbzd \
| grep -oE 'GLIBC_[0-9]+\.[0-9]+' | sort -Vu | tail -1)
echo "glibc floor: $floor"
highest=$(printf '%s\n' "$floor" "GLIBC_2.35" | sort -V | tail -1)
[ "$highest" = "GLIBC_2.35" ] || { echo "::error::floor $floor exceeds 2.35"; exit 1; }
# NOTE: the T15 brief's literal YAML names this artifact
# `qbzd-linux-arm64`, but T14 (39eb12fc, already merged into this
# branch) wired the package job's Download-qbzd step below to expect
# `qbzd-linux-aarch64` — matching what already landed wins over the
# brief text (see task-15-report.md for the full note).
- uses: actions/upload-artifact@v7
with: { name: qbzd-linux-aarch64, path: crates/target/release/qbzd }
package:
needs: [build, build-qbzd]
runs-on: ubuntu-24.04-arm
permissions:
contents: write
env:
VERSION: ${{ needs.build.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Download binary
uses: actions/download-artifact@v8
with:
name: qbz-linux-aarch64
# T14: the slint-free qbzd binary, produced by T15's build-qbzd job — see
# release-linux.yml's matching step for the full rationale (one
# artifact feeds nfpm, the main tarball, and the standalone tarball).
- name: Download qbzd binary
uses: actions/download-artifact@v8
with:
name: qbzd-linux-aarch64
- name: Install nfpm
run: |
echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | \
sudo tee /etc/apt/sources.list.d/goreleaser.list
sudo apt-get update
sudo apt-get install -y nfpm
nfpm --version
- name: Build deb + rpm (nfpm)
env:
QBZ_VERSION: ${{ env.VERSION }}
QBZ_ARCH: arm64
QBZ_BINARY: ./qbz
run: |
chmod +x qbz
chmod +x qbzd
mkdir -p dist
nfpm package -f packaging/nfpm/nfpm.yaml -p deb \
-t "dist/QBZ_${VERSION}_arm64.deb"
nfpm package -f packaging/nfpm/nfpm.yaml -p rpm \
-t "dist/QBZ-${VERSION}-1.aarch64.rpm"
# Standalone qbzd packages (daemon-only, mirrors the amd64 job).
nfpm package -f packaging/nfpm/nfpm-qbzd.yaml -p deb \
-t "dist/qbzd_${VERSION}_arm64.deb"
nfpm package -f packaging/nfpm/nfpm-qbzd.yaml -p rpm \
-t "dist/qbzd-${VERSION}-1.aarch64.rpm"
- name: Build AppImage
run: |
APPDIR="QBZ.AppDir"
mkdir -p "$APPDIR/usr/bin"
cp qbz "$APPDIR/usr/bin/qbz"
cp packaging/linux/qbz.desktop "$APPDIR/qbz.desktop"
cp packaging/icons/256x256.png "$APPDIR/qbz.png"
cp packaging/icons/256x256.png "$APPDIR/.DirIcon"
ln -s usr/bin/qbz "$APPDIR/AppRun"
curl -fsSL -o appimagetool \
https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-aarch64.AppImage
chmod +x appimagetool
ARCH=aarch64 ./appimagetool --appimage-extract-and-run "$APPDIR" \
"dist/QBZ_${VERSION}_aarch64.AppImage"
# Tauri-updater bridge — see release-linux.yml for the rationale.
- name: Sign AppImage (Tauri-updater bridge)
if: startsWith(github.ref, 'refs/tags/')
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
run: |
npx --yes @tauri-apps/cli@^2 signer sign \
--private-key "$TAURI_SIGNING_PRIVATE_KEY" \
--password "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD" \
"dist/QBZ_${VERSION}_aarch64.AppImage"
ls -la dist
- name: Build AUR/Gentoo tarball
run: |
# CONTRACT: same name + internal layout as the Tauri-era tarball.
TARBALL_DIR="qbz_${VERSION}_aarch64"
mkdir -p "tarball-staging/${TARBALL_DIR}/icons/hicolor"/{32x32,48x48,64x64,128x128,256x256}/apps
cp qbz "tarball-staging/${TARBALL_DIR}/qbz"
chmod +x "tarball-staging/${TARBALL_DIR}/qbz"
cp packaging/icons/32x32.png "tarball-staging/${TARBALL_DIR}/icons/hicolor/32x32/apps/qbz.png"
cp packaging/icons/48x48.png "tarball-staging/${TARBALL_DIR}/icons/hicolor/48x48/apps/qbz.png"
cp packaging/icons/64x64.png "tarball-staging/${TARBALL_DIR}/icons/hicolor/64x64/apps/qbz.png"
cp packaging/icons/128x128.png "tarball-staging/${TARBALL_DIR}/icons/hicolor/128x128/apps/qbz.png"
cp packaging/icons/256x256.png "tarball-staging/${TARBALL_DIR}/icons/hicolor/256x256/apps/qbz.png"
cp packaging/linux/qbz.desktop "tarball-staging/${TARBALL_DIR}/qbz.desktop"
# Additive (T14) — see release-linux.yml's matching block for the
# layout-contract rationale.
cp qbzd "tarball-staging/${TARBALL_DIR}/qbzd"
chmod +x "tarball-staging/${TARBALL_DIR}/qbzd"
cp crates/qbzd/service/qbzd.service "tarball-staging/${TARBALL_DIR}/qbzd.service"
mkdir -p "tarball-staging/${TARBALL_DIR}/completions"
./qbzd completions bash > "tarball-staging/${TARBALL_DIR}/completions/qbzd.bash"
./qbzd completions zsh > "tarball-staging/${TARBALL_DIR}/completions/qbzd.zsh"
./qbzd completions fish > "tarball-staging/${TARBALL_DIR}/completions/qbzd.fish"
tar -czvf "dist/qbz_${VERSION}_aarch64.tar.gz" -C tarball-staging "${TARBALL_DIR}"
ls -la dist
# T15 Step 2b (OD3, G0) — see release-linux.yml's matching step for
# the rationale. Never touches snap/Flathub/AppImage/dmg.
- name: qbzd-tarball
run: |
QBZD_DIR="qbzd-${VERSION}-linux-aarch64"
mkdir -p "qbzd-tarball-staging/${QBZD_DIR}/completions"
cp qbzd "qbzd-tarball-staging/${QBZD_DIR}/qbzd"
chmod +x "qbzd-tarball-staging/${QBZD_DIR}/qbzd"
cp crates/qbzd/service/qbzd.service "qbzd-tarball-staging/${QBZD_DIR}/qbzd.service"
./qbzd completions bash > "qbzd-tarball-staging/${QBZD_DIR}/completions/qbzd.bash"
./qbzd completions zsh > "qbzd-tarball-staging/${QBZD_DIR}/completions/qbzd.zsh"
./qbzd completions fish > "qbzd-tarball-staging/${QBZD_DIR}/completions/qbzd.fish"
cp packaging/linux/qbzd-standalone-README.md "qbzd-tarball-staging/${QBZD_DIR}/README.md"
tar -czvf "dist/qbzd-${VERSION}-linux-aarch64.tar.gz" -C qbzd-tarball-staging "${QBZD_DIR}"
ls -la dist
- name: Upload artifacts
uses: actions/upload-artifact@v7
with:
name: qbz-linux-aarch64-packages
path: dist/*
# Attaches to the Release created by release-linux.yml.
- name: Publish GitHub Release Assets
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v3
with:
files: dist/*
prerelease: ${{ needs.build.outputs.prerelease }}
make_latest: ${{ needs.build.outputs.prerelease == 'true' && 'false' || 'true' }}
# #648: the apt dispatch MUST also fire from here — this workflow is the
# slow one (native arm64 build), so release-linux.yml's dispatch ran
# hours earlier, when the Release still held only the x86_64 .deb; the
# arm64 .deb landed after and nothing re-indexed (apt arm64 stuck at
# 1.2.15). update-repo.yml is idempotent (re-downloads every *.deb,
# overwrites the pool, rebuilds both indices), so a second run simply
# completes whichever arch the first one missed.
- name: Trigger APT repo update
if: startsWith(github.ref, 'refs/tags/')
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.APT_REPO_TOKEN }}
repository: vicrodh/qbz-apt
event-type: new-release
client-payload: '{"tag": "${{ github.ref_name }}"}'
# RPM repo (qbz-rpm) — same dual-dispatch rule as #648 above: the arm64
# .rpms land hours after x86_64's, and update-repo.yml is idempotent.
# continue-on-error until the token scope covers the new repo.
- name: Trigger RPM repo update
if: startsWith(github.ref, 'refs/tags/')
continue-on-error: true
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.APT_REPO_TOKEN }}
repository: vicrodh/qbz-rpm
event-type: new-release
client-payload: '{"tag": "${{ github.ref_name }}"}'