Skip to content

Skip CI jobs whose exact inputs already passed - #143

Merged
NinjaLikesCheez merged 2 commits into
mainfrom
ci/skip-passed-inputs
Oct 19, 2026
Merged

NinjaLikesCheez merged 2 commits into
mainfrom
ci/skip-passed-inputs

Conversation

@NinjaLikesCheez

Copy link
Copy Markdown
Contributor

Why

Pushing only a CHANGELOG entry to a PR re-ran Lint, typecheck, test, build and Docker image builds. dorny/paths-filter compares the whole PR against main, so once a PR touches src/, every later push matches. Jobs that were "skipped" also still started a runner just to echo "Skipping". The Terraform plan jobs started (and could wait on an Environment approval) even when they then did nothing.

What changes

Content-addressed gate instead of path diffs. Each workflow's first job (What changed) hashes the files a job depends on: a list in .github/path-filters.yml, plus the workflow file, the filter file and the gate itself (scripts/ci-inputs.sh, which uses git ls-files -s, so paths, modes and contents all count). It then looks the hash up as a tiny Actions cache marker (.github/actions/ci-inputs). A job runs only if that exact set of files has never passed, and it saves the marker on success (.github/actions/ci-passed).

  • A CHANGELOG-only push, a re-push of the same code, or a rebase that doesn't change a job's inputs → skipped.
  • Docs-only PRs reuse main's markers (PRs can read the base branch's caches), so check and Docker skip.
  • A cancelled or failed run saves nothing, so its inputs run again. This avoids the "push B cancels A, then B's diff vs A looks clean" hole that a "changed since last push" filter would have.
  • Skipped jobs use job-level if:, so no runner starts. GitHub reports them as passed. The Protecc Main ruleset is disabled today, and a skipped job satisfies required checks anyway.

Per workflow

  • CI: check and docker are gated as above. The image list is what the Dockerfile copies, without tests (*.test.ts, src/testing/), so test-only changes don't rebuild. justfile and ci.yml no longer count as image inputs for CD. pull-requests: read is dropped (it was only for dorny).
  • CD: uses match: latest, which publishes when the image inputs differ from the most recent publish. A revert to older code therefore republishes, and a run cancelled by a newer merge leaves the newer one to publish. The marker is saved only after the image and the Sentry maps are done.
  • Docs: gated on the docs list. The paths: trigger now also covers root package.json/lockfile/biome.json, which the site and its Biome lint read. Publishing (workflow_dispatch) always builds.
  • Terraform: the duplicated "did Terraform files change" step moves into one changes job. Plan jobs are skipped at job level, so they never wait on the dev/prod Environment when there's nothing to plan. Validate and plans skip when the terraform inputs already passed.
  • Changelog gate: no more pnpm install (scripts/check-changelog.ts only uses Node built-ins and Node 24 runs the TS itself). just check-changelog now uses node directly.

Guard rails: scripts/ci-inputs.test.ts tests the hasher in a temp repo (exclusions, renames, extra files, unknown list). On the real repo it checks that everything the Dockerfile COPYs is in image, that every filter: a workflow uses exists, and that the paths: triggers of docs.yml and terraform.yml cover their lists.

just ci-inputs <list> prints what a list matches.

Trade-offs / notes

  • Same inputs are assumed to give the same result. A runner image change, or a flaky test that passed once, won't rerun by itself. To force a rerun, delete the ci-passed-… cache (Actions → Caches) and re-run, or change a file. The runbook covers this. Markers are evicted after 7 days unused, and then the job simply runs again.
  • On a merge to main, CI still runs once, because PR markers aren't visible to main. That run seeds the markers future PRs reuse. The Docker smoke on main overlaps with CD's build, but both share the GHA layer cache.
  • Overlaps with Stop CD losing images and gate Release on just check #137, which also edits cd.yml to stop CD losing images by queueing runs and diffing against the last green run. This PR solves the same problem through the latest marker, so the cd.yml half of Stop CD losing images and gate Release on just check #137 becomes unnecessary. Its Release just check gate is unaffected.
  • Docs and pipeline diagrams are updated in docs/architecture.md, docs/runbook.md and docs/infra.md. CHANGELOG entry is under Changed.

Verified

  • just check passes (includes the new tests). just docs-build passes.
  • actionlint is clean apart from a pre-existing SC2129 style note in release.yml, which this PR doesn't touch.
  • Not yet verified on GitHub: the first run on this PR will show the gate. A follow-up CHANGELOG-only push should then skip check/Docker.

🤖 Generated with Claude Code

Comment thread .github/path-filters.yml
NinjaLikesCheez and others added 2 commits October 19, 2026 09:48
Path filters compared the whole PR against main, so every push (even one
that only adds a CHANGELOG entry) re-ran just check and the Docker build,
and skipped jobs still started a runner. Each workflow now hashes the files
a job depends on (.github/path-filters.yml plus the workflow) and skips the
job when a cache marker says that exact set already passed. CD publishes
when the image inputs differ from the last publish. Terraform plans that
aren't needed never start. The changelog gate drops pnpm install.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/ci-inputs.test.ts reads the Dockerfile and the cd, docs and
terraform workflows, so a PR touching only those skipped the tests that
guard them. Add them to the check list, with a test that keeps it so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@NinjaLikesCheez
NinjaLikesCheez merged commit 931f5aa into main Oct 19, 2026
13 checks passed
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