fix(#698): make auto-backmerge main→next actually land (admin-merge via PAT) (#699)

The Auto Back-Merge workflow could open a back-merge PR but never landed it,
and silently reported success on failure. Fixes:

- Merge via admin bypass using GSD_BOT_PR_TOKEN (the PAT) instead of auto-merge,
  since back-merge PRs structurally can't satisfy next's required checks
  (Issue-link / PR-template / changeset-lint).
- Stop swallowing create/merge failures with "|| echo ::warning" — real
  failures now fail the job. (That greenwashing hid the whole bug.)
- Resolve the PR number with `--jq '.[0].number // empty'` (a no-match returns
  the string "null", not empty) and capture a freshly-created PR's number from
  the create URL to avoid GitHub API eventual-consistency races.
- Merge the exact PR number (env-bound) rather than by branch name.
- Force-push the disposable SHA-named bot branch, guarded by a
  chore/backmerge-main-to-next-* name check so a mislabeled PR can't redirect
  the force-push.
- Apply labels non-fatally so a missing label can't abort PR creation.
- On a genuine merge conflict, fail loudly (::error + exit 1) instead of
  pushing an empty branch and opening a PR with no diff.

Closes #698

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-04 23:46:17 -04:00
committed by GitHub
parent 0be4e442ff
commit d494f24cbd

View File

@@ -71,6 +71,10 @@ jobs:
EXISTING_BR=$(echo "$EXISTING_PR" | jq -r '.headRefName // empty')
if [ -n "$EXISTING_BR" ]; then
case "$EXISTING_BR" in
chore/backmerge-main-to-next-*) ;;
*) echo "::error::refusing to operate on non-backmerge branch: $EXISTING_BR"; exit 1 ;;
esac
echo "Updating existing back-merge branch: $EXISTING_BR"
git fetch origin "$EXISTING_BR":"$EXISTING_BR" || true
git checkout "$EXISTING_BR"
@@ -79,7 +83,7 @@ jobs:
echo "::warning::Merge conflict back-merging main into existing back-merge branch. Human resolution required."
exit 1
}
git push origin "$EXISTING_BR"
git push --force origin "$EXISTING_BR"
echo "branch=$EXISTING_BR" >> "$GITHUB_OUTPUT"
echo "reused=true" >> "$GITHUB_OUTPUT"
else
@@ -87,26 +91,17 @@ jobs:
git checkout -b "$BR" next
# Bring main's commits onto next via a merge commit (preserves tag history).
if ! git merge --no-edit -m "chore: back-merge main into next" origin/main; then
echo "::warning::Conflict during initial back-merge main → next. Pushing the branch anyway so a maintainer can resolve via PR."
# Abort and recreate as an empty branch with a CONFLICT marker — gives the maintainer a PR to work in.
echo "::error::Cannot auto-back-merge main into next: merge conflict. A maintainer must back-merge manually (git checkout next; git merge origin/main; resolve; push)."
git merge --abort || true
git push origin "$BR"
gh pr create \
--base next \
--head "$BR" \
--title "chore: CONFLICT back-merging main → next (manual resolution required)" \
--label automation \
--label backmerge \
--label needs-human \
--body "main moved to ${SHORT_SHA} and cannot be auto-merged into next. Check out this branch locally, resolve the conflict, and push."
exit 0
exit 1
fi
git push origin "$BR"
git push --force origin "$BR"
echo "reused=false" >> "$GITHUB_OUTPUT"
fi
- name: Open or update PR
if: steps.check.outputs.next_exists == 'true' && steps.branch.outputs.reused != 'true'
if: steps.check.outputs.next_exists == 'true'
id: openpr
env:
GH_TOKEN: ${{ secrets.GSD_BOT_PR_TOKEN || secrets.GITHUB_TOKEN }}
BR: ${{ steps.branch.outputs.branch }}
@@ -123,21 +118,27 @@ jobs:
Generated by \`.github/workflows/auto-backmerge.yml\`.
EOF
)
gh pr create \
--base next \
--head "$BR" \
--title "chore: back-merge main → next (${SHORT_SHA})" \
--label automation \
--label backmerge \
--body "$BODY" \
|| echo "::warning::Could not create back-merge PR (may already exist)."
PR=$(gh pr list --base next --head "$BR" --state open --json number --jq '.[0].number // empty' 2>/dev/null || echo "")
if [ -z "$PR" ]; then
PR_URL=$(gh pr create \
--base next \
--head "$BR" \
--title "chore: back-merge main → next (${SHORT_SHA})" \
--body "$BODY")
PR="${PR_URL##*/}"
fi
if [ -z "$PR" ]; then
echo "::error::back-merge PR was not created"
exit 1
fi
gh pr edit "$PR" --add-label automation --add-label backmerge || true
echo "pr=$PR" >> "$GITHUB_OUTPUT"
- name: Enable auto-merge
- name: Admin-merge the back-merge PR
if: steps.check.outputs.next_exists == 'true'
env:
GH_TOKEN: ${{ github.token }}
BR: ${{ steps.branch.outputs.branch }}
GH_TOKEN: ${{ secrets.GSD_BOT_PR_TOKEN || secrets.GITHUB_TOKEN }}
PR: ${{ steps.openpr.outputs.pr }}
run: |
# Squash would lose the merge-commit context; use merge commit.
gh pr merge --auto --merge "$BR" \
|| echo "::warning::Could not enable auto-merge (PR may not exist yet or auto-merge is disabled on repo)."
gh pr merge --admin --merge "$PR"