ci(nightly-next): never ship a build on retained DerivedData or replayed input times

Merged
LA
19d ago
+121-183

Summary

On a trusted worker (CI_NIGHTLY_NEXT_TRUSTED_* set), scripts/ci/nightly_build_state.py linked build-universal to a retained DerivedData and the build step replayed old input mtimes into it (#21167). An incremental Xcode build can keep stale autolink and force-load data in objects that it did not recompile after an import set changed (the import-set defect found in the 2026-10-18 build audit). Thus, setting those variables would ship those objects in NIGHTLY NEXT.

This change:

  • nightly_build_state.py prepare nightly-next now deletes every retained DerivedData of the runner (all toolchain and path identities), leaves build-universal as an empty real directory, and keeps only the Swift package checkouts link (.spm-cache, source only, no build output).
  • Removes replay and record and their calls in Build nightly app (Release). Inputs keep their checkout times.
  • Renames Prepare persistent Release DerivedData to Start Release DerivedData empty. The classic-channel path in this file gets the same empty start as main (manaflow-ai/cmux#21761), with the same text, so a later main merge is clean.
  • Rewrites tests/test_nightly_build_state.py (it runs the real prepare on a temporary home and workspace) and the guard in tests/test_nightly_universal_build.sh. The test commit comes first and fails on the old code.

This change does not change CI_NIGHTLY_NEXT_TRUSTED_*, NIGHTLY_NEXT_HOLD, NIGHTLY_NEXT_WRITER, or any runner variable.

Added build time

Today: none. CI_NIGHTLY_NEXT_TRUSTED_* is not set, so NIGHTLY NEXT builds on Blacksmith, which already starts from an empty tree (run 39111318928: 6m31s in Build nightly app (Release)). When a trusted worker is set, its builds take about the same cold time, less the package fetch, instead of an incremental time that was never measured.

Related

#21552 conflicts with this change in three places:

  • It adds id: persistent-release to the old step name Prepare persistent Release DerivedData, which this change renames.
  • It adds a step Replay persistent NEXT source times before Rust helpers that runs nightly_build_state.py replay. After this change, that call exits 2 (usage), so the NEXT build fails closed.
  • It gates the cache restore and save steps on steps.persistent-release.outputs.enabled, and its routing test expects trusted runners to keep persistent DerivedData.

The content-keyed compilation cache part of #21552 agrees with the hq-25 decision. The replay step, the persistent-release gating and the persistent-state test do not. #21552 must drop those three parts after this change lands.

Testing

  • ruby -e 'require "psych"; Psych.load_file(".github/workflows/nightly.yml")' parses the workflow.
  • tests/test_nightly_build_state.py, tests/test_nightly_universal_build.sh, tests/test_nightly_next_arm64_only.py and tests/test_nightly_trusted_routing.py run in CI Guards (not on a local Mac, per team policy).
  • Not run: a trusted-worker NEXT build (no trusted worker is set).

Changelog

none