Skip to content

ci: fast-forward bump PRs as the release app instead of waiting for a human merge - #266

Merged
Larry-Osakwe merged 2 commits into
mainfrom
devin/1788365735-bump-ff-merge
Sep 2, 2026
Merged

ci: fast-forward bump PRs as the release app instead of waiting for a human merge#266
Larry-Osakwe merged 2 commits into
mainfrom
devin/1788365735-bump-ff-merge

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Summary

scripts/bump_package.py opened the bump PR, armed auto-merge, asked for a squash merge, then polled up to 1800s for the PR to merge and died with the tag stranded. The require-review ruleset lists the release app as a bypass actor, but GitHub only honours app bypass on direct ref updates, never on the merge API or auto-merge, so neither path could ever land the PR without a human. Two manual tag recoveries this week.

The app-performed direct ref update is now the primary, deliberate merge path, sequenced so the incident-class protections from #253 stay intact (tag only after a verified merge, scoped bump detection and NO_COMMITS_FOUND handling untouched, recover_untagged_bump still covers a run that dies between merge and tag):

bump_package():
    ... cz bump, remote branch at parent_sha, signed commit, create_bump_pr (unchanged, minus auto-merge arming)
    merge_sha = merge_bump_pr(repo, pr, branch, target, parent_sha, files, headline, body)
    if merge_sha is None: fail loudly, no tag
    create_and_push_tag(repo, tag, merge_sha)

merge_bump_pr():                       # up to 3 rounds
    head = wait_for_checks(pr)          # SUCCESS -> head sha; FAILURE/CLOSED/timeout(1200s) -> None
    live = get_live_branch_sha(target)  # GET repos/{repo}/git/ref/heads/{target}
    if live != base_sha:                # strict fast-forward guard
        rebase_bump_branch(...)         # refuses if target's new commits touched the bumped files,
        base_sha = live; continue       # else moves the *bump branch* to live and re-creates the signed commit
    fast_forward_target(target, head)   # PATCH repos/{repo}/git/refs/heads/{target} sha=head, never force
    return verify_pr_merged(pr, head)   # MERGED with mergeCommit.oid == head within 120s, else None

checks_verdict replaces checks_green: FAILURE beats PENDING, so one red check aborts even while others still run; pending never passes. The fast-forward log line states the ref update was performed by the release app under its ruleset bypass, so an auditor understands why no review appears on the PR.

Auto-merge arming was dropped: with the app doing the merge it only adds a race where a squash merge could land first and turn the fast-forward into a spurious non-FF refusal.

.github/workflows/bump-package.yml is unchanged. The app installation token already needs contents write (it creates tag refs and the bump branch today) and pull-requests write; the fast-forward is another refs write under the same permission, so no additional permission is required.

Tests (python3 -m unittest discover -s scripts -p 'test_*.py', 28 pass with commitizen installed): strict-FF guard refuses when the target moved and never touches the refs API; the FF PATCH carries no force; rebuild refuses when the target changed bumped files; tag is unreachable when verify_pr_merged returns None; checks gate refuses on FAILURE and times out on PENDING without opening the merge path.

Link to Devin session: https://app.devin.ai/sessions/52b1004809294cdaa25c036b6c644e7f
Open in Devin Desktop: https://app.devin.ai/desktop/session/52b1004809294cdaa25c036b6c644e7f?variant=devin
Requested by: @Larry-Osakwe

… human merge

The require-review ruleset lists the release app as a bypass actor, but
GitHub only honours app bypass on direct ref updates, never on the merge
API or auto-merge. The bump script therefore polled 1800s for a human to
merge and then died with the tag stranded. The script now waits for the
PR's checks, verifies the live target tip is still the SHA the bump was
built on, fast-forwards the target ref without force, verifies GitHub
reports the PR merged, and only then tags.

Co-Authored-By: Larry Osakwe <larry@keycard.ai>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

An unregistered check rollup reads as pending, never passing, and the
stable-wait re-arms after every bump-branch rebuild, so a fresh commit
cannot merge before Actions reports on it. A PR merged by hand mid-wait
is adopted and tagged at its merge commit. A stale bump branch or open
bump PR from a failed run is reused, and the branch is deleted after
the tag. A refused fast-forward re-probes the live tip and rebuilds
within the round budget, and a rebuild refuses when the target changed
any file under the package directory. Failed-check aborts name the
blocking check.

@Larry-Osakwe Larry-Osakwe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed as release infrastructure: the checks gate fails closed on empty rollups, the fast-forward is strict with the non-force PATCH as the TOCTOU backstop, the tag is unreachable without a verified merge, and all four lived failure modes (5am hands-off, human merge mid-wait, failed checks, moved main) end clean. Five mutation checks caught, 42 script tests green.

@Larry-Osakwe
Larry-Osakwe merged commit 8a2f418 into main Sep 2, 2026
8 checks passed
@Larry-Osakwe
Larry-Osakwe deleted the devin/1788365735-bump-ff-merge branch September 2, 2026 18:48
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