ci: run app checks in secret-free jobs for the nightly path
Merged
LA
22d ago+886+886+012345678901234567890123456789-210-210-012345678901234567890123456789
Summary
CI hardening, follow-up to #20996 (cmuxterm-hq#2053): job-level isolation for the nightly path. A job that holds a signing, notarization or publishing secret now runs no code from the app bundle; every check that runs bundle code moves to a job with no environment and no secrets, on a literal ephemeral runner label.
nightly.yml- New
prepare-nightly-variant(no secrets): downloads the unsigned app, installs the Ghostty helper and the cmux-tui client (its probe runs here), runs the theme picker and CLI memory guard regressions, thins, runs the full sidecar check, and uploads the prepared app. build-sign-notarize-nightlysigns exactly that app and runs no bundle code:--embedwithout--verify-cli, launch smokes deferred innotarize-nightly-dmg.sh(CMUX_DEFER_LAUNCH_SMOKES), and it uploads the signed app for verification.- New
verify-signed-nightly(no secrets,contents: readonly): sidecar handshake, SSH daemon--verify-cli, launch smoke, bundled CLI smoke, and on the inline path a launch of the app inside each final DMG (scripts/ci/smoke-dmg.sh), recording each tested DMG digest. publish-nightlyneedsverify-signed-nightlyand refuses any DMG whose SHA-256 is not a tested digest.
- New
auto-resume-nightly-notarization.yml: the preflight continues a source run only when everyVerify signed nightly app (<variant>)job passed.wait-and-stapleandstaple-outerdefer their launch smokes; newverify-stapled(no secrets) launches the app inside each exact stapled DMG (checked againstfinal_dmg_sha256) and records the digest;publishneeds it and requires the tested digest.resume-nightly-notarization.yml: launch smokes deferred to a new secret-freeverify-stapledjob.cmux-next-dev-daily.yml: split intosign(release environment, no bundle code),verify(no secrets, sidecar handshake),publish(job token only; publishes only the archive whose digestverifytested).repair-v0-64-25-helper-rpaths.yml: the sidecar check runs on the shipped release app before the certificate import.- Signing and other secret-holding jobs pick a literal Blacksmith label instead of
vars.*RUNNER*(the values are the same as the current variable values). - The guard
tests/test_ci_signing_keychain_is_deleted_before_bundle_code.pygains these rules (first commit fails onmain).release.ymlis the one remaining step-level job; its build/sign/verify/publish split is a separate PR.
Trade-offs
- A launch failure of the signed app is now found after the Apple submission (the verify job needs the signing job to finish), not before it. Publication still waits for it.
- More runner minutes: one more macOS job per variant before signing and one after.
Residual
build-nightly-appcan run on an owned, reused Mac (CI_NIGHTLY_TRUSTED_RUNNER,CI_SEED_TRUSTED_POOL). It holds no secrets, but its unsigned app is the signing input.release.ymlkeeps bundle code in its signing job until its split.
Testing
- On a Linux VM: the guard, and 73 workflow and script tests that read the touched workflows, pass (3 tests fail identically on the base tree because the VM lacks unrelated tooling).
- actionlint built from manaflow-ai/actionlint v1.7.12-manaflow.1 on the touched workflows: no new finding.
- Not verified before merge: a real nightly run. The new jobs run only on
main(schedule, push) and in the continuation.
Changelog
none
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Hardens the nightly, NEXT DEV, and repair release paths so no job holding signing, notarization, or publishing secrets runs bundle code. Every check that executes the app, its CLI, helpers, or sidecar moved to secret-free verify jobs, and publication only ships bytes whose digest a verify job tested.
Refactors
- Nightly is now prepare → sign → verify → publish:
prepare-nightly-variantinstalls the helper and cmux-tui client and runs all runtime regressions, the signing job signs only, andverify-signed-nightlylaunches the app, its CLI, sidecar, and SSH daemon and smokes the final DMG on the inline path. publish-nightlyrefuses any DMG whose SHA-256 is not a tested digest;verify-stapledjobs smoke each stapled DMG in the notarization continuations, and the auto-resume preflight continues only when every source verify job passed.cmux-next-dev-dailysplits into sign, verify, and publish jobs; the repair workflow runs its sidecar check on the shipped app before importing the certificate and refuses to re-sign a DMG whose digest differs from the one it verified.- Signing and other secret-holding jobs use literal Blacksmith runner labels so a repository variable cannot move them onto a reused machine.
scripts/ci/smoke-dmg.shlogs to stderr, writes its tested digest to fd 3, closes that fd for the launched app, and removes its smoke log; the guard test also covers it, the cmux-tui client install, andsecrets[...]references.release.ymlkeeps bundle code in its signing job for now; splitting it is a separate PR.- Launch failures surface after the Apple submission instead of before; publication still waits for verification. The build costs one extra macOS job per variant on each side of signing.
Summary by CodeRabbit
- Reliability
- Nightly and rolling releases now undergo signing, notarization, and launch checks before publication.
- Published disk images are verified against the exact artifacts that passed testing.
- Recovery releases also receive launch testing, and their disk images are checked before publication.