ci: parallelize workspace checks to cut PR build time - #353
Conversation
The build and build-in-minimal jobs each recompiled the whole 24-crate workspace several times per PR: build ran cargo build, then cargo test, then cargo clippy (each a near-full compile since the three don't share artifacts), and build-in-minimal re-ran test + clippy via `minimal run ci`. That is ~5 sequential full compiles. Split the work into parallel single-purpose jobs so wall-clock time is roughly one compile instead of the sum: - fmt: cargo fmt --all -- --check (no compilation) - clippy: cargo clippy --workspace --all-targets - test: cargo nextest run --workspace plus a doctest pass Drop the redundant standalone cargo build (cargo test already builds every target). Replace the Cargo.lock-keyed actions/cache with Swatinem/rust-cache for registry/git state and sccache (GHA backend) as a compiled-object cache shared across the clippy and test jobs. Reduce the dogfood job (was build-in-minimal) to `minimal run build-smoke` so it still proves `minimal run` works without a second full test+clippy pass, and add the build-smoke task. Add a ci-success aggregator job for branch protection to gate on, and rename the docs-skip stub job to match. https://claude.ai/code/session_01Xm385bunemjDpH9rz9AFZP
Use the current marketplace-recommended release of mozilla-actions/sccache-action instead of v0.0.9. https://claude.ai/code/session_01Xm385bunemjDpH9rz9AFZP
|
Warning Review limit reached
More reviews will be available in 60 minutes and 55 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe PR restructures GitHub Actions CI to separate quality-gate jobs ( ChangesCI Workflow and Status Gating
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Empty commit to measure run-over-run sccache/rust-cache improvement. https://claude.ai/code/session_01Xm385bunemjDpH9rz9AFZP
CI timing — measured results (baseline)Measured on real Actions runs. "Old CI" = a recent Wall-clock (PR critical path)
Old jobs for reference: Takeaways
Next: replacing sccache with
|
The sccache layer hit 90% on warm runs but barely helped: ~138s went to downloading cached objects from the GHA cache one-by-one, build scripts and proc-macros (184 compilations) are never cacheable by sccache, and the parallel clippy+test jobs collided writing one shared namespace (239 write errors, causing forced misses on the next run). Switch the clippy and test jobs to Swatinem/rust-cache with its default target caching: one tarball restore of the registry plus dependency build artifacts (including build-script output), keyed per job so the two jobs no longer contend. Also trim the Free Disk Space step to the fast rm-based removals, dropping the slow apt package purge. https://claude.ai/code/session_01Xm385bunemjDpH9rz9AFZP
Replace the single `minimal run build-smoke` dogfood step with a matrix that runs the real fmt, clippy, and test work through `minimal run`, so the tool is exercised on the same checks as the cargo jobs without a serial pass. Break the monolithic `[tasks.ci]` blob into `ci-fmt`, `ci-clippy`, and `ci-test` sub-tasks aligned to the cargo jobs (`fmt --all`, `clippy --workspace --all-targets`, `test --workspace`), and drop the now-unused `build-smoke` task. `minimal` has no native parallel or composite task support, so the GitHub Actions matrix provides the parallelism. https://claude.ai/code/session_01Xm385bunemjDpH9rz9AFZP
This reverts commit 4e6b2be. The matrix legs run inside minimal's sandbox, which does not use rust-cache/sccache, so each leg stayed a full ~13-16 min sandboxed compile and parallelizing them did not move the wall-clock. Restore the lightweight `minimal run build-smoke` dogfood and the original task definitions. https://claude.ai/code/session_01Xm385bunemjDpH9rz9AFZP
CI timing — after replacing sccache with
|
| total | Run Clippy step | |
|---|---|---|
| sccache — cold | 13.1m | 10.8m |
| sccache — warm | 10.7m | 8.4m |
| rust-cache — cold | 11.25m | 9.9m |
| rust-cache — warm | 2.5m | 0.5m |
test
| total | nextest step | |
|---|---|---|
| sccache — cold | 17.3m | 14.1m |
| sccache — warm | 11.1m | 9.5m |
| rust-cache — cold | 15.6m | 12.8m |
| rust-cache — warm | 4.4m | 2.1m |
Free Disk Space step (trimmed: dropped the slow apt package purge)
~1.8m → 44s–1.3m.
Why rust-cache wins
- One tarball restore (~13–22s) instead of sccache fetching 875 objects one-by-one (~138s of
cache_read_hit_duration). - Caches dependency
.rlibs and build-script output directly → cargo skips deps entirely. This repo's protobuf build scripts (prost-build/tonic-prost-build) were in sccache's 184 "non-cacheable" compilations; rust-cache restores their output. - Per-job cache keys → no cross-job write contention (sccache had 239 write errors → 97 forced warm misses). rust-cache warm runs hit clean.
Net for the cached jobs, warm: clippy 10.7m → 2.5m, test 11.1m → 4.4m.
⚠️ Honest caveat: end-to-end PR wall-clock is unchanged
The PR's critical path is now gated by dogfood (~15–16m), which runs minimal run build-smoke inside minimal's sandbox using minimal's own state cache — it does not use rust-cache and barely warms. So clippy/test finishing in 2.5/4.4m now just means they idle while dogfood runs.
- Compute/cost drops meaningfully (warm clippy+test went from ~22 job-min to ~7).
- Wall-clock won't drop below ~15m until
dogfood's sandboxed build is cached or slimmed — tracked separately; not in scope here.
| Wall-clock (PR critical path) | value | bound by |
|---|---|---|
Original CI (build + build-in-minimal) |
~32m | serial build job |
| This PR, warm | ~15–16m | dogfood (uncached) |
Generated by Claude Code
Problem
The
buildandbuild-in-minimaljobs each recompiled the whole 24-crate workspace several times per PR:buildrancargo build→cargo test→cargo clippysequentially. These three don't share compiled artifacts (clippy usesclippy-driver; build/test use different target metadata), so it was ~3 near-full compiles — andcargo buildwas redundant sincecargo testalready builds every target.build-in-minimalranminimal run ci=cargo test+cargo clippyagain — ~2 more full compiles.≈ 5 sequential full compiles per PR, behind weak caching (
actions/cachekeyed only onCargo.lock, sotarget/was only re-saved when deps changed). The slowness was redundant recompilation, not crate count.Changes
Split the monolithic
buildjob into parallel single-purpose jobs so wall-clock ≈ one compile instead of the sum:fmtcargo fmt --all -- --checkclippycargo clippy --workspace --all-targets -- -D warningstestcargo nextest run --workspace+cargo test --workspace --doccargo build(subsumed bycargo test).Cargo.lock-keyedactions/cachewithSwatinem/rust-cache(registry/git state) +sccache(GHA backend) as a compiled-object cache shared across theclippyandtestjobs, so the split doesn't blow up total minutes.dogfood(wasbuild-in-minimal): reduced tominimal run build-smoke— still provesminimal runworks end-to-end without re-running the full test+clippy suite. Adds abuild-smoketask (cargo build -p minimal) to.minimal/minimal.toml.ci-success: aggregator job for branch protection to gate on. The docs-skip stub job is renamedbuild→ci-successto keep docs-only PRs unblocked.Release jobs,
cargo-deny, andminimal-checkare unchanged.The branch-protection ruleset on
mainmust be updated to requireci-successinstead ofbuild/build-in-minimal. Otherwise PRs will block waiting on the oldbuildcheck that no longer reports.Verification
Validated locally: both workflows +
minimal.tomlparse, the job graph is consistent,minimalis a real package, andcargo fmt --all -- --checkpasses. The end-to-end payoff (parallel wall-clock, sccache hit rates) shows on this PR's CI run.Generated by Claude Code
Summary by CodeRabbit