feat(reusable): disable auto-merge when a new commit is pushed

Reusable workflow that consumers call from their pr-guards.yml on
pull_request:synchronize. When a new commit is pushed to an open PR
that has auto-merge enabled, this disables auto-merge and posts a
comment so the operator must explicitly re-engage after verifying.

Background: on 2026-04-27, PR #2174 in molecule-core auto-merged
with only the first commit because the second commit was pushed
AFTER the merge queue had locked the PR's SHA. The second commit
ended up orphaned on a merged-and-deleted branch (the wider
"automatically delete head branches" repo setting now blocks the
push entirely; this workflow catches the race window where the PR
is queued but not yet merged).

Defense in depth — if both fixes are active:
1. Repo setting "delete branch on merge" prevents pushes to a
   merged branch (post-merge orphan case).
2. This workflow catches in-queue races (push lands while the
   queue is processing) by force-disabling auto-merge so the
   operator must re-engage explicitly.

Together they cover the full lifecycle of "auto-merge enabled →
new commits arrive" without relying on operator discipline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hongming Wang 2026-04-27 06:38:15 -07:00
parent 0335ec169c
commit f12b8981d2

View File

@ -0,0 +1,53 @@
name: Disable auto-merge on push
# Reusable guard against the "I enabled auto-merge then pushed more
# commits" race. Background: on 2026-04-27, PR #2174 in molecule-core
# auto-merged with only the first commit because the second commit
# was pushed AFTER the merge queue had already locked the PR's SHA.
# The second commit ended up orphaned on a merged-and-deleted branch.
#
# Mechanism: on every `pull_request: synchronize` event (= new commit
# pushed to an open PR), check if auto-merge is enabled. If yes,
# disable it and post a comment. This forces the operator to
# re-engage `gh pr merge --auto` after the new push, with the
# re-engagement acting as the verification step.
#
# Call from each repo's .github/workflows/ via a thin wrapper:
#
# name: pr-guards
# on:
# pull_request:
# types: [synchronize]
# permissions:
# pull-requests: write
# jobs:
# disable-auto-merge-on-push:
# uses: Molecule-AI/molecule-ci/.github/workflows/disable-auto-merge-on-push.yml@main
#
# False-positive behavior: if a CI bot pushes (e.g. dependency-update
# rebase, secret rotation), this also disables auto-merge for that
# PR. That's acceptable — the operator who originally enabled
# auto-merge gets notified and re-engages, which is exactly the
# verify-after-machine-edits behavior we want.
on:
workflow_call:
jobs:
guard:
name: Disable auto-merge on push
runs-on: ubuntu-latest
if: github.event.pull_request.auto_merge != null
permissions:
pull-requests: write
steps:
- name: Disable auto-merge
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
NEW_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -eu
gh pr merge "$PR" --disable-auto -R "$REPO" || true
gh pr comment "$PR" -R "$REPO" --body "🔒 Auto-merge disabled — new commit (\`${NEW_SHA:0:7}\`) pushed after auto-merge was enabled. The merge queue locks SHAs at entry, so subsequent pushes can race. Verify the new commit and re-enable with \`gh pr merge --auto\`."