Skip to content

Accept promote dispatches from the latest release tag, not just main - #14

Merged
gpmayorga merged 1 commit into
mainfrom
claude/promote-accept-release-tag-ref
Sep 11, 2026
Merged

gpmayorga merged 1 commit into
mainfrom
claude/promote-accept-release-tag-ref

Conversation

@gpmayorga

Copy link
Copy Markdown
Collaborator

Problem

Three real dispatch attempts of apps-invest's promote-production.yml today: two people used GitHub's "Run workflow" dropdown to select the release tag (v3.2.2) — the intuitive choice while looking at a release — and both were rejected by Verify dispatched from main. That's user-hostile: selecting the tag you're about to promote is the natural first instinct during a release, not a mistake to train people out of.

Why relaxing this is safe

The ref used to dispatch was never the promotion target. resolve-tag always resolves GitHub's actual latest release via gh api repos/.../releases/latest, and every downstream step (promote job) operates on needs.resolve-tag.outputs.tag, never on $GITHUB_REF or any dispatch input — there is no tag input at all. The ref-check exists solely to pin which copy of the workflow YAML is trusted, not to select what gets deployed.

So a tag ref is trustworthy under exactly one condition: it's the same tag resolve-tag is about to promote anyway. Accepting that one additional ref doesn't touch the actual security boundary (AUTHORIZED_DEPLOYERS allowlist, resolve-tag's API lookup, the on-main ancestry check in the promote job) — it only widens which refs may invoke the trusted, unchanged logic.

Fix

Verify dispatched from main now accepts:

  • refs/heads/main (unchanged), or
  • refs/tags/<tag> where <tag> exactly matches resolve-tag's output (GitHub's current latest release)

Any other ref — a feature branch, an older or unrelated tag — is refused with the same fail-closed error, now pointing at rollback.yml for deploying something else. Updated the in-file header comment and CLAUDE.md's "intentional decisions" section to match, since the old wording documented "must be dispatched from main" as a hard rule.

Verification

  • Reviewed all three real failed runs on apps-invest (#1–#3) against the new logic: #1/#2 (dispatched from v3.2.2, which was GitHub's latest release at the time) would now pass the ref-check; a dispatch from an unrelated/older tag or a feature branch still fails.
  • yamllint clean.
  • No change to resolve-tag, Check deployer authorization, or the promote job's on-main ancestry check — the actual deploy target and authorization logic are untouched.

🤖 Generated with Claude Code


Generated by Claude Code

Dispatching "Run workflow" from the tag being released is the natural
first move during a release, and rejecting it forced deployers to
remember a counter-intuitive workaround (switch to main first). It's
safe to allow: promote never uses the dispatch ref as the deploy
target — it always resolves and promotes GitHub's latest release via
the API, regardless of which ref triggered the run.

Now accepts refs/heads/main or refs/tags/<latest-release-tag> and
refuses everything else (a feature branch, an older tag) with the
same fail-closed message pointing at rollback.yml.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZs3qvSwpTvBZEPSqfXQzQ
@gpmayorga
gpmayorga marked this pull request as ready for review September 11, 2026 12:35
@gpmayorga
gpmayorga requested a review from hieronx as a code owner September 11, 2026 12:35
@gpmayorga
gpmayorga merged commit 9f8b428 into main Sep 11, 2026
3 checks passed
@gpmayorga
gpmayorga deleted the claude/promote-accept-release-tag-ref branch September 11, 2026 12:41
gpmayorga added a commit that referenced this pull request Oct 21, 2026
Promotion refused any latest release whose commit is not an ancestor of
main. A hotfix cut from an older release tag never is: merging it back
with a squash merge or a cherry-pick gives main new commits. invest-v3
v3.3.5 hit this on 2026-10-21. Its changes had been squash-merged to
main the day before (#363 in apps-invest), yet promotion failed with
"not on main" and the release went out through rollback.yml.

When the tag is not on main, the check now takes the release's delta
from its merge-base with main and requires it to reverse-apply cleanly
to main's tree, i.e. every change the hotfix carries is already on main.
Releases on main pass as before. A hotfix with any change missing from
main (added, edited or deleted files) is still refused, and the error
names the files.

Also updates the README gate description, including the latest-tag
dispatch allowed since #14, and records the rule in CLAUDE.md.


Claude-Session: https://claude.ai/code/session_01GNMKQZ7D8fHsi98P76jU5Z

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants