fix(labels): a failed rollup read must not read as "nothing is failing"
gh returning nothing left the rollup KEY absent, which collapsed into the same NONE as a PR that genuinely has no checks -- so an API hiccup presented as mergeable-by-a-human. That is the unknown-certified-as-green shape this machine exists to stop, surviving in the one place the #136 fix did not look. checks_state now reports UNREADABLE for an absent key, and the sweep leaves that PR exactly as it is rather than recomputing on facts it did not read. Blocking on it instead would flap the whole board on one bad call. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2d49a5ad42
commit
9bf669dabc
2 changed files with 27 additions and 2 deletions
22
.github/scripts/labels-reconcile.sh
vendored
22
.github/scripts/labels-reconcile.sh
vendored
|
|
@ -56,7 +56,14 @@ run() { # every mutation goes through here — DRY_RUN=1 logs instead of doing
|
|||
|
||||
requested() { grep -qxF "$1" <<<"$REQUESTED"; }
|
||||
|
||||
checks_state() { # rollup JSON on stdin → SUCCESS | FAILURE | PENDING | NONE
|
||||
checks_state() { # rollup JSON on stdin → SUCCESS | FAILURE | PENDING | NONE | UNREADABLE
|
||||
# UNREADABLE is the absence of the key itself, which is what a failed fetch
|
||||
# leaves behind — distinct from a present-but-empty rollup, which honestly
|
||||
# means this PR has no checks. Collapsing the two let an API hiccup present
|
||||
# as "nothing is failing", i.e. as mergeable-by-a-human: the same
|
||||
# unknown-certified-as-green shape as the bug this machine exists to stop.
|
||||
# The caller skips the PR entirely rather than labelling on facts it did not
|
||||
# read; blocking on it instead would flap the whole board on one bad call.
|
||||
# The rollup mixes two node types with two different closed enums: CheckRun
|
||||
# carries `conclusion` (CheckConclusionState), StatusContext carries `state`
|
||||
# (StatusState). Rather than list the outcomes that block — the version that
|
||||
|
|
@ -70,6 +77,8 @@ checks_state() { # rollup JSON on stdin → SUCCESS | FAILURE | PENDING | NONE
|
|||
# not in consequence: a false FAILURE parks the PR on the agent, who looks;
|
||||
# a false SUCCESS invites a human to merge a tree that will not merge.
|
||||
jq -r '
|
||||
if (has("statusCheckRollup") | not) then "UNREADABLE" else
|
||||
|
||||
# NEUTRAL and SKIPPED satisfy branch protection — a skipped required check
|
||||
# is not a failed one, and path-filtered jobs skip constantly here.
|
||||
["SUCCESS", "NEUTRAL", "SKIPPED"] as $passing
|
||||
|
|
@ -121,7 +130,9 @@ checks_state() { # rollup JSON on stdin → SUCCESS | FAILURE | PENDING | NONE
|
|||
| if ($latest | length) == 0 then "NONE"
|
||||
elif (($latest - $passing - $waiting) | length) > 0 then "FAILURE"
|
||||
elif (($latest - $passing) | length) > 0 then "PENDING"
|
||||
else "SUCCESS" end'
|
||||
else "SUCCESS" end
|
||||
|
||||
end'
|
||||
}
|
||||
|
||||
bot_verdict() { # $1 = login → MISSING | BLOCK | APPROVE | STALE | FEEDBACK
|
||||
|
|
@ -414,6 +425,13 @@ main() {
|
|||
GH_VIEW="$(gh pr view "$n" -R "$REPO" --json mergeable,statusCheckRollup 2>/dev/null || echo '{}')"
|
||||
MERGEABLE="$(jq -r '.mergeable // "UNKNOWN"' <<<"$GH_VIEW")"
|
||||
CHECKS="$(checks_state <<<"$GH_VIEW")"
|
||||
# Read failed: leave this PR exactly as it is. Recomputing on facts we
|
||||
# did not read is how an API hiccup turns into a false "merge me" —
|
||||
# and the next tick is 15 minutes away, not 15 hours.
|
||||
if [ "$CHECKS" = UNREADABLE ]; then
|
||||
log "#$n: could not read mergeability/checks — left alone this pass"
|
||||
exit 0
|
||||
fi
|
||||
reconcile_pr "$n"
|
||||
) || log "#$n: reconcile failed — continuing with the remaining PRs"
|
||||
done
|
||||
|
|
|
|||
|
|
@ -258,6 +258,13 @@ ctx_() { jq -n --arg n "$1" --arg s "$2" --arg t "${3:-2026-07-20T15:00:00Z}" \
|
|||
'{__typename:"StatusContext", context:$n, state:$s, createdAt:$t}'; }
|
||||
|
||||
expect "no checks at all is NONE" NONE "$(rollup '[]' | checks_state)"
|
||||
# A failed fetch leaves no rollup KEY; a PR with no checks leaves an empty
|
||||
# ARRAY. Collapsing the two let an API hiccup read as "nothing is failing" —
|
||||
# the same unknown-certified-as-green shape as #136, in the one place that
|
||||
# fix did not look. The caller skips an UNREADABLE PR rather than relabelling.
|
||||
expect "a failed read is UNREADABLE, not NONE" UNREADABLE "$(echo '{}' | checks_state)"
|
||||
expect "...and a real empty rollup is still NONE" NONE \
|
||||
"$(echo '{"mergeable":"MERGEABLE","statusCheckRollup":[]}' | checks_state)"
|
||||
expect "all green is SUCCESS" SUCCESS \
|
||||
"$(rollup "[$(run_ a SUCCESS),$(run_ b SUCCESS)]" | checks_state)"
|
||||
expect "a queued run is PENDING" PENDING \
|
||||
|
|
|
|||
Loading…
Reference in a new issue