Alert on nightly bench failures and on nights that never run - #234
Alert on nightly bench failures and on nights that never run#234danielrmerskine wants to merge 1 commit into
Conversation
953cfab to
669c3b3
Compare
|
Live validation ran before merge and caught a real defect, now fixed in the amended commit:
Test artifacts #232/#233 are closed and unpinned. Full cycle re-validated end to end: |
A red nightly previously notified no one -- one 2026-08 outage ran eight consecutive red nights before a human noticed -- and a dead self-hosted runner leaves runs queued forever with nothing failing at all. Two additions close both blind spots: nightly-bench.yml gains two HOSTED notify jobs (the bench being down is exactly the condition they must survive). notify (failure) fires when either child is not success -- != 'success' rather than failure(), so integration SKIPPED behind a failed lifecycle also alerts -- and upserts a single issue labeled bench-alert. notify (recovery) closes it on the next fully green night, so the issue cannot decay into an always-open banner nobody reads. A cancelled run does not alert: cancellation has a human attached. bench-watchdog.yml (cron every 6h at :41) covers the night that never starts: a nightly run queued over 3h (runner offline), running over 5h (past every job timeout), or no scheduled run created in 26h (cron dead). It reads run history with the default token -- the runner-status API needs administration:read, which GITHUB_TOKEN cannot hold -- and only ever adds to the issue; closing is the nightly's call, because "runs are flowing" is not the same claim as "the bench is healthy". Both writers share tools/bench_alert.sh (issue upsert by label, pin on create, close on recover). The body travels as a file, not a string: gh --body-file sidesteps shell quoting, and a heredoc inside $() with an apostrophe does not even parse under macOS's bash 3.2. Live validation found that GitHub's issue list endpoints -- both the search-backed `gh issue list` and the REST issues endpoint -- are eventually consistent: an issue created 1-2s earlier is invisible to each, and each filed a duplicate when probed back-to-back. No read-side query beats that, so the guards are temporal and structural: alert re-checks once after a grace sleep before creating (covers the measured seconds-scale window; production writers are minutes to hours apart); recover closes EVERY open bench-alert issue in two passes with the same grace between them, so a duplicate that still slips through heals on the next green night; and issues are unpinned before closing, because GitHub keeps closed issues pinned and pins cap at 3 per repo -- leaked pins would eventually break pinning entirely. The bench-alert label must exist; a missing label fails the notify job loudly rather than silently skipping the alert.
|
Correction to the previous comment: it described the REST-endpoint switch as re-validated, The fix that is actually in the amended commit: no read-side query beats seconds-scale
Both observed failure modes are now covered by offline behavior tests against a mocked |
669c3b3 to
6c5d93c
Compare
A failing nightly currently notifies no one -- one 2026-08 outage ran eight consecutive red
nights before anyone noticed -- and a dead runner is worse: runs queue forever and nothing
fails at all. This adds both halves of the fix.
nightly-bench.yml: two hosted notify jobs.notify (failure)fires when either child is notsuccess. The condition is!= 'success', notfailure(): when lifecycle fails, integration is SKIPPED, and askipped instrument sweep is exactly the half-covered night that must alert. Cancelled
runs do not alert (a cancellation has a human attached). It files -- or comments on --
ONE open issue labeled
bench-alert, pinned on creation.notify (recovery)closes that issue on the next fully green night, so the alert issuecannot rot into an always-open banner people learn to ignore.
ubuntu-latestdeliberately: the bench being down is the exact conditionthey must survive. Permissions are per-job (
issues: write), so the called benchworkflows keep their default token.
bench-watchdog.yml(new, cron every 6h at :41): the night that never runs.Alerts on: a nightly run QUEUED over 3h (runner offline), RUNNING over 5h (past every job
timeout), or no scheduled run created in 26h (cron disabled/dead). It reads run history
with the default token -- the runner-status API needs
administration:read, whichGITHUB_TOKENcannot carry -- and only ever adds to the issue; closing stays thenightly's call.
Shared plumbing:
tools/bench_alert.sh-- label-keyed issue upsert (comment if open,create+pin if not, close on recover). Bodies travel as files (
gh --body-file), whichsidesteps shell quoting entirely.
Known, documented gaps: direct
workflow_dispatchof the child bench workflows bypassesnotification (a dispatch has a human watching); the watchdog is itself cron-driven, so a
60-day-inactive repo would lose both it and the nightly together.
Verified locally: YAML parses;
bash -non every embedded run script; the watchdog'sdetection logic executed against this repo's real run history (healthy path, 20 runs);
bench_alert.shis shellcheck-clean at-S warningand its no-open-issue query pathreturns empty/exit-0. Live create/comment/pin/close cycle to be validated with a manual
script run before merge (needs the
bench-alertlabel created first).Follow-up (deliberately not here): widening static-checks' shellcheck scope from
test/to include
tools/-- belongs with the other static-checks changes.