improvement(ci): scan CodeQL PRs at the promotion boundary, refresh main daily

Merged
WA
2mo ago
+26-2

Summary

CodeQL runs ~3,090 times a month and costs ~$382/mo across both languages. 90 of the last 100 PRs target staging, and every one of those is scanned again — against the exact tree being promoted — when the staging→main PR opens.

  • PR scans restricted to main. This defers the signal to the promotion boundary rather than dropping it. Verified safe: no ruleset or branch protection requires a CodeQL status check (all four rulesets carry only pull_request/deletion/non_fast_forward), and the code-scanning alert view is fed by the push-to-main and scheduled analyses — PR analyses upload against refs/pull/N/merge and only ever surface as PR annotations.
  • Deliberately a branch cut, not an activity-type cut. Dropping synchronize would have cut a similar share, but it scans a PR's first commit and never its final state — backwards, since review fixups land in later pushes.
  • Scheduled scan weekly → daily. Pushes to main are rare, so with PR scans limited to main the alert view leans on the cron more than before. It also reseeds the overlay-base database (see below). Costs a few dollars a month.
  • Documented a trap in codeql-config.yml: adding queries:/packs:/query-filters: trips OverlayDisabledReason.NonDefaultQueries and permanently disables overlay analysis — trading a documented up-to-10x win for a few percent.

Follow-up, not fixed here

The scan is slow for a reason unrelated to scope: the overlay-base database uploads on every push to main and never restores on a PR run. Observed 4/4 misses against a byte-identical cache key:

Looking in Actions cache for overlay-base database with restore key
  codeql-overlay-base-database-1-<hash>-javascript-<version>-
No overlay-base database found in Actions cache

...49 minutes after a run logged Successfully uploaded overlay-base database with a key matching that prefix exactly. Phase timings show extraction is 118s of a 221s run (53%) — precisely what overlay skips. GitHub's cache API holds zero overlay entries, so the multi-GB base is going into the runner provider's transparent cache rather than GitHub's. Fixing that restore is worth roughly half of every PR scan and is tracked separately.

Type of Change

  • Improvement

Testing

actionlint clean. Verified the parsed triggers (pull_request.branches: [main], unchanged types, push: [main], daily cron) and that both matrix languages still expand. bun run lint and all 38 check:audits pass.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)