Skip to content

fix(release): add protected tag recovery dispatch#2195

Closed
brow wants to merge 1 commit into
mainfrom
fix/release-tag-recovery
Closed

fix(release): add protected tag recovery dispatch#2195
brow wants to merge 1 commit into
mainfrom
fix/release-tag-recovery

Conversation

@brow

@brow brow commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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-bot App path to create the tag.

The recovery path rejects unmerged pull requests, forks, non-main targets, 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.8 tag 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?

  • Full repository pre-push suite
  • Release ref contract, including recovery-path assertions
  • Workflow YAML parse and git diff --check

🤖 This PR was authored with an agent.

Co-authored-by: npub1ft62tztwwm2x9xamk25smmuaj4sfckdkldksruf2x2jwqalffkrq0g7arr <4af4a5896e76d4629bbbb2a90def9d95609c59b6fb6d01f12a32a4e077e94d86@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1ft62tztwwm2x9xamk25smmuaj4sfckdkldksruf2x2jwqalffkrq0g7arr <4af4a5896e76d4629bbbb2a90def9d95609c59b6fb6d01f12a32a4e077e94d86@sprout-oss.stage.blox.sqprod.co>
@brow

brow commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@brow

brow commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@builderbot review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@brow

brow commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

🤖 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 brow closed this Jul 21, 2026

@brow brow left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 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 pipefail plus jq -er keep null/missing fields fatal.
  • No injection surface: inputs.pull_request_number reaches the shell only via env and is validated; branch/merge_sha flow 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 new pull-requests: read); no gh workflow run introduced, so the mobile lane stays push-only with manual sprout_ref consumption.
  • 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=false short-circuit is unchanged.
  • Contract test tracks source: the 11 new assertions match the exact new workflow lines; I ran scripts/test-release-ref-contract.sh locally 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 as fb8c90cf into main, same-repo head) passes every gate, resolving to tag mobile-v0.4.8 at fb8c90cf; 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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 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/*) ;;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant