Skip to content

Commit 296e733

Browse files
committed
fix(ci): a red base must not trap the PR that repairs it
1 parent 3cb98fe commit 296e733

1 file changed

Lines changed: 49 additions & 3 deletions

File tree

scripts/ci/auto-merge-sweep.sh

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ echo "[auto-merge] sweeping open PRs against ${BASE_BRANCH} in ${REPO}"
6464
# batching this script exists to prevent.
6565
base_sha=$(gh api "repos/${REPO}/commits/${BASE_BRANCH}" --jq '.sha')
6666
base_ci=$(gh run list --repo "$REPO" --workflow "$CI_WORKFLOW" --branch "$BASE_BRANCH" --limit 1 \
67-
--json status,conclusion,headSha --jq '.[0] // empty')
67+
--json databaseId,status,conclusion,headSha --jq '.[0] // empty')
6868

6969
if [ -z "$base_ci" ]; then
7070
echo "[auto-merge] no CI history for ${BASE_BRANCH} — proceeding"
@@ -81,9 +81,32 @@ else
8181
echo "[auto-merge] ${BASE_BRANCH} CI is still running — deferring to the next sweep"
8282
exit 0
8383
fi
84+
# A red base must not become a trap for the PR that repairs it.
85+
#
86+
# "Never merge onto red" is right for an unrelated change: it stops a broken
87+
# base quietly collecting more of them and getting harder to diagnose. But
88+
# when the PR *is* the repair, the same rule deadlocks the repo — the fix
89+
# cannot travel the path its own redness blocks, and only a human can move
90+
# it. Seen in maonakamoto/aoz-housing on 2026-08-07: E2E red on the base, the
91+
# fix sitting green in a PR, every sweep refusing politely.
92+
#
93+
# So identify WHICH jobs are red and let a PR through only if its own checks
94+
# pass every one of them. Not a weakening: a PR's checks run on the MERGE
95+
# result (refs/pull/N/merge), so green-on-those-jobs is direct evidence the
96+
# post-merge base is better than the pre-merge base. Still refused: a PR that
97+
# does not cover the failing jobs, one that covers only some of them, and a
98+
# base failure whose jobs cannot be identified at all.
99+
base_red_jobs=""
84100
if [ "$base_conclusion" != "success" ]; then
85-
echo "[auto-merge] ${BASE_BRANCH} CI is ${base_conclusion} — refusing to merge onto a broken base" >&2
86-
exit 0
101+
base_run_id=$(printf '%s' "${base_ci}" | jq -r '.databaseId')
102+
base_red_jobs=$(gh run view "${base_run_id}" --repo "$REPO" --json jobs \
103+
--jq '[.jobs[] | select(.conclusion == "failure") | .name] | .[]' 2>/dev/null || true)
104+
if [ -z "${base_red_jobs}" ]; then
105+
echo "[auto-merge] ${BASE_BRANCH} CI is ${base_conclusion} and no failing job could be identified — refusing to merge onto a broken base" >&2
106+
exit 0
107+
fi
108+
echo "[auto-merge] ${BASE_BRANCH} CI is ${base_conclusion} — failing: $(printf '%s' "${base_red_jobs}" | tr '\n' ' ')" >&2
109+
echo "[auto-merge] only a PR that is green on those exact jobs may merge (its checks run on the merge result)"
87110
fi
88111
fi
89112

@@ -222,6 +245,29 @@ for number in $(printf '%s' "$prs_json" | jq -r 'sort_by(.number) | .[].number')
222245
break
223246
fi
224247

248+
# Red base: this PR merges only if it proves every failing job green.
249+
if [ -n "${base_red_jobs}" ]; then
250+
pr_green=$(printf '%s' "$pr" | jq -r '
251+
[ .statusCheckRollup[]?
252+
| select(((.conclusion // .state // "") | test("^(SUCCESS|NEUTRAL|SKIPPED)$")))
253+
| (.name // .context) ] | .[]')
254+
uncovered=""
255+
while IFS= read -r job; do
256+
[ -z "$job" ] && continue
257+
printf '%s\n' "$pr_green" | grep -Fxq "$job" || uncovered="${uncovered}${job}; "
258+
done <<INNER_EOF
259+
${base_red_jobs}
260+
INNER_EOF
261+
if [ -n "$uncovered" ]; then
262+
echo "[auto-merge] #${number} skip: ${BASE_BRANCH} is red on [${uncovered%; }] and this PR does not prove those green — ${title}"
263+
continue
264+
fi
265+
echo "[auto-merge] #${number} is green on every job ${BASE_BRANCH} fails — merging it to repair the base: ${title}" >&2
266+
if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
267+
echo "- 🔧 #${number} merged onto a red \`${BASE_BRANCH}\` because it passes every failing job — ${title}" >> "$GITHUB_STEP_SUMMARY"
268+
fi
269+
fi
270+
225271
echo "[auto-merge] #${number} green and ready — merging: ${title}"
226272
if gh pr merge "$number" --repo "$REPO" --squash --delete-branch; then
227273
merged_any=1

0 commit comments

Comments
 (0)