Skip to content

Commit 9245686

Browse files
ci(auto-update): wait for state to settle and surface real errors (#5958)
## Summary First run of \`auto-update-pr-branches.yml\` after #5957 found 0 BEHIND PRs even though three were stuck behind main (#5916, #5870, #5902). Two issues: 1. **Timing.** The workflow runs ~4s after the push to main, but GitHub recomputes \`mergeStateStatus\` and the cached PR head SHA asynchronously. Right after the push the field is still UNKNOWN and the cached head can be stale → \`update-branch\` returns *expected head sha didn't match current head ref*. Add a 30s sleep at the start. 2. **Over-strict filter.** The script only iterated PRs where \`mergeStateStatus == "BEHIND"\`, skipping UNKNOWN candidates — exactly the ones we wanted to fix. Drop the filter: after a push to main, every open auto-merge PR is behind, and \`update-branch\` is a no-op when the head is already up-to-date. Also: - Bump permissions to \`contents: write\` (update-branch creates a merge commit on the head ref). - Drop \`--silent\` and capture stderr so the actual GitHub error lands in the log. Verified manually: calling \`PUT /pulls/{num}/update-branch\` from the CLI on #5916 and #5870 worked and they auto-merged within seconds. The 422 on #5902 was a real history-divergence conflict (4 ahead / 58 behind / merge_base differs) — separate problem. ## Test plan - [ ] After this merges, push something to main and confirm the workflow finds N>0 PRs (where N is open auto-merge PRs). - [ ] Confirm any genuinely stuck PR (conflict) gets a clear error in the log instead of \`likely conflict or stale ref\`. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 85d48bf commit 9245686

1 file changed

Lines changed: 29 additions & 20 deletions

File tree

.github/workflows/auto-update-pr-branches.yml

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ on:
2222
workflow_dispatch:
2323

2424
permissions:
25-
contents: read # required for actions/checkout (not strictly needed here, but cheap)
25+
contents: write # update-branch creates a merge commit on the head ref
2626
pull-requests: write # required to call PUT /pulls/:num/update-branch
2727

2828
concurrency:
@@ -33,50 +33,59 @@ jobs:
3333
update:
3434
runs-on: ubuntu-latest
3535
steps:
36-
- name: Update behind PRs with auto-merge enabled
36+
- name: Wait for PR mergeable state to settle
37+
# GitHub recomputes `mergeStateStatus` asynchronously after a
38+
# push to main. If we list PRs immediately the field can still
39+
# be "UNKNOWN" — and the cached head SHA on the PR can lag
40+
# behind the actual ref, so update-branch returns "expected
41+
# head sha didn't match current head ref." Give GitHub a
42+
# moment to settle.
43+
run: sleep 30
44+
45+
- name: Update PRs with auto-merge enabled
3746
env:
3847
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3948
GH_REPO: ${{ github.repository }}
4049
run: |
4150
set -eo pipefail
4251
43-
# Pull every open PR targeting main that has auto-merge enabled.
44-
# `mergeStateStatus` is the field that surfaces "BEHIND" — the
45-
# exact state we want to fix. Skip everything else (CLEAN,
46-
# BLOCKED for missing reviews, DIRTY for conflicts, etc.).
52+
# After a push to main, every open PR with auto-merge is by
53+
# definition behind. Don't filter on `mergeStateStatus`:
54+
# right after the push it can still be UNKNOWN, and we'd
55+
# skip valid candidates. Just iterate every open
56+
# auto-merge PR — update-branch is a no-op when the head
57+
# is already up-to-date.
4758
PRS=$(gh pr list \
4859
--repo "$GH_REPO" \
4960
--state open \
5061
--base main \
5162
--limit 200 \
52-
--json number,title,headRefName,mergeStateStatus,autoMergeRequest)
63+
--json number,title,headRefName,autoMergeRequest)
5364
54-
BEHIND=$(echo "$PRS" | jq -c '
55-
[ .[] | select(.autoMergeRequest != null and .mergeStateStatus == "BEHIND") ]
65+
CANDIDATES=$(echo "$PRS" | jq -c '
66+
[ .[] | select(.autoMergeRequest != null) ]
5667
')
5768
58-
COUNT=$(echo "$BEHIND" | jq 'length')
59-
echo "::notice::Found $COUNT PR(s) BEHIND with auto-merge enabled"
69+
COUNT=$(echo "$CANDIDATES" | jq 'length')
70+
echo "::notice::Found $COUNT open PR(s) with auto-merge enabled"
6071
6172
if [[ "$COUNT" -eq 0 ]]; then
6273
exit 0
6374
fi
6475
65-
echo "$BEHIND" | jq -c '.[]' | while read -r pr; do
76+
echo "$CANDIDATES" | jq -c '.[]' | while read -r pr; do
6677
NUM=$(echo "$pr" | jq -r '.number')
6778
TITLE=$(echo "$pr" | jq -r '.title')
6879
BRANCH=$(echo "$pr" | jq -r '.headRefName')
6980
echo "::notice::Updating PR #${NUM} (${BRANCH}): ${TITLE}"
7081
71-
# update-branch merges the base ref into the head ref; this
72-
# is what the "Update branch" button does in the UI.
73-
# Failures here (e.g. merge conflict, branch deleted) are
74-
# logged as a warning but don't fail the whole workflow —
82+
# Capture stderr so the actual GitHub error (conflict,
83+
# SHA mismatch, etc.) lands in the workflow log instead
84+
# of being swallowed. Failures here don't fail the job —
7585
# one stuck PR shouldn't block the others.
76-
if ! gh api -X PUT \
86+
if ! OUT=$(gh api -X PUT \
7787
"repos/${GH_REPO}/pulls/${NUM}/update-branch" \
78-
-H "Accept: application/vnd.github+json" \
79-
--silent 2>&1; then
80-
echo "::warning::Could not update PR #${NUM} (likely conflict or stale ref)"
88+
-H "Accept: application/vnd.github+json" 2>&1); then
89+
echo "::warning::Could not update PR #${NUM}: ${OUT}"
8190
fi
8291
done

0 commit comments

Comments
 (0)