You can tell a Rust project which compiler version to use with one tiny file.
I did. My laptop ignored it, and didn’t say a word.
What I built
No CMS features yet. This step is the ground everything else stands on:
- A workspace with five crates (Rust’s word for packages): the rules, the database layer, the API, the
colophoncommand-line tool andcolophon-server. They’re empty for now, apart from--versionand--help. - A pinned compiler: Rust 1.99.1, written down in
rust-toolchain.toml. - Strict lint rules: no
unsafecode at all, every Clippy warning is an error, and any warning I silence has to say why. - Supply-chain rules with cargo-deny: only licences I’ve approved, no OpenSSL, no second async runtime, packages only from crates.io, and a daily check for new security advisories.
- CI on GitHub, with every outside action pinned to an exact commit, so nobody can swap one out from under me.
- One script,
scripts/check.sh, that runs exactly what CI runs. If it passes on my laptop, it should pass there.
How it works (the short version)
The pin file is read by rustup, Rust’s version manager. But on my laptop, the cargo that runs first comes from Homebrew, and Homebrew’s cargo has never heard of the pin. It just builds with whatever it has: 1.96.0 instead of 1.99.1. No warning, no error.
So the first script compares the compiler that’s actually running with the pinned one, and stops if they differ. The error message includes the fix.
The second script guards the most important line in the whole plan. The crate that holds the rules, colophon-domain, must never touch the network, the disk or a database. That’s what will let the simulator run it with a fake clock and a fake, unreliable network. The script reads the full dependency tree, including dependencies of dependencies, and fails if a banned package shows up anywhere.
Does it actually work?
- With my normal setup:
error: rust-toolchain.toml pins 1.99.1, but this shell runs rustc 1.96.0 and cargo 1.96.0.Exit code 1. Exactly the point. - With rustup’s tools first:
toolchain ok: rustc 1.99.1, cargo 1.99.1. - The architecture check: a green result proves nothing on its own, because a check that checks nothing is also green. So I pointed it at the command-line crate and put
clap(which that crate uses) on the banned list. It failed:error: colophon-cli must not depend on: clap. Now its green means something. - 6 tests, all about promises other programs will rely on. Mistype a flag and
colophonexits with code 2 and prints nothing to stdout, so a script can tell “bad command” apart from real output.colophon-serveraccepts exactly the three documented roles (api,worker,all) and rejects anything else with code 2. - Everything together: format, Clippy, tests, docs, architecture and cargo-deny all pass locally, with zero warnings.
- And on GitHub: the first CI run passed on its first try, both jobs green (run 40219191638). The laptop and CI ran the same script and got the same answer, which was the whole point of this step.
Where I messed up
- The first full run failed on formatting: one test line was too long for rustfmt’s taste.
- Then Clippy failed my test helpers for using
expect(). I’d told Clippy that tests are allowed to, but it turns out that permission only covers code inside#[test]functions, not helper functions next to them. The fix is a labelled exception,#[expect(clippy::expect_used, reason = "...")], which complains if it’s ever no longer needed. - And my first
git pushwas refused: the password manager that holds my SSH key wanted a click, and nobody was there to give it. One unlock later, it went through.
What it doesn’t do yet
Anything a CMS does. colophon --version works, and that’s honestly it.
Up next
0.2, the local stack: PostgreSQL, a cache, file storage and sign-in, all started with one command, with a check that each one is really up.