feat(infra): ./infra approve opens the promotion PR for vault/docs-only changes #164

Merged
jfraeys merged 1 commit from jfraeys/feat/approve-carry-over into main 2026-10-14 18:32:44 -04:00
Owner

Gap: a change staging never runs for (a rotation's vault PR, docs, scripts outside lib/) opens no promotion PR. After a rotation, ./infra deploy from main was refused (checkout is not the approved commit), and checking out the approved commit would deploy the old secrets: the only way through was the logged emergency bypass. Found 2026-10-14 when #163 landed after #160 was approved (gate: REFUSED ... git checkout 5d15f97).

./infra approve with no open promotion PR now:

  1. takes the validated commit from the last merged promotion PR;
  2. refuses unless main differs from it only in files staging does not exercise (promotion_drift, the gate's own rule), or if prod already contains main;
  3. lists the commits it would promote, opens the main -> prod PR whose body carries passed on commit <validated> (so the deploy gate accepts it);
  4. continues as before: CI, plan-prod.sh, you type the PR number, merge, confirm the merge took.
    A PR number that is not open is still refused (no carry-over then).

CI check fix: merge commits on main carry no statuses (CI runs on PR heads; 63bbb25 has none). When the head has none, every first-parent commit since the validated one must be a PR merge whose PR head is green; a commit that did not come through a PR, or a red PR, is refused.

rotate-secret.sh's next steps and daily-commands.md now include ./infra approve between merging the vault PR and deploying.

Tests: test-approve-promotion.sh 22 cases (+7 for the above). test-promotion, test-infra-cli, test-rotation-lock pass. Not run against the real Forgejo (it opens a PR).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ

**Gap:** a change staging never runs for (a rotation's vault PR, docs, scripts outside `lib/`) opens no promotion PR. After a rotation, `./infra deploy` from main was refused (checkout is not the approved commit), and checking out the approved commit would deploy the *old* secrets: the only way through was the logged emergency bypass. Found 2026-10-14 when #163 landed after #160 was approved (gate: `REFUSED ... git checkout 5d15f97`). **`./infra approve` with no open promotion PR now:** 1. takes the validated commit from the last merged promotion PR; 2. refuses unless main differs from it only in files staging does not exercise (`promotion_drift`, the gate's own rule), or if prod already contains main; 3. lists the commits it would promote, opens the `main -> prod` PR whose body carries `passed on commit <validated>` (so the deploy gate accepts it); 4. continues as before: CI, `plan-prod.sh`, you type the PR number, merge, confirm the merge took. A PR number that is not open is still refused (no carry-over then). **CI check fix:** merge commits on main carry no statuses (CI runs on PR heads; `63bbb25` has none). When the head has none, every first-parent commit since the validated one must be a PR merge whose PR head is green; a commit that did not come through a PR, or a red PR, is refused. `rotate-secret.sh`'s next steps and `daily-commands.md` now include `./infra approve` between merging the vault PR and deploying. **Tests:** `test-approve-promotion.sh` 22 cases (+7 for the above). test-promotion, test-infra-cli, test-rotation-lock pass. Not run against the real Forgejo (it opens a PR). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
feat(infra): ./infra approve opens the promotion PR for vault/docs-only changes
All checks were successful
Lint / lint (pull_request) Successful in 2m0s
Secret scan / scan (pull_request) Successful in 14s
0ab5bb33d1
A change staging never runs for (a rotation's vault PR, docs, scripts outside
lib/) opens no promotion PR, so after a rotation `./infra deploy` from main was
refused (the checkout is not the approved commit) and checking out the approved
commit would have deployed the old secrets: the only way through was the logged
emergency bypass. Found 2026-10-14 when #163 landed after #160 was approved.

With no open promotion PR, `./infra approve` now: takes the validated commit from
the last merged promotion PR, refuses unless main differs from it only in files
staging does not exercise (promotion_drift, the gate's rule), lists what would be
promoted, opens the main -> prod PR carrying "passed on commit <validated>", and
continues with the usual plan, typed confirmation and confirmed merge. A PR number
that is not open is still refused.

CI check: merge commits on main carry no statuses (CI runs on PR heads; 63bbb25
had none), so when the head has none, every first-parent commit since the
validated one must be a PR merge whose head is green; anything else is refused.

rotate-secret.sh's next steps and daily-commands.md now include ./infra approve.
test-approve-promotion.sh: 22 cases (+7: carry-over opens and merges, names the
validated commit, refuses deploy-affecting drift, nothing to promote, named PR
not open, no-status head with a non-PR commit / all green PRs / a red PR).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jfraeys/infra!164
No description provided.