Skip to content

Commit 14bf830

Browse files
mason: suppress superseded CI cancellations
Co-authored-by: Alfonso <alfonso-magic-context@users.noreply.github.com>
1 parent e288129 commit 14bf830

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

‎scripts/ci-sentinel.sh‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ ERROR_STREAK=0
121121
TAB=$(printf '\t')
122122
RUN_FIELDS=databaseId,workflowName,headBranch,headSha,status,conclusion,createdAt,attempt
123123
RUN_ROW='def nonempty: if . == null or . == "" then "-" else . end; [.databaseId, (.attempt // 1), (.workflowName | nonempty), (.headBranch | nonempty), (.headSha | nonempty), .status, (.conclusion | nonempty), .createdAt] | @tsv'
124+
# GitHub's ISO creation times sort lexically when comparing sibling runs.
125+
is_superseded() {
126+
SUPERSEDED=$(LC_ALL=C awk -F "$TAB" -v workflow="$WORKFLOW" -v branch="$BRANCH" -v created="$CREATED" '$3 == workflow && $4 == branch && $8 > created { found = 1; exit } END { print found ? 1 : 0 }' "$WORK/runs")
127+
}
124128

125129
# Keep API failures distinct from empty results, with a portable wall deadline.
126130
gh_bounded() {
@@ -185,6 +189,7 @@ poll() {
185189
done < "$STATE_DIR/active"
186190
while IFS="$TAB" read -r RUN_ID ATTEMPT WORKFLOW BRANCH SHA STATUS CONCLUSION CREATED; do
187191
[ -n "$RUN_ID" ] || continue
192+
is_superseded
188193
if [ "$STATUS" != completed ]; then
189194
printf '%s\n' "$RUN_ID" >> "$WORK/active"
190195
elif seen "checked:$RUN_ID:$ATTEMPT"; then
@@ -193,6 +198,10 @@ poll() {
193198
if gh_bounded 30 "$WORK/jobs" run view "$RUN_ID" --json jobs -q '.jobs[] | select(.conclusion == "failure" or .conclusion == "cancelled") | [.databaseId, .conclusion, .name] | @tsv'; then
194199
while IFS="$TAB" read -r JOB_ID JOB_CONCLUSION JOB_NAME; do
195200
[ -n "$JOB_ID" ] || continue
201+
# CI (required) aggregates shards, so cancellations in an older run can make it fail.
202+
if [ "$SUPERSEDED" -eq 1 ] && { [ "$JOB_CONCLUSION" = cancelled ] || { [ "$JOB_CONCLUSION" = failure ] && [ "$JOB_NAME" = 'CI (required)' ]; }; }; then
203+
continue
204+
fi
196205
seen "job:$JOB_ID" && continue
197206
remember "job:$JOB_ID"
198207
{
@@ -207,8 +216,10 @@ poll() {
207216
[ "$STATUS" != completed ] || printf '%s\n' "$RUN_ID" >> "$WORK/active"
208217
fi
209218
if [ "$STATUS" = completed ] && { [ "$CONCLUSION" = failure ] || [ "$CONCLUSION" = cancelled ]; } && ! seen "run:$RUN_ID:$ATTEMPT"; then
210-
remember "run:$RUN_ID:$ATTEMPT"
211-
describe_run >> "$WORK/reports"
219+
if [ "$CONCLUSION" != cancelled ] || [ "$SUPERSEDED" -eq 0 ]; then
220+
remember "run:$RUN_ID:$ATTEMPT"
221+
describe_run >> "$WORK/reports"
222+
fi
212223
fi
213224
done < "$WORK/runs"
214225
mv -f "$WORK/active" "$STATE_DIR/active" || die "Cannot persist watched runs"

‎scripts/ci-sentinel.test.sh‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ if [ "$ACTION" = list ]; then
5858
elif [ "$FIELDS" = jobs ]; then
5959
[ "$FAKE_SCENARIO" != jobs-unavailable ] || exit 1
6060
INPUT="$FAKE_DIR/jobs"
61+
[ ! -f "$FAKE_DIR/jobs-$RUN" ] || INPUT="$FAKE_DIR/jobs-$RUN"
6162
else
6263
[ "$RUN" = 101 ] || exit 64
6364
INPUT="$FAKE_DIR/run"
@@ -172,6 +173,42 @@ cancelled() {
172173
expect_rc 0
173174
[ ! -s "$OUT" ] || fail 'duplicate cancelled job'
174175
}
176+
add_newer_sibling() {
177+
jq '.status = "completed" | .conclusion = "cancelled"' "$FAKE_DIR/run" > "$FAKE_DIR/old"
178+
jq '.databaseId = 102 | .createdAt = "2026-10-14T10:01:00Z" | .status = "in_progress" | .conclusion = ""' "$FAKE_DIR/run" > "$FAKE_DIR/new"
179+
jq -s . "$FAKE_DIR/old" "$FAKE_DIR/new" > "$FAKE_DIR/runs"
180+
printf '{"jobs":[]}\n' > "$FAKE_DIR/jobs-102"
181+
}
182+
superseded_cancelled_run() {
183+
add_newer_sibling
184+
printf '{"jobs":[{"databaseId":1003,"name":"cancelled matrix job","conclusion":"cancelled"}]}\n' > "$FAKE_DIR/jobs"
185+
invoke
186+
expect_rc 0
187+
[ ! -s "$OUT" ] || fail 'superseded cancellation was reported'
188+
}
189+
newest_cancelled_run() {
190+
jq '.status = "completed" | .conclusion = "cancelled"' "$FAKE_DIR/run" > "$FAKE_DIR/new"
191+
mv "$FAKE_DIR/new" "$FAKE_DIR/run"
192+
jq -s . "$FAKE_DIR/run" > "$FAKE_DIR/runs"
193+
printf '{"jobs":[{"databaseId":1003,"name":"cancelled matrix job","conclusion":"cancelled"}]}\n' > "$FAKE_DIR/jobs"
194+
invoke
195+
expect_rc 1
196+
contains 'job: cancelled matrix job (cancelled, id: 1003)'
197+
contains 'conclusion: cancelled'
198+
}
199+
failed_job() {
200+
printf '{"jobs":[{"databaseId":1004,"name":"unit tests","conclusion":"failure"}]}\n' > "$FAKE_DIR/jobs"
201+
invoke
202+
expect_rc 1
203+
contains 'job: unit tests (failure, id: 1004)'
204+
}
205+
superseded_gate() {
206+
add_newer_sibling
207+
printf '{"jobs":[{"databaseId":1005,"name":"CI (required)","conclusion":"failure"},{"databaseId":1006,"name":"unit tests","conclusion":"cancelled"}]}\n' > "$FAKE_DIR/jobs"
208+
invoke
209+
expect_rc 0
210+
[ ! -s "$OUT" ] || fail 'superseded gate or cancellation was reported'
211+
}
175212
multiple_jobs() {
176213
printf '{"jobs":[{"databaseId":1001,"name":"one","conclusion":"failure"},{"databaseId":1003,"name":"two","conclusion":"cancelled"},{"databaseId":1005,"name":"three","conclusion":"failure"}]}\n' > "$FAKE_DIR/jobs"
177214
invoke
@@ -375,6 +412,10 @@ PY
375412

376413
run_test 'mid-run failed job is reported once' mid_run
377414
run_test 'cancelled job is reported once' cancelled
415+
run_test 'superseded cancelled run is silent' superseded_cancelled_run
416+
run_test 'newest cancelled run is reported' newest_cancelled_run
417+
run_test 'failed job is still reported' failed_job
418+
run_test 'gate in superseded run is silent' superseded_gate
378419
run_test 'all failed jobs in a poll are reported once' multiple_jobs
379420
run_test 'run completion is a separate report' completion
380421
run_test 'successful run completion is silent' success

0 commit comments

Comments
 (0)