Repository navigation
Accept promote dispatches from the latest release tag, not just main - #14
Merged
Merged
Conversation
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
marked this pull request as ready for review
September 11, 2026 12:35
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Three real dispatch attempts of apps-invest's
promote-production.ymltoday: 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 byVerify 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-tagalways resolves GitHub's actual latest release viagh api repos/.../releases/latest, and every downstream step (promotejob) operates onneeds.resolve-tag.outputs.tag, never on$GITHUB_REFor any dispatch input — there is notaginput 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-tagis about to promote anyway. Accepting that one additional ref doesn't touch the actual security boundary (AUTHORIZED_DEPLOYERSallowlist, resolve-tag's API lookup, the on-main ancestry check in thepromotejob) — it only widens which refs may invoke the trusted, unchanged logic.Fix
Verify dispatched from mainnow accepts:refs/heads/main(unchanged), orrefs/tags/<tag>where<tag>exactly matchesresolve-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.ymlfor deploying something else. Updated the in-file header comment andCLAUDE.md's "intentional decisions" section to match, since the old wording documented "must be dispatched from main" as a hard rule.Verification
#1–#3) against the new logic:#1/#2(dispatched fromv3.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.yamllintclean.resolve-tag,Check deployer authorization, or thepromotejob's on-main ancestry check — the actual deploy target and authorization logic are untouched.🤖 Generated with Claude Code
Generated by Claude Code