Skip to content

ci: gate coverage and complexity (tarpaulin + cargo-crap, pinned) - #24

Merged
bkataru merged 3 commits into
mainfrom
ci/coverage-crap-gates
Oct 14, 2026
Merged

bkataru merged 3 commits into
mainfrom
ci/coverage-crap-gates

Conversation

@bkataru

@bkataru bkataru commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Gates added

Converts the existing upload-only coverage job (cargo llvm-cov + Codecov with fail_ci_if_error: false) into a job that actually gates, running two gates off ONE instrumented test run:

  • Coverage floor — cargo-tarpaulin 0.37.5 with --fail-under. Tarpaulin's run is also the test run (it exits non-zero on any test failure), so this becomes the repo's hermetic test gate for the non-integration suite too.
  • Complexity ceiling — cargo-crap 0.6.1 with --threshold + --fail-above, parsed from the same run's lcov.info. CRAP(m) = CC²·(1−cov)³ + CC, so it only means anything with a coverage feed — hence one job, not two.
  • Both tools install from per-arch release tarballs at exact versions, each asserted at install time (a run cannot silently use a newer cargo-crap). An always() summary step posts coverage %, function count, and worst CRAP to the run summary. The Codecov upload stays, still non-gating.

Runner choice

runs-on: ubuntu-latest, unchanged — this is a public repo and its existing ubuntu-latest jobs work; the job follows the repo's own conventions (actions/checkout@v4, dtolnay/rust-toolchain@stable + llvm-tools-preview, actions/cache@v4 keyed on Cargo.lock). The coverage engine is --engine Llvm for estate-wide consistency (the ptrace engine is blocked inside confined runner containers — "ASLR disable failed: EPERM" — measured on the fleet); the llvm-tools-preview component the toolchain step already installs backs it.

Measured first-run numbers (local, build host)

Measured with the pinned binaries (tarpaulin 0.37.5) under the ptrace engine, --all-features, --locked, suite filtered -- --skip integration:

  • Line coverage: 63.72% (1823/2861 lines); suite green — 339 tests, 0 failures, 23 ignored across 5 test binaries.
  • Per file: backend 340/373 (91%), fetch 319/432 (74%), rss 164/201 (82%), server 105/255 (41%), main 121/430 (28%), crawl 72/296 (24%).
  • Worst CRAP: 380.0 = crawl_site (src/tools/crawl.rs:521, CC 19, 0% coverage — behind the opt-in crawlberg feature, measured under --all-features). Next: 99.5 Commands::run (src/main.rs:291), then 90.0 ×3 (run_crawl, expand_sitemap_children, crawl_site_with_crawlberg) and 86.3 FetchClient::fetch_with_retry. 333 functions scored; no file needs excluding — the clap entry point in src/main.rs peaks at 99.5, far under the ceiling.

Thresholds + rationale

  • Coverage floor 61% — measured 63.72% minus ~3pp headroom (covers llvm-vs-ptrace engine differences and fleet jitter).
  • CRAP ceiling 420 — measured max 380 plus ~10% headroom (CRAP is deterministic given coverage; the margin absorbs coverage jitter, which a coverage dip on a high-CC function amplifies through (1−cov)³).

Both gates were proven able to fail before merge: --fail-above at threshold 100 exits 1 on this tree; at 420 it exits 0. The coverage floor likewise fails the build if the suite drops below 61% or any test fails.

Ratchet rule

  • Coverage: +2pp per merged PR touching src/ (floor rises 61 → 63 → 65 …); lowered only with an explicit measurement-based reason in the PR.
  • CRAP ceiling: lowered on the next refactor PR that reduces any function's CRAP (the obvious first targets: crawl_site at 380 and the three 90-scored crawl functions — a few tests covering the crawlberg move collapses all four); hard floor at 30 CRAP before the gate may be tightened further.

DB needs

None. No Postgres, no services, no secrets.

Caveats / blockers

  • Integration tests are network-dependent and stay out of the gate. The integration_tests.rs suite hits live search backends, example.com, arXiv, and celiachia.it; it cannot run hermetically, which is why this repo's own test job gates on -- --skip integration and its integration-test job runs continue-on-error: true. The coverage gate runs exactly the suite the repo already gates on. Retiring the skip means hermeticizing that suite (e.g. wiremock fixtures), not a CI change.
  • Public-repo gate state: required-intended. Branch protection is available on this plan, so once this job is green on the PR it is intended to become a required check; the maintainer wires that after merge.
  • Numbers were measured under ptrace on the build host; the first CI run re-measures under the llvm engine and the summary step will print both gates' live values.

Turn the upload-only coverage job into two gates fed by one instrumented
test run: cargo-tarpaulin 0.37.5 with --fail-under (the run doubles as the
test run, since tarpaulin exits non-zero on test failure) and cargo-crap
0.6.1 with --threshold/--fail-above scored from the same lcov.info. Both
tools install from pinned per-arch release tarballs with asserted versions,
so runs cannot disagree on tool version.

Measured on this tree (ptrace, --all-features, non-integration suite):
line coverage 63.72% (1823/2861), 339 tests green; worst CRAP 380.0
crawl_site (src/tools/crawl.rs:521, CC 19, 0% covered). Floor 61
(measured -3pp), ceiling 420 (worst +10%); the ceiling was proven able to
fail (threshold 100 exits 1 on this tree, 420 exits 0).

The gate covers the suite the repo already gates on: the integration
tests hit live network backends and are skipped here as in the test job.
@bkataru
bkataru merged commit 64bb8fd into main Oct 14, 2026
14 checks passed
@bkataru
bkataru deleted the ci/coverage-crap-gates branch October 14, 2026 06:21
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