fix(release): add protected tag recovery dispatch#2195
Conversation
Co-authored-by: npub1ft62tztwwm2x9xamk25smmuaj4sfckdkldksruf2x2jwqalffkrq0g7arr <4af4a5896e76d4629bbbb2a90def9d95609c59b6fb6d01f12a32a4e077e94d86@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1ft62tztwwm2x9xamk25smmuaj4sfckdkldksruf2x2jwqalffkrq0g7arr <4af4a5896e76d4629bbbb2a90def9d95609c59b6fb6d01f12a32a4e077e94d86@sprout-oss.stage.blox.sqprod.co>
|
@codex review |
|
@builderbot review |
|
To use Codex here, create a Codex account and connect to github. |
|
🤖 Closing per tomb's decision: we are skipping the 0.4.8 tag recovery and cutting a fresh 0.4.9 release, which exercises the fixed auto-tag workflow via a normal release PR merge. Keeping the branch in case we want this recovery entry point later. |
brow
left a comment
There was a problem hiding this comment.
🤖 Independent review at exact head 2dd7692 (round-1 sole reviewer per the release-recovery review loop).
Verified:
- Fail-closed dispatch gates: numeric PR input regex, PR must exist, closed+merged, base must be
block/buzz:main, same-repo head (deleted fork resolves to""and is rejected), dispatch restricted to explicit release-lane branches, recorded merge SHA must be 40-hex. Every violation exits 1;set -euo pipefailplusjq -erkeep null/missing fields fatal. - No injection surface:
inputs.pull_request_numberreaches the shell only via env and is validated;branch/merge_shaflow through$GITHUB_OUTPUT(git ref names cannot contain newlines) and env-scoped expansions; no new inline${{ }}interpolation inside run bodies. - Tag authority unchanged: App-token (buzz-release-bot) creation retained; default token stays read-only (
contents: read, plus newpull-requests: read); nogh workflow runintroduced, so the mobile lane stays push-only with manualsprout_refconsumption. - Existing-tag semantics preserved and corrected:
$TARGET_SHA(validated merge commit) replaces$GITHUB_SHA, which is required for dispatch and removes merge-ref ambiguity on the merge-event path; exists-at-expected skips, exists-elsewhere fails. - Merge-event path regression check: the resolve step re-reads the same fields the event payload provided; the Chart.yaml auto-detect fallback remains reachable only from merge events; the
enabled=falseshort-circuit is unchanged. - Contract test tracks source: the 11 new assertions match the exact new workflow lines; I ran
scripts/test-release-ref-contract.shlocally at 2dd7692 and it passed. CI is green at this head, including zizmor and the contract step. - Dry-run of the motivating recovery: PR #2187 (
mobile-release/0.4.8, merged asfb8c90cfinto main, same-repo head) passes every gate, resolving to tagmobile-v0.4.8atfb8c90cf; the tag is currently absent (404), so creation proceeds.
One preexisting note, not introduced by this PR: any write-access workflow already had access to the tagger App secret, so the dispatch entry point does not widen who can reach the key; the gates here constrain what a well-formed dispatch can do.
Two non-blocking inline notes below (error-message fidelity; dispatch lane scope). Verdict: approve.
| exit 1 | ||
| fi | ||
|
|
||
| if ! pr_json="$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER" 2>/dev/null)"; then |
There was a problem hiding this comment.
🤖 Non-blocking: 2>/dev/null collapses every gh api failure mode (rate limit, auth, transient 5xx) into the same "was not found" error. In a recovery tool an operator may act on that message, so capturing stderr into the error (or dropping the redirect) would keep the fail-closed behavior while surfacing the real cause.
| fi | ||
| if [ "$EVENT_NAME" = "workflow_dispatch" ]; then | ||
| case "$head_ref" in | ||
| version-bump/*|relay-release/*|chart-release/*|push-chart-release/*|mobile-release/*) ;; |
There was a problem hiding this comment.
🤖 Non-blocking, intended-scope confirmation: restricting dispatch to explicit release-lane branches means a missed chart tag from the auto-detect lane (ordinary internal PR bumping Chart.yaml) is not recoverable via this path. That reads as a deliberate conservative choice consistent with the header comment; flagging so the scope decision is explicit.
What changed?
Adds a manual recovery entry point to the release auto-tag workflow. An operator supplies a merged release pull request number, and the workflow resolves its recorded merge commit and release branch before using the existing
buzz-release-botApp path to create the tag.The recovery path rejects unmerged pull requests, forks, non-
maintargets, invalid merge commits, and branches outside the explicit release lanes. Existing tags still fail closed unless they already point to the expected merge commit.Why?
The
mobile-v0.4.8tag was missed when the original merge event ran an older, broken workflow definition. GitHub reruns preserve that definition, and the protected release-tag ruleset prevents manual tag creation, so recovery needs a reviewed and auditable entry point.How is it tested?
git diff --check🤖 This PR was authored with an agent.