diff --git a/.github/workflows/app-binaries.yml b/.github/workflows/app-binaries.yml new file mode 100644 index 0000000000..9bdf9a2714 --- /dev/null +++ b/.github/workflows/app-binaries.yml @@ -0,0 +1,41 @@ +name: app-binaries +# Regression gate: build every binary the macOS Studio app ships, each with its +# required-features. `cargo build --workspace` skips required-features bins, so +# this is the only CI that compiles chat_metal / eval_perplexity / train_grad_full +# / generate_lora / embed with the features the app actually builds them with. +# See scripts/build-app-bins.sh for the rationale and the two regressions that +# motivated it. + +on: + pull_request: + branches: [main] + paths: + - 'crates/**' + - 'scripts/build-app-bins.sh' + - '.github/workflows/app-binaries.yml' + push: + branches: [main] + paths: + - 'crates/**' + workflow_dispatch: + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: '0' + +jobs: + build-app-bins: + name: build app-shipped binaries + runs-on: macos-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@1.94.1 + - uses: Swatinem/rust-cache@v2 + with: + shared-key: app-binaries + - name: Build all app-shipped binaries (required-features included) + run: ./scripts/build-app-bins.sh diff --git a/scripts/build-app-bins.sh b/scripts/build-app-bins.sh new file mode 100755 index 0000000000..be398d26d6 --- /dev/null +++ b/scripts/build-app-bins.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# +# Build every binary the macOS Studio app ships (apps/macos/scripts/package-app.sh), +# each with its required-features. +# +# This is the regression gate that `cargo build --workspace` cannot provide: +# --workspace (even with --all-targets) silently SKIPS any [[bin]] that declares +# `required-features` unless those features are explicitly enabled. So a +# required-features binary can stop compiling and never trip fmt / clippy / test +# / build CI — the break only surfaces when the app is packaged. +# +# Two real regressions slipped through exactly this gap and were caught only at +# packaging time: +# - train_grad_full E0063 (missing GDN gradient fields under train-backward) +# - generate_lora E0599 (generate_streaming method not in scope) +# +# Keep this binary list in sync with apps/macos/scripts/package-app.sh. + +set -uo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$REPO_ROOT" + +FAIL=0 +build_bin() { + echo "" + echo "==> cargo build --release $*" + if ! cargo build --release "$@"; then + echo "!! FAILED: cargo build --release $*" + FAIL=1 + fi +} + +# lattice-inference — default features (already covered by --workspace, built +# here too so the gate reflects the full app binary set). +for BIN in quantize_q4 quantize_quarot lattice qwen35_generate; do + build_bin -p lattice-inference --bin "$BIN" +done + +# required-features binaries — the ones `cargo build --workspace` silently skips. +build_bin -p lattice-tune --bin train_grad_full --features train-backward +build_bin -p lattice-tune --bin generate_lora --features safetensors,inference-hook +build_bin -p lattice-inference --bin eval_perplexity --features f16,metal-gpu +build_bin -p lattice-inference --bin chat_metal --features f16,metal-gpu + +# lattice-embed — `native` is a default feature, so no explicit --features. +build_bin -p lattice-embed --bin embed + +if [ "$FAIL" -ne 0 ]; then + echo "" + echo "ERROR: one or more app-shipped binaries failed to build." + echo "The macOS Studio app would fail to package. Fix before merging." + exit 1 +fi +echo "" +echo "OK: all app-shipped binaries built."