Skip to content

fix(promote): accept hotfix releases already merged back to main - #18

Merged
gpmayorga merged 1 commit into
mainfrom
fix/promote-merged-back-hotfixes
Oct 21, 2026
Merged

gpmayorga merged 1 commit into
mainfrom
fix/promote-merged-back-hotfixes

Conversation

@gpmayorga

Copy link
Copy Markdown
Collaborator

Problem

app-promote-production.yml refuses any latest release whose tag commit is not an ancestor of main. A hotfix cut from an older release tag can never pass that check, even after it is merged back, because squash merges and cherry-picks give main new commits with new SHAs.

This blocked invest-v3 v3.3.5 on 2026-10-21 (failed run):

##[error]Release v3.3.5 (49412fee…) is not on main.

v3.3.5 was cut from v3.3.3 on hotfix/basin_allow_list because main carried unreleased work (the SDK snapshot bump and others). Its two commits were already on main as squash/cherry-pick equivalents (centrifuge/apps-invest#363 and centrifuge/apps-invest#341; git cherry origin/main v3.3.5 marks both -), merged the day before the release was published. The release went out through rollback.yml instead, which has no on-main check at all.

Change

When the tag is not on main, the check now:

  1. finds where the release left main (git merge-base),
  2. diffs the release against that point (--binary --no-renames),
  3. requires that delta to reverse-apply cleanly to main's tree (git apply --cached --check --reverse against a temporary index; nothing is written).

So a hotfix promotes only once every change it carries is on main, however it got there (squash merge, merge commit, cherry-pick, multi-commit branch squashed into one PR). Releases on main take the unchanged ancestry fast path.

Still refused, with git apply naming the files:

  • a hotfix with any change not merged back (new file, edited lines, deleted file or line),
  • a hotfix whose lines main has since rewritten (rollback.yml stays the escape hatch for that case),
  • a tag with no shared history with main.

Also updated: the Slack failure reason, the workflow header, the README gate description (which also gains the latest-tag dispatch allowed since #14), and a CLAUDE.md note so the check is not reverted to ancestry-only.

Validation

  • actionlint 1.7.7 (with shellcheck) and yamllint (relaxed, line-length off): clean.
  • Ran the step's run: block, extracted from the workflow, against apps-invest history:
    • v3.3.5 vs current main: passes (hotfix delta already on main).
    • v3.3.3 vs current main: passes (on main).
    • v3.3.5 vs main before centrifuge/apps-invest#363 merged: refused, naming instantRedemptionWhitelist.ts and its test.
  • Synthetic repo: multi-commit hotfix squash-merged (with a binary change and a rename) passes; main moving on elsewhere in the same file passes; empty delta passes; unmerged added file, deleted file, removed line and rewritten hotfix lines are all refused.

Rollout

No contract change: inputs, secrets and permissions are untouched, so callers need nothing. Takes effect on the next promote run after merge.


Generated by Claude Code

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.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GNMKQZ7D8fHsi98P76jU5Z
@gpmayorga gpmayorga added the bug Something isn't working label Oct 21, 2026 — with Claude
@gpmayorga
gpmayorga marked this pull request as ready for review October 21, 2026 16:00
@gpmayorga
gpmayorga requested a review from hieronx as a code owner October 21, 2026 16:00
@gpmayorga
gpmayorga merged commit b4f05d7 into main Oct 21, 2026
3 checks passed
@gpmayorga
gpmayorga deleted the fix/promote-merged-back-hotfixes branch October 21, 2026 16:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants