Skip to content

ci: main health watch -- one alarm issue while main is red, and test heads CI never ran on - #3355

Merged
Xore merged 1 commit into
mainfrom
ci/3324-main-health-watch
Sep 26, 2026
Merged

Xore merged 1 commit into
mainfrom
ci/3324-main-health-watch

Conversation

@Xore

@Xore Xore commented Sep 26, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #3324, and closes a gap the investigation turned up. The 2026-09-25 incident wasn't "main red for 7 hours". No CI ran on those five Dependabot merges at all. Merges made by github-actions (auto-merge with GITHUB_TOKEN) start no push workflows, so main was broken and silent until #3300's push. Every future Dependabot auto-merge leaves main untested the same way.

scripts/main-health-watch.py + main-health-watch.yml (after every Quality/Containers run on main, and hourly):

  • Red: one main-red-alarm issue with the failing jobs, the first-red and last-green commits, and the suspect commits between them. It comments again only when the failing head changes, and closes itself when both workflows are green. Cancelled runs are ignored.
  • Untested: when main's head is over an hour old and a watched workflow never ran on it, the watch dispatches that workflow on main. workflow_dispatch is the exception GITHUB_TOKEN may start. The next sweep judges the result like any push run.
  • Replay: --before ISO8601 shows any past moment, read-only.

The workflow has empty default permissions; the job gets issues: write + actions: write (to dispatch) + contents: read. It runs on a GitHub-hosted runner with a SHA-pinned checkout and no persisted credentials.

Validation

  • Replays against the real repo:
    • 2026-09-25 10:00Z → untested head 87df7e145d (chore(deps): bump sha2 (#3289)), would dispatch both workflows.
    • 06:40Z → inside the one-hour grace period.
    • now → green.
  • scripts/tests/test_main_health_watch.py: 8 tests against a fake gh covering open, dedupe on the same head, append on a new head, close on green, ignored cancelled runs, dispatch, the grace period, and dispatched runs counting.
  • actionlint clean; check-doc-paths-exist.py passes.

Security impact

  • No real credentials, private addresses, payloads, PCAPs, keys, or .env files were added.
  • Sandbox/network-isolation implications were reviewed. (CI only.)
  • Publicly exposed ports and routes are unchanged.

…heads CI never ran on

Two failure modes, one of which the 2026-09-25 incident turns out to have
been. A red push run on main is read by nobody; and a merge made by
github-actions (dependabot auto-merge) starts no push run at all, because
GITHUB_TOKEN events trigger no workflows -- the five broken dependabot
merges that day produced no red run, only silence, until #3300's push.

scripts/main-health-watch.py, run after every Quality/Containers run on
main and hourly:
- red: one main-red-alarm issue with the failing jobs, first red / last
  green commit and the commits between; a new comment only when the failing
  head changes; closed automatically when both workflows are green again.
  Cancelled runs are ignored.
- untested: main's head older than an hour with no run of a watched
  workflow gets that workflow dispatched on main (workflow_dispatch is the
  event GITHUB_TOKEN may start); dispatched runs are judged like push runs.
- --before ISO8601 replays any past moment read-only. Replaying 2026-09-25
  10:00Z flags 87df7e1 (the sha2 bump) as untested; 06:40Z is inside the
  grace hour; now is green.

8 tests against a fake gh cover open, dedupe, append, close, cancelled
runs, dispatch, the grace period and dispatched runs counting.

Closes #3324
@github-actions

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

OpenSSF Scorecard

PackageVersionScoreDetails
actions/actions/checkout 3d3c42e5aac5ba805825da76410c181273ba90b1 🟢 6.6
Details
CheckScoreReason
Maintained🟢 79 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 7
Code-Review🟢 10all changesets reviewed
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Pinned-Dependencies🟢 3dependency not pinned by hash detected -- score normalized to 3
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Packaging⚠️ -1packaging workflow not detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
SAST🟢 10SAST tool is run on all commits
Branch-Protection🟢 5branch protection is not maximal on development and all release branches

Scanned Files

  • .github/workflows/main-health-watch.yml

@Xore
Xore merged commit 510c5f1 into main Sep 26, 2026
129 of 130 checks passed
@Xore
Xore deleted the ci/3324-main-health-watch branch September 26, 2026 16:32
Xore added a commit that referenced this pull request Sep 26, 2026
#3355 added main-health-watch.yml, which uses workflow_run. That is the
first file in the tree to trip zizmor's dangerous-triggers rule, a finding
class the ADVISORY set in the #3314 gate predates, so the new gate blocked
every PR on it.

The workflows concerned are already hardened in the ways that rule is
pointing at: every action SHA-pinned, permissions: {} at the workflow
default with job-scoped grants, persist-credentials: false, and no
untrusted event input consumed. #3313 owns the remaining trigger
hardening. Reporting without blocking matches how the other four rules
this gate already defers are handled, and keeps a documented ADVISORY
entry from silently becoming a hard gate on unrelated work.

The rule is still counted and printed, so it stays visible; any rule
outside the set still fails the build.
Xore added a commit that referenced this pull request Sep 26, 2026
…tion and eight truncated names (#3358)

* ci: run actionlint and zizmor on every workflow; fix a template injection and eight truncated names

.github/actionlint.yaml existed but nothing ran actionlint, and nothing
audited the workflows for injection or credential issues.

- "Workflow lint (actionlint, #3314)" row, blocking: pinned 1.7.7 with
  upstream's checksum, SHELLCHECK_OPTS=-S warning to match the repo's
  high-severity ShellCheck policy (all 8 current findings are info). Also
  fails on an unquoted `name:` containing " #": YAML reads the rest as a
  comment, and eight names were shipping truncated ("Sandbox shell tests
  (#2268,") -- all eight now quoted.
- "Workflow security audit (zizmor, #3314)" row: pinned 1.30.1 (no upstream
  checksum file; pinned on first download). Fails on every medium+ finding
  except the four rules #3313's hardening owns (unpinned-uses,
  excessive-permissions, artipacked, self-repository), which are reported
  until then.
- Fixed now: template injection in disk-usage-watch.yml (dispatch input
  interpolated into run: -> env). Justified suppression for the dependabot
  bot-condition (github.actor is ANDed with the unspoofable PR author).

Measured before: zizmor 93 medium+ findings, 77 of them #3313's scope;
after: 0 blocking. Both rows executed exactly as CI runs them: exit 0; a
probe workflow with an unquoted "(x, #1)" name fails the lint row.

Closes #3314

* ci: treat zizmor dangerous-triggers as advisory until #3313 lands

#3355 added main-health-watch.yml, which uses workflow_run. That is the
first file in the tree to trip zizmor's dangerous-triggers rule, a finding
class the ADVISORY set in the #3314 gate predates, so the new gate blocked
every PR on it.

The workflows concerned are already hardened in the ways that rule is
pointing at: every action SHA-pinned, permissions: {} at the workflow
default with job-scoped grants, persist-credentials: false, and no
untrusted event input consumed. #3313 owns the remaining trigger
hardening. Reporting without blocking matches how the other four rules
this gate already defers are handled, and keeps a documented ADVISORY
entry from silently becoming a hard gate on unrelated work.

The rule is still counted and printed, so it stays visible; any rule
outside the set still fails the build.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request ops Deployment, runners, observability, host access

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: scheduled main-health sweep that opens and closes a single 'main is red' issue

1 participant