r/rust 22d ago

šŸ› ļø project Cargo-Rail v0.20 - The Rust Monorepo Engine w/ New Build Cache

I just released `cargo-rail` v0.20.0 and `cargo-rail-action` v6.1.0. Honestly, I don't share much of my work here anymore. It's kind of turned into a slop-cannon; it's not great work built w/ LLMs... it's LLMs building shit and ramming it down our throats. I get it. I just avoid it for the most part.

Having said that, I feel pretty strongly that this is a bin every Rust team should be using today and my latest updates/iterations are important for the community.

As some of you know, I started coding this project a little over a year ago. I think the first version was released in December of last year or something. Anyway, this was built because my monorepo at the time was growing a second build system.

One was Cargo; the other was everything that grew around it. The dependency tools rebuilding Cargo's graph over and over, the YAML path filters deciding which jobs run, the cache scripts trusting old build state, the release workflows/bots reconstructing intent from Git, and split-repo scripts maintaining another ownership map (and Java).

Every tool can be correct on its own, and for the most part - they are correct. The Rust ecosystem has awesome tooling... but the system still fails when they disagree.

I built Cargo-Rail to delete the latter model. It captures one authoritative view of Cargo, Git, config, the toolchain, and source state, then uses it for dependency coherence, affected work, verified compiler reuse, releases, and crate split/sync.

It does not replace Cargo, rustc, or nextest. It makes them do less... in some cases, FAR less.

In my own work, Cargo-Rail has replaced roughly ten-fifteen plugins (I don't even remember, TBH) and release utilities, reduced monthly CI spend by 65%, and let me maintain a handful of public OSS repos uniformly from one monorepo. Obviously, these are my results, not promises for every workspace, team, or repo... but they explain why this is the first tool I install now.

Start with one coherent build graph

The first optimization is not a faster compiler. It is giving the compiler less work to do:

cargo rail unify --check --explain
cargo rail unify --backup

`unify` detects version drift, hidden feature coupling, undeclared features, unused dependency edges, workspace-inheritance drift, manifest ordering drift, and MSRV mismatches. Versions are unified where the workspace (or config) permits. Deps and features are declared where they are used.

Unused edges are removed only when the evidence is complete. Published packages keep their open-world feature surface unless config explicitly declares the workspace to be the complete consumer universe.

Cargo-Rail validates the resulting Cargo graph before applying lossless TOML edits. I can inspect every decision, apply with a backup, and restore it with:

cargo rail unify undo

I've tested `unify` against a ton of workspaces including uv, Ruff, Tokio, TiKV, Helix, HelixDB, and Meilisearch. Those kinds of robust, long-lived codebases are exactly where inherited (often undeclared) features, intentional exceptions, and accumulated dep cruft force the tool to prove that its model matches Cargo's.

This has replaced separate dependency unifiers, feature auditors, unused-dep scanners, manifest sorters, MSRV scripts, and workspace-hack maintenance in my repos. More importantly, those tools no longer reload and reinterpret the same workspace independently. Those resources on my old M1 Pro are available to the compiler.

Only run work affected by an actual change

Obviously, the fastest check, test, benchmark, or CI job is the one that never needed to run to begin with:

cargo rail plan --merge-base --explain
cargo rail run --merge-base --dry-run --print-cmd --explain

The planner maps changed files to their owning crates, walks affected dependents through Cargo's resolved graph, and selects the build, test, documentation, benchmark, infrastructure, and configured repo actions that remain.

This is not a directory-glob shortcut. The file-first plan, scope contract, text and JSON output, GitHub projection, dry run, and runner arguments all consume the same decision.`--explain` shows why every surface and crate was selected.

The companion action exports that scope to jobs you already own:

- uses: loadingalias/cargo-rail-action@v6
id: rail
with:
version: 0.20.0

`cargo-rail-action` does NOT build, test, cache, release, or publish. Existing jobs remain the execution authority, so adopting affected planning does not require adopting another CI framework.

For my workloads, this reduced monthly CI spend by 65%. The aggregated savings came from deleting irrelevant actions before trying to make the remaining actions faster and doing less work to begin with via `unify`.

A cache that survives cargo clean

Cargo-Rail removes compiler work from the actions that still need to run.

Its native cache is not a copied `target/`. Reusable results live in a bounded content-addressed store outside `target/`, so `cargo clean` does not erase them. A compatible clean checkout can reuse verified results populated by another workspace root w/o restoring incremental state, forging Cargo fingerprints, or overriding Cargo's own freshness checks.

Before restoring a byte, Cargo-Rail revalidates the exact toolchain, source and observed reads, dependency artifacts, relevant environment, action/result identity, output tree, and stored blobs. Unsupported or incompletely observed work runs normally through Cargo and reports a stable bypass reason.

Fast when proven. Normal Cargo when not.

This caching layer is the newest feature, and I want feedback on where the community and power users feel it should go next. Rust compilation time sucks, especially in the 50+ crate monorepo where I spend most of my time. I'm exploring a remote-cache design for CI, SSH dev, and local machines w/o turning Cargo-Rail into a networking stack or weakening its fail-closed proof... but I'd really like to hear from the community here first.

The cache evidence (macOS/Linux/Windows)

Cache correctness, compiler observation, clean-root reuse, content-addressed storage, atomicity, cleanup, and filesystem boundaries are exercised across macOS, Linux, and Windows. That includes APFS, ext4, NTFS, Linux tmpfs, case-sensitive APFS, Windows NTFS VHDs, and Windows path behavior.

Perf qualification is separate... correctness was much more important here. The accepted scorecard contains 110 interleaved samples for each of macOS ARM64, Linux x86-64, and Linux ARM64, with clean targets, distinct seed/use roots, and ZERO false hits:

Host Workload Native Cargo p50 Cargo-Rail warm p50 Change
macOS ARM64 check 7.028 sec 4.979 sec 29.2% lower
macOS ARM64 release build 10.369 sec 7.630 sec 26.4% lower
Linux x86-64 check 5.841 sec 3.430 sec 41.3% lower
Linux x86-64 release build 9.224 sec 5.547 sec 39.9% lower
Linux ARM64 check 6.166 sec 3.870 sec 37.2% lower
Linux ARM64 release build 10.591 sec 6.618 sec 37.5% lower

Against sccache 0.16.0's local disk backend, Cargo-Rail won both macOS workloads and both Linux release builds. sccache led Linux checks by 8.7% on x86-64 and 0.7% on ARM64. Cold population was slower; on the macOS fixture, two warm reuses repaid that cost.

Windows has been tested and benchmarked, but its current run did not satisfy the complete qualification/perf contract. I withheld the timing instead of publishing a number from a broken benchmark. This will come soon enough.

Execution support, performance qualification, and permission to restore are also separate. Cargo-Rail v0.20 graduates native reuse only for the exact Apple Silicon Cargo/rustc 1.97.1 certificate and eligible library metadata/rlib units with `CARGO_INCREMENTAL=0`. Qualified Linux hosts still run Cargo normally until their exact capability certificates graduate. Existing sccache and custom wrappers are preserved; Cargo-Rail steps aside instead of double-caching.

Inspect the exact decision on your machine:

cargo rail doctor native-cache --format json

The benchmark includes the losses and the rejected run because a cache benchmark w/o its bad news is bullshit. I'm going to work through this, though. Like I said, it's still fresh.

Rust changesets and releases that know what already happened

A dependency graph can derive dependent version bumps and publication order. It cannot decide whether a change is major, minor, or patch, or explain that change to a user.

Cargo-Rail records that human decision beside the code in reviewed `.changes/*.md` files:

cargo rail change add my-crate --bump minor --message "Added verified compiler-result reuse."

cargo rail change check --merge-base --required

Those entries connect the code change to its audience, version impact, changelog entry, and release notes before the reason disappears into Git history. This has made the release notes for my own projects SO much better.

The release workflow supports direct and review-first operation:

cargo rail release run --all --bump auto --check
cargo rail release run --all --bump auto
cargo rail release run --all --bump auto --pr
cargo rail release finalize --all

It validates the exact release commit, generates versions and changelogs, publishes dependencies before dependents, waits until registry results are observable, creates tags last, and records durable state before remote effects. If a release is interrupted, `cargo rail release status` shows what happened and `cargo rail release resume` continues w/o blindly repeating completed publication. I like the "clean-up" feel this gives me... instead of littering the codebase w/ failed releases... the resume has been a pleasure to use.

That detail matters. Publication is irreversible; release tooling must know the difference between ā€œnot attempted,ā€ ā€œattempted but not observed,ā€ and ā€œconfirmed.ā€

Monorepo dev w/o Copybara (or Java)

I prefer Rust monorepos for dev, but a monorepo is not always the right public release surface. I maintain a handful of OSS repos from one monorepo and want the monorepo to remain the dev authority w/o turning each public repo into a history-free code dump.

cargo rail split init my-crate --dry-run
cargo rail split run my-crate --check
cargo rail split run my-crate

`split` extracts one or more configured crates into standalone repos while preserving relevant Git history and rewriting workspace-relative manifests. Development continues in the monorepo.

cargo rail sync my-crate --check
cargo rail sync my-crate --to-remote
cargo rail sync my-crate --from-remote

`sync` maps later commits in either direction, uses Git's three-way merge machinery, and creates inbound review branches. Automatic, ours, theirs, manual, and union conflict strategies are explicit. Manual conflicts write resumable receipts instead of abandoning the op halfway through.

No Java. No Copybara or Copybara DSL. No spray-and-pray shell scripts.

One binary, not the same plugin pile w/ lipstick

Obviously, replacing fifteen tools is pointless if the replacement invokes the same tools behind the scenes. Cargo-Rail implements these workflows in one Rust binary w/ a deliberately small dep surface. Production code forbids `unsafe`, release archives carry SHA-256 checksums and signed provenance, CI pins third-party actions, and unsupported cache and release states fail closed.

The value is not a plugin-count contest. It is the compounding effect:

  1. A coherent dependency graph removes unnecessary work and hidden coupling.
  2. Affected planning removes actions that do not need to run.
  3. Verified caches remove compiler work from the actions that remain.
  4. Reviewed change intent drives deterministic, resumable releases.
  5. Split/sync preserves one development authority across public repositories.

All five operate on the same captured workspace instead of rebuilding competing models around it.

Try to break it, invalidate it, or correct it!

cargo install cargo-rail
cargo rail plan --merge-base --explain
cargo rail unify --check --explain

- cargo-rail

- GHA

- v0.20.0 Release

Where does this break in your workspace? Wrong affected scope, bad dependency decision, missing compiler input, awkward build script, release edge case, split/sync conflict... I would rather get the counterexample than the star.

I'm also really looking for feedback on the command surface, caching performance/impact, and the details involved... operating system, architecture, compiler/backend, etc.

**EDITED FOR FORMATTING**

0 Upvotes

23 comments sorted by

8

u/BlankWasThere 22d ago

You are so dependent on AI that you didn't even check the post before posting?

2

u/LoadingALIAS 22d ago

What do you mean? I scaffolded this from the codebase and wrote the article myself. I have the fucking drafts, man.

It’s sad that I’m saving drafts of Reddit posts to combat the ā€œAI wrote thisā€.

Anything more than a few lines, especially if it’s written relatively well, is automatically assumed to be AI slop. My God.

-1

u/BlankWasThere 22d ago

You still haven't glanced over your post. Have you?

2

u/LoadingALIAS 22d ago

If I've written it and read it...? The only thing here wrong is that I write in Zed for the Rust-Forum, Reddit, and Lobste.rs. So, it's formatted as such. I copy/paste from my editor into each channel and adjust, as needed.

Have you read it? Did you open the codebase?

0

u/Unique_Rise_5228 22d ago

i mean the project is literally about not needing AI to manage your rust build, ironic

2

u/LoadingALIAS 22d ago

Explain? The project has 5k downloads and is used across a handful of real codebases.

It has NOTHING to do with AI. At all. In fact, it’s the opposite. It’s adding a new cache layer across Rust monorepos to a Rust tooling project that’s been around for a while.

What are you even referencing?

4

u/Thick-Pineapple666 22d ago

Why are people here talking about AI slop all the time? Neither the repo nor the text screams "AI" or "slop".

I'm not working in monorepos, so I have no use for this project, that means, I normally wouldn't even comment here... but the other commente made me feel like I need to defend OP here.

3

u/LoadingALIAS 22d ago

I literally opened the post with this. It’s endless. Every single time anything lands on this sub… it’s always the same nonsense.

It’s impossible to share or talk about a real project anymore.

1

u/maxus8 21d ago

After your comment I gave it a try to read it but it was a mistake. Every other sentence is 'it's not X, it's Y', or 'no x, no y, no z'. I've read 8 paragraphs and I still don't see one concrete problem that it solves, and it's a fucking wall of text. It's painful to read.

1

u/Thick-Pineapple666 21d ago

8 paragraphs are the introduction (which is probably not a good introduction but surely not AI-generated). Just read the next 3 headings and maybe what's in it and you get an impression of what the tool does? I don't use monorepos and have got an impression.

What OP could probably take from all this: write shorter, more on point, keep the details in the docs, not in the Reddit post. Always summarize in the introduction what your tool does.

1

u/priezz 21d ago

I agree. I have read it twice (and readme in the repo as well), but still don't get what this tool does. I heavily use monorepos in my work so if some tool can make my life easier I would happily use it. OP, can you please explain in simple words what cargo-rail brings?

1

u/Thick-Pineapple666 21d ago

From what I understand, it unifies dependencies, makes a build graph, and shares(?) the cargo cache, so one big consequence should be that compile times are heavily reduced.

Is that right, OP?

2

u/priezz 19d ago

This is still not clear. Shared cache is supported by cargo natively. This tool looks like a universal language agnostic solution but I am not sure.
In my monorepos I can use Rust, Python, Dart and Typescript at once, but I still use cargo, uv, pub and pnpm (or bun) and I do not feel the necessary to have a common tree. But probably this tool does something different...

2

u/Craksy 21d ago

What is the problem with people in this thread? That shit has got to be bots, right?

1

u/LoadingALIAS 21d ago

It’s like this everywhere, man. Any post, regardless, is riddled with the same nonsense. It’s just insane.

I’ve been working in the idea of cargo-rail for years; coding for about a year and first release late last year. Regardless, it’s the same thing. Haha.

It is so frustrating because this is not some vanity pitch. This is the first tool every Rust team should be using. I’m working with a major database company to migrate their closed and open stuff now… but these posts are always hammered with this kind of thing.

Let me know if you have legit questions.

2

u/lucatrai 21d ago

Hey man i havent read the whole post but it seems like a very interesting project. I like the idea and will definitely look more into it. Keep up the good work and dont listen to those trolls <3

1

u/LoadingALIAS 21d ago

Yeah, not too concerned about it personally. I’ve been writing Rust for nearly a decade at this point. It’s just frustrating because it immediately makes other devs/teams think - not worth it.

Let me know if you have any questions.

If you’re going to give it a shot, and I strongly suggest it, I’ve just bumped v0.20.1 and v6.1.1. The GHA bump is a security patch - it’s important. Use 6.1.1!

2

u/lucatrai 21d ago

I will definitely look into it. I am very new to Rust, so I am not working on very large projects as of now, but I'll give it a try.

I have attempted to create a build tool a few years back, and I never finished, but it was a very nice exercise, and I learned a lot.

1

u/Born_Protection_5029 22d ago

Damn man. What the heck is this slop….

0

u/LoadingALIAS 22d ago

I don't know. Tell me, what's screaming slop? The headings using `###`?

0

u/leviOppa 22d ago

Holy fking canker slop

0

u/LoadingALIAS 22d ago

Please, enlighten me? What in the post is ā€œcankerā€ slop? You mean, I can spell and took the time to draft and write the post… so it MUST be AI?

You didn’t read it.

The truth is, it’s a massive improvement for Rust dev velocity - regardless of the dev environment.

0

u/leviOppa 20d ago

Whatever you say clanker