ci: run app checks in secret-free jobs for the nightly path

Merged
LA
22d ago
+886-210

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-nightly signs exactly that app and runs no bundle code: --embed without --verify-cli, launch smokes deferred in notarize-nightly-dmg.sh (CMUX_DEFER_LAUNCH_SMOKES), and it uploads the signed app for verification.
    • New verify-signed-nightly (no secrets, contents: read only): 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-nightly needs verify-signed-nightly and refuses any DMG whose SHA-256 is not a tested digest.
  • auto-resume-nightly-notarization.yml: the preflight continues a source run only when every Verify signed nightly app (<variant>) job passed. wait-and-staple and staple-outer defer their launch smokes; new verify-stapled (no secrets) launches the app inside each exact stapled DMG (checked against final_dmg_sha256) and records the digest; publish needs it and requires the tested digest.
  • resume-nightly-notarization.yml: launch smokes deferred to a new secret-free verify-stapled job.
  • cmux-next-dev-daily.yml: split into sign (release environment, no bundle code), verify (no secrets, sidecar handshake), publish (job token only; publishes only the archive whose digest verify tested).
  • 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.py gains these rules (first commit fails on main). release.yml is 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-app can 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.yml keeps 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


View with [code]smith Autofix with [code]smith
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-variant installs the helper and cmux-tui client and runs all runtime regressions, the signing job signs only, and verify-signed-nightly launches the app, its CLI, sidecar, and SSH daemon and smokes the final DMG on the inline path.
  • publish-nightly refuses any DMG whose SHA-256 is not a tested digest; verify-stapled jobs smoke each stapled DMG in the notarization continuations, and the auto-resume preflight continues only when every source verify job passed.
  • cmux-next-dev-daily splits 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.sh logs 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, and secrets[...] references.
  • release.yml keeps 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.