Skip to content

ci(prek): scope PR runs to the diff on ubuntu-slim, keep main whole-tree - #1317

Merged
potiuk merged 1 commit into
apache:mainfrom
potiuk:ci/prek-scope-pr-runs
Sep 21, 2026
Merged

potiuk merged 1 commit into
apache:mainfrom
potiuk:ci/prek-scope-pr-runs

Conversation

@potiuk

@potiuk potiuk commented Sep 21, 2026

Copy link
Copy Markdown
Member

Summary

  • The prek job now runs in two shapes. On a pull request the hooks see only the PR's own diff (--from-ref / --to-ref) on ubuntu-slim; on a push to main they run --all-files on ubuntu-latest. ci(runners): move every job to the 1-vCPU ubuntu-slim runner #1190's reasoning for slim holds for the PR shape — at a handful of files there is nothing for prek to split across the extra cores, so they buy nothing. The cores only pay on the whole-tree sweep, which is also the run that has to be exhaustive, so main keeps them.
  • The link check is exempt from that scoping and runs whole-repo on both events, in a step of its own. lychee is a whole-repo hook wearing a file filter: pass_filenames: false + a trailing . means it always walked the tree itself, and prek's file list only decides whether it fires (files: \.(md|rst|j2)$). Under --from-ref that gate is a hole — a PR that breaks a link by renaming or deleting a non-doc file matches no .md, the hook never runs, and the break lands on main.
  • Running it unconditionally is nearly free: 29 MiB peak RSS in 0.24s over 10,251 links, because .lychee.toml sets offline = true (no URL is fetched) and the hook is language: rust (no Docker daemon, so slim is fine) off the existing hook-env cache.
  • Docs updated where they described CI as always whole-tree (AGENTS.md, CONTRIBUTING.md). A green PR check is no longer a whole-repo result, which makes the existing "run prek run --all-files before you push" pre-flight load-bearing rather than redundant.

Type of change

  • Skill change (.claude/skills/<name>/) — eval fixtures updated below
  • Tool / bridge contract (tools/<system>/*.md)
  • Python package (tools/*/ with pyproject.toml)
  • Groovy reference impl
  • Cross-cutting (RFC, AGENTS.md, sandbox, privacy-LLM)
  • Documentation (docs/, README.md, CONTRIBUTING.md) — the CI-shape descriptions only
  • CI / dev loop (prek, workflows, validators)

Test plan

  • prek run --all-files passes (exit 0, whole tree)
  • uv run zizmor --config .zizmor.yml .github/workflows/pre-commit.yml — no findings
  • Workflow parses; both steps' resolved shapes checked via PyYAML:
    • runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-slim' || 'ubuntu-latest' }}
    • scoped step: prek run … $PREK_SCOPE --skip workspace-pytest --skip identity --skip skill-token-count --skip lychee
    • lychee step: prek run lychee --color=always --all-files, if: ${{ !cancelled() }}
  • prek run lychee --all-files passes locally (the exact command the new step runs)
  • lychee memory measured with getrusage(RUSAGE_CHILDREN) rather than assumed: 29.3 MiB peak, 0.24s, 🔍 10251 Total 🔗 4338 Unique ✅ 8929 OK 🚫 0 Errors
  • prek run --help confirms --from-ref / --to-ref exist and default --to-ref to HEAD

RFC-AI-0004 compliance

CI-only change — no skill or tool behaviour, no new mutation, no new host access. No row applies.

Linked issues

Refs #1190 (the PR that moved every job to ubuntu-slim; this splits prek so the PR shape joins them).

Notes for reviewers (optional)

  • The fetch-depth ternary is quoted on purpose. ${{ cond && 0 || 1 }} evaluates to 1 on a pull request, because 0 is falsy in GitHub expressions and the idiom falls through to the right operand. That would have left PRs on a shallow clone and broken --from-ref silently. '0' / '1' are strings, and only the empty string is falsy.
  • PREK_SCOPE is built in env:, not interpolated into run:, so no expression is expanded into the step's shell source.
  • !cancelled() rather than always() on the lychee step: a link break and a hook failure in the step above are independent, and a contributor should see both in one run instead of fixing one to discover the other.
  • The OOM mechanism from the typos comment does not apply to lychee, and this PR does not expose it to one. typos hit it because prek hands it the file list and chunks that list across ~cpu_count invocations, so 1 vCPU collapsed ~4.9k paths into one ~5.7 GB process. lychee has pass_filenames: false, so it receives no list and has always been a single whole-repo process regardless of core count. I corrected the typos comment, which said "the 4-vCPU runner the prek workflow now uses" — now true only of the main job, and that hook walks the tree precisely so slim is safe.
  • Known, deliberate residue: lychee is not the only pass_filenames: false whole-repo hook gated by a files: pattern — typos, check-placeholders, symlink-lint, check-doc-sync and skill-and-tool-validate share the shape, so under --from-ref each fires only when a PR happens to touch a matching path. main still catches them. I scoped this PR to lychee because it is the one whose gate is routinely missed by a legitimate PR (a rename); say the word if you would rather have one "whole-repo hooks" step covering the set.
  • If offline is ever turned back off, the 29 MiB figure stops holding — max_concurrency = 14 in-flight responses get buffered and memory becomes a function of response size. That is already coupled to reinstating the .lycheecache step; worth remembering the memory dimension travels with it.
  • Spec-sync pre-check: .last-sync is 2 commits behind main, but this ships no skill, tool or mode change, so it is exempt per AGENTS.md.

🤖 Generated with Claude Code

The prek job now runs in two shapes. On a pull request the hooks see
only the PR's own diff (`--from-ref` / `--to-ref`) on `ubuntu-slim`; on
a push to `main` they run `--all-files` on `ubuntu-latest`. The cores
only pay off on the whole-tree sweep — at a handful of files there is
nothing for prek to split across them.

The link check is exempt from that scoping and runs whole-repo on both
events, in a step of its own. lychee is a whole-repo hook wearing a
file filter: `pass_filenames: false` means prek's file list only
decides *whether it fires*, so under `--from-ref` a PR that breaks a
link by renaming a non-doc file matches no `.md`, the hook never runs,
and the break lands on `main`. It is nearly free to run unconditionally
— measured at 29 MiB peak RSS in 0.24s over 10,251 links, since
`offline = true` fetches no URL and `language: rust` needs no Docker.

Docs updated where they described CI as always whole-tree. A green PR
check is no longer a whole-repo result, which makes the existing
"run `prek run --all-files` before you push" pre-flight load-bearing
rather than redundant.

Generated-by: Claude Opus 5
@potiuk
potiuk merged commit 4b20603 into apache:main Sep 21, 2026
11 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