216 lines
8.4 KiB
YAML
216 lines
8.4 KiB
YAML
# macOS release — Slint binary (crates/ workspace).
|
|
#
|
|
# Rewritten 2026-07-02 for the Tauri -> Slint migration (v2.0). Builds the
|
|
# qbz binary per target, assembles a minimal QBZ.app bundle (Info.plist +
|
|
# icns + binary), ad-hoc signs it (same trust level as the unsigned Tauri
|
|
# builds), and packs a dmg with hdiutil. No Tauri updater: the .app.tar.gz
|
|
# + .sig updater assets are gone, dmg is the distribution.
|
|
#
|
|
# MEMORY NOTE: hosted macOS runners have ~7 GB RAM; the qbz_ui rustc peaks
|
|
# ~30 GB on Linux. macOS swaps dynamically to SSD so the build may survive
|
|
# (slowly) — if it proves unreliable, point runs-on at a self-hosted runner
|
|
# on the M2 Mini instead. continue-on-error keeps a mac failure from
|
|
# blocking the Linux release.
|
|
|
|
name: release-macos
|
|
|
|
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-macos-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
build-macos:
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
- target: aarch64-apple-darwin
|
|
artifact: qbz-macos-aarch64
|
|
arch: aarch64
|
|
- target: x86_64-apple-darwin
|
|
artifact: qbz-macos-x86_64
|
|
# Historical dmg naming: intel assets ship as "x64".
|
|
arch: x64
|
|
runs-on: macos-latest
|
|
timeout-minutes: 360
|
|
continue-on-error: true
|
|
permissions:
|
|
contents: write
|
|
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: Assert Cargo.toml version matches tag
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
env:
|
|
VERSION: ${{ steps.release.outputs.version }}
|
|
run: |
|
|
CARGO_VERSION="$(grep -m1 '^version = ' crates/Cargo.toml | sed 's/version = \"\(.*\)\"/\1/')"
|
|
if [[ "$CARGO_VERSION" != "$VERSION" ]]; then
|
|
echo "::error::crates/Cargo.toml workspace version ($CARGO_VERSION) != tag version ($VERSION)."
|
|
exit 1
|
|
fi
|
|
|
|
# Same pinned nightly as the Linux builders (-Z threads support,
|
|
# consistent measured behavior). Bump all release workflows together.
|
|
- name: Setup Rust
|
|
uses: dtolnay/rust-toolchain@master
|
|
with:
|
|
toolchain: nightly-2026-06-23
|
|
targets: ${{ matrix.target }}
|
|
|
|
- name: Rust cache
|
|
uses: Swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: crates -> target
|
|
shared-key: slint-macos-${{ matrix.target }}
|
|
cache-on-failure: true
|
|
save-if: true
|
|
|
|
# MIN memory tier — the 7 GB hosted runner relies on macOS dynamic
|
|
# swap for the qbz_ui rustc peak. Binary is stripped by the workspace
|
|
# profile (strip = "symbols"), which rustc applies correctly to Mach-O.
|
|
- 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"
|
|
RUSTFLAGS: "-Z threads=1"
|
|
run: |
|
|
cargo build --release --manifest-path crates/Cargo.toml \
|
|
-p qbz --target ${{ matrix.target }}
|
|
|
|
- name: Assemble QBZ.app
|
|
env:
|
|
VERSION: ${{ steps.release.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
APP="dist/QBZ.app"
|
|
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
|
cp "crates/target/${{ matrix.target }}/release/qbz" "$APP/Contents/MacOS/qbz"
|
|
chmod +x "$APP/Contents/MacOS/qbz"
|
|
cp packaging/icons/icon.icns "$APP/Contents/Resources/icon.icns"
|
|
cat > "$APP/Contents/Info.plist" <<PLIST
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>CFBundlePackageType</key><string>APPL</string>
|
|
<key>CFBundleIdentifier</key><string>com.blitzfc.qbz</string>
|
|
<key>CFBundleName</key><string>QBZ</string>
|
|
<key>CFBundleDisplayName</key><string>QBZ</string>
|
|
<key>CFBundleExecutable</key><string>qbz</string>
|
|
<key>CFBundleIconFile</key><string>icon</string>
|
|
<key>CFBundleShortVersionString</key><string>${VERSION}</string>
|
|
<key>CFBundleVersion</key><string>${VERSION}</string>
|
|
<key>LSMinimumSystemVersion</key><string>11.0</string>
|
|
<key>NSHighResolutionCapable</key><true/>
|
|
<key>LSApplicationCategoryType</key><string>public.app-category.music</string>
|
|
<key>NSSupportsAutomaticGraphicsSwitching</key><true/>
|
|
<key>CFBundleURLTypes</key>
|
|
<array>
|
|
<dict>
|
|
<key>CFBundleURLName</key><string>com.blitzfc.qbz</string>
|
|
<key>CFBundleURLSchemes</key>
|
|
<array><string>qbz</string><string>qobuzapp</string></array>
|
|
</dict>
|
|
</array>
|
|
</dict>
|
|
</plist>
|
|
PLIST
|
|
# Ad-hoc signature: required for arm64 Macs to launch at all;
|
|
# matches the trust level of the previous unsigned Tauri builds.
|
|
codesign --force --deep -s - "$APP"
|
|
codesign --verify --verbose "$APP"
|
|
|
|
- name: Build dmg
|
|
env:
|
|
VERSION: ${{ steps.release.outputs.version }}
|
|
ARCH: ${{ matrix.arch }}
|
|
run: |
|
|
hdiutil create -volname "QBZ" -srcfolder dist/QBZ.app -ov \
|
|
-format UDZO "dist/QBZ_${VERSION}_${ARCH}.dmg"
|
|
ls -la dist
|
|
|
|
# Tauri-updater bridge: v1.2.x installs poll latest.json and verify
|
|
# with the Tauri minisign pubkey baked into the old binaries. The
|
|
# updater expects QBZ_${ARCH}.app.tar.gz — keep producing it, signed
|
|
# with the SAME key, so old installs update straight into the Slint
|
|
# .app. release-updater-manifest.yml collects the .sig files.
|
|
- name: Package .app.tar.gz + sign (Tauri-updater bridge)
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
env:
|
|
ARCH: ${{ matrix.arch }}
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
run: |
|
|
tar -czf "dist/QBZ_${ARCH}.app.tar.gz" -C dist QBZ.app
|
|
npx --yes @tauri-apps/cli@^2 signer sign \
|
|
--private-key "$TAURI_SIGNING_PRIVATE_KEY" \
|
|
--password "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD" \
|
|
"dist/QBZ_${ARCH}.app.tar.gz"
|
|
ls -la dist
|
|
|
|
- name: Upload artifacts
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: ${{ matrix.artifact }}
|
|
path: |
|
|
dist/*.dmg
|
|
dist/*.app.tar.gz
|
|
dist/*.app.tar.gz.sig
|
|
|
|
# Attaches to the Release created by release-linux.yml; same
|
|
# prerelease logic (the old workflow hardcoded prerelease: true).
|
|
- name: Publish GitHub Release Assets
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
uses: softprops/action-gh-release@v3
|
|
with:
|
|
files: |
|
|
dist/*.dmg
|
|
dist/*.app.tar.gz
|
|
dist/*.app.tar.gz.sig
|
|
prerelease: ${{ steps.release.outputs.prerelease }}
|
|
make_latest: ${{ steps.release.outputs.prerelease == 'true' && 'false' || 'true' }}
|