Skip to content

ci: make every check report, and scan the dependencies - #69

Merged
fadion merged 1 commit into
masterfrom
ci/required-checks-and-scanning
Aug 27, 2026
Merged

fadion merged 1 commit into
masterfrom
ci/required-checks-and-scanning

Conversation

@fadion

@fadion fadion commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Protecting master turned this up. A required status check waits on a check run, and a workflow that a path filter stops from triggering produces no check run at all — so the moment these checks are required, a docs-only PR waits forever on six checks that never arrive. The paths-ignore added in #57 would have made master unmergeable for exactly the changes it was meant to speed up.

A job skipped by an if does report, as "skipped", and that satisfies the requirement.

What changed

  • Moved the filtering off the triggers and into the jobs. The workflow always runs; a changes job decides whether the rest is worth it; the four heavy jobs gate on its answer.
  • changes asks the API which files the PR touches rather than checking out and diffing — no clone, no fetch-depth to get wrong, no third-party action in the supply chain for something this small.
  • A push to master always runs everything. It is the check after the merge, it is rare, and making it conditional only adds a way to be wrong.
  • Added govulncheck to the lint job, unpinned where golangci-lint is pinned: a new linter check turning a branch red says nothing about this repo, while a new vulnerability is a fact about it that nobody knew yesterday. It reports only what this code can reach, so the 3 vulnerabilities currently in imported packages and 6 in required modules do not fail the build — something reachable would.
  • Added dependabot.yml, monthly, for Go modules and actions.

The filter is written as "is there a file that is not ignorable", so an unfamiliar path counts as code and the jobs run. Checked against docs-only, README-only, examples-only, mixed, an empty list, and the lookalikes CLAUDE.md.bak and docsy/ — both correctly run everything.

Before turning on required checks, land a docs-only PR and confirm the four jobs report as skipped rather than pending. This PR touches a workflow, so it runs everything and cannot exercise that path itself.

Protecting master is what turned this up. A required status check waits on
a check run, and a workflow that a path filter stops from triggering
produces no check run at all -- so the moment these checks are required, a
docs-only pull request waits forever on six checks that will never arrive.
The paths-ignore added in #57 to keep documentation off the matrix would
have made master unmergeable for exactly the changes it was meant to
speed up.

A job skipped by an `if` does report, as "skipped", and that satisfies the
requirement. So the filtering moves off the triggers and into the jobs: the
workflow always runs, a `changes` job decides whether the rest is worth it,
and the four heavy jobs gate on its answer.

`changes` asks the API which files the pull request touches rather than
checking out and diffing, so there is no clone, no fetch-depth to get
wrong, and no third-party action in the supply chain for something this
small. A push to master always runs everything: it is the check after the
merge, it is rare, and making it conditional only adds a way to be wrong.

The test is written as "is there a file that is not ignorable" rather than
the inverse, so an unfamiliar path counts as code and the jobs run.
Checked against docs-only, README-only, examples-only, mixed, an empty
list, and the two lookalikes -- CLAUDE.md.bak and docsy/ -- both of which
correctly run everything.

govulncheck joins the lint job, unpinned where golangci-lint is pinned.
The reason to pin a linter is that a new check turning a branch red says
nothing about this repo; a new vulnerability is the opposite, a fact about
this repo nobody knew yesterday. It reports only what this code can
actually reach, so the three vulnerabilities currently sitting in imported
packages and the six in required modules do not fail the build, and
something reachable would.

Dependabot opens a pull request monthly for the Go modules and the actions.
Everything here is pinned on purpose, which is right and is also how a repo
sits on a three-year-old toolchain without noticing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@fadion
fadion merged commit 0c14613 into master Aug 27, 2026
7 checks passed
@fadion
fadion deleted the ci/required-checks-and-scanning branch August 27, 2026 20:37
fadion added a commit that referenced this pull request Aug 27, 2026
Requiring the seven job names deadlocked the first documentation-only pull
request, and not for the reason #69 anticipated.

Skipping works: lint, language and fuzz all reported "skipping" on #71,
which is a conclusion and satisfies a required check. But a skipped MATRIX
job reports its name unexpanded -- literally "test (${{ matrix.os }})"
rather than the three real ones -- so "test (ubuntu-latest)" and its two
siblings never arrived at all, and the pull request sat BLOCKED on checks
that cannot exist. Exactly the deadlock #69 set out to prevent, one level
down.

Requiring individual job names has a second problem worth fixing while
here: every required name is coupled to a job name, so renaming a job
silently stops protection enforcing it, with nothing anywhere to notice.

So: a `ci` job that always runs, gates on the other five, and fails if any
of them failed or was cancelled. Skipped is fine, since that is the
filtering working as intended. It always reports because it always runs,
whatever the jobs beneath it did.

Branch protection should require this one check and nothing else. Adding a
job later then needs no change to the ruleset, and renaming one cannot
quietly disable it.

The comparison pads both sides -- `case " $RESULTS " in *" failure "*` --
so a status that merely contains another as a substring cannot match by
accident. Checked against all-success, all-skipped, a failure, a
cancellation, and a failure among skips.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
fadion added a commit that referenced this pull request Aug 27, 2026
A block was added in #66 to show that boolean operators coerce their
operands, and the count in compatibility.md was the one place not updated
with it.

Also the first change confined to docs/, which is what proves the CI
restructure in #69 does what it claims: the four heavy jobs should report
skipped rather than sitting pending, which is the difference between a
required check that is satisfied and one that never arrives.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fadion added a commit that referenced this pull request Aug 27, 2026
A block was added in #66 to show that boolean operators coerce their
operands, and the count in compatibility.md was the one place not updated
with it.

Also the first change confined to docs/, which is what proves the CI
restructure in #69 does what it claims: the four heavy jobs should report
skipped rather than sitting pending, which is the difference between a
required check that is satisfied and one that never arrives.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant