CI for Rust
ModernCI runs your cargo commands unchanged and adds Rust-aware handling around them. It narrows test runs to the crates a change touches (Cargo's affected set), reads per-test results from cargo-nextest and libtest, fails a test check that ran zero tests, resolves Cargo.lock and Cargo.toml merge conflicts by itself, and reuses a green result only for identical inputs. ModernCI is itself a Rust workspace tested this way. Speed against other Rust CI setups: measurement in progress.
Marks: live is in release 0.4.28 on main; preview is built in an open pull request, not released; planned is decided, not built.
A branch file for a Rust workspace
# .ci/default.yaml: what a branch must pass to merge into main
to: main
on_green: merge
deadline: 45m
env: { CARGO_INCREMENTAL: "0" }
checks:
fmt: { run: cargo fmt --all -- --check }
clippy: { run: cargo clippy --workspace --all-targets -- -D warnings, needs: [fmt] }
test: { run: cargo nextest run --workspace, needs: [fmt], scope: affected }
The file is read from the target branch, never from the pull request. live
What is Rust-specific
| Capability | Mark | Detail |
|---|---|---|
| Affected crates only | live | scope: affected narrows a check to the crates a change touches, from the Cargo workspace. |
| Per-test results | live | cargo-nextest and libtest output is parsed per test. A test check that ran zero tests fails. |
| Failures already on the target | live | A test that also fails on the target branch is listed apart and isn't charged to the pull request. |
| Flaky tests | live (nextest) | A suspect is re-run alone once, then quarantined: still run and reported, never blocking. Plain cargo test re-runs are planned. |
| Lockfile conflicts | live | Conflicts only in Cargo.lock, Cargo.toml (and package*.json, go.mod, go.sum, changelogs) are resolved in one labelled merge commit, then tested in full. |
| Result reuse | live | A green result is reused only for the same input key: the whole tree plus declared inputs. |
| Reuse keyed by build environment | preview | Keys include the backend's toolchain environment (on dev, #56). |
| Pinned tools | live | Tools a check needs are fetched per pin and verified by sha256 per architecture. |
| Cargo acceleration | preview | A target directory per build environment, with dependency layers restored into fresh work directories (PR #54). |
| Compiler-cache supervision | preview | A declared shared service (such as an sccache server) gets a canary every 5 minutes and is restarted when it hangs (PR #83). |
| Affected-set parse in pure Rust | planned | Today the affected set uses Cargo's own metadata. |
Where Rust builds run
On your own machine (each check as an unprivileged user), over ssh, or on Latchkey elastic runners: a fresh runner per job, billed per minute. The commit under test is packed by the daemon as a git bundle, so no Git provider token reaches the runner. Each pull request goes on the cheapest backend that meets its deadline. live
Evidence
- ModernCI's own workspace (fmt, clippy
-D warnings, tests, a load-test gate) is tested by ModernCI on every change. - Clean and incremental build times compared with GitHub Actions, Blacksmith and Namespace: measurement in progress. The method, runner specs and commands are on benchmarks.
- Measured baseline (busbar, a Rust workspace, 2026-10-12 to 13, one 8-vCPU host): the test check took 243 s at p50 and 961 s at p95, and the median push to green result was 43 min, 37 min of it queued behind checks run one after another.
Limitations
- In 0.4.28 one pull request's checks run one after another. Parallel checks are planned (PR #93 is a first cut).
- A fresh elastic runner starts cold unless a cache is restored. Cargo acceleration is in preview, not released.
- A shared sccache server on our own install has hung several times. Supervising it is in preview (PR #83), and ModernCI's own build cache (PR #54) replaces it.
- Affected-test selection exists only for Cargo.
See also: AI coding agents, benchmarks, facts.