ci(nightly-next): train drive job runs only feat-cmux-next code with the release App; fix signing-guard base red
Fixes the signing-guard red on feat-cmux-next, and closes the review finding on #22669 (receipt) that the train's drive job runs code from inputs.tip while holding the release App token.
What changes
-
nightly-next-train.ymldrive job:- It checks out
github.sha, the dispatched feat-cmux-next head (the job runs only onrefs/heads/feat-cmux-next). It no longer checks outinputs.tip. - A new step, "Check the tip is on feat-cmux-next", runs before the token is minted and has no
if:. It refuses a tip that isn't exactly 40 hex characters, or isn't an ancestor of that head. An empty tip still means the head. - Before this, anyone who could dispatch the workflow could run an unreviewed commit's
nightly_next_train_drive.pywith a token that can write contents and workflows. - No current caller passes
tip: the tick sends onlymode, and fix-forward sends the build and quarantine inputs. So nothing that works today breaks.
- It checks out
-
tests/test_nightly_next_train_drive_pin.py(ci-guards, groupci) checks that:- the checkout ref is
${{ github.sha }}; inputs.tipnever appears in arun:orwith:;- the check runs before the mint step and has no
if:orcontinue-on-error.
It also runs the real check step's script against a scratch repo: empty, the head and an ancestor pass; a side-branch commit, a short SHA, a branch name and a tip with a newline are refused.
- the checkout ref is
-
tests/test_ci_signing_secrets_need_release_environment.py: #22661 added the drive job'sCMUX_RELEASE_APP_*read in environmentnightly-next-train, and that turned this guard red on feat-cmux-next. The guard now allows that one job, only the release App credentials, and only that environment. That's the same shape as the promote jobs' existing allowance, and the same change as in #22669. -
Second path, from the review of 185c159a (receipt):
good_buildcould name a fork's pull_request run on a branch callednightly-next. Its commit's train trailers then named "carried" cars, and the car guards check out and run those cars while the token is held. Fixed twice:nightly_next_train.require_on_tip: before any car guard runs, the drive refuses a train whose source or any car commit isn't an ancestor of the tip. The tip is pinned to the dispatched head.nightly_next_fix_forward.build_commit: a build is placed only by a push, schedule or dispatch run whose head repository is this one.find-nightly-next-recovery.pyalready checks the same.- New tests refuse a crafted base naming an off-tip car, an off-tip source and a short SHA. A fork run and a pull_request run now place no build. The drive-pin test also checks that
require_on_tipcomes beforerun_car_guards, and that no step moves the checkout to the tip.
Risk
risk:high: the change touches release credentials. It makes the workflow strictly safer. coord-deputy is taking it to Leo together with #22669.
Observed / Not run
-
Ran:
- actionlint on
nightly-next-train.ymlandci-guards.yml: clean; prepush-gate.py: no new violations;- the check step's extracted script under bash in a scratch repo: all seven cases behave as described.
Writing that step turned up a bug in my first draft:
grep -xmatches each line on its own, so a valid first line followed by a newline got through. The step now uses bash=~, and the test includes that case. - actionlint on
-
Not run locally: the Python tests. A hook on this Mac blocks test-file runs, so CI's ci-guards runs them on this head.
-
The train itself hasn't run with
NIGHTLY_NEXT_TRAIN=on, and environmentnightly-next-traindoesn't exist yet.
Changelog
none
Agent session: cosmic-gray
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes the nightly-next train's drive job so it holds the release App token only while running code from the dispatched feat-cmux-next head, and fixes the signing-guard red that #22661 caused on that branch.
- The drive job checks out
github.shainstead ofinputs.tip, and a new step before token minting refuses a tip that isn't a 40-hex ancestor of that head. - Previously anyone able to dispatch the workflow could run an unreviewed commit's drive script with a token that can write contents and workflows; no current caller passes
tip. - A new
require_on_tipcheck refuses a train whose source or car commits fall outside the tip's history, closing a path where a craftedgood_buildcould run unreviewed cars under the token. build_commitnow only places a build from a push, schedule, or dispatch run coming from this repository, so a fork'spull_requestrun can't name the base.- New ci-guards tests pin the checkout ref and check order, keep
inputs.tipout ofrun:/with:, and exercise the real check script and the new refusal paths. - The signing guard now allows the drive job's
CMUX_RELEASE_APP_*read in environmentnightly-next-trainonly, in the same shape as the promote jobs' existing allowance.