Big shout-outs to @reillywood, who managed cut the CI times for @nu_shell in half!— JT (@jntrnr) April 30, 2022
If you do @rustlang and see your CI build times creeping up, you should give his fixes a look. Smarter test ordering and build caching makes a huge difference.
I recently spent a few days tuning Nushell’s GitHub Actions CI pipelines and it paid off: CI used to take about 30 minutes, and now it’s closer to 10. This is not pleasant or glamorous work, but it has a big payoff; every Nu change going forward will spend a lot less time waiting for essential feedback. Here’s how you can do the same.
Seriously, it’s really good! GitHub build runners are slow. But GitHub gives every repo 10GB of cache space, and
rust-cache takes advantage of that. It caches temporary files for your build dependencies across CI runs, so if you have a lot of dependencies you’ll likely see a big performance boost.
One gotcha to be aware of: GitHub Actions has slightly unintuitive behavior across PRs. PR X is unable to see cache data from PR Y, but they can both see cache data from the base branch (usually
master). This makes sense from an isolation perspective, but it’s not especially well-documented; I ended up adding an extra CI trigger on
main just to fill caches properly.
Split your build and test jobs
Previously we were running
cargo build then
cargo test in a single job. This was suboptimal for a few reasons:
cargo testoften needed to recompile crates that had just been built for
#[cfg(test)]is the most likely culprit here; it makes sense that build output might be different in “test mode”. This has implications for caching too!
- It’s faster to run build and test in parallel; GitHub gives us 20 build runners for free, and we might as well use them.
Run Clippy after cargo build
Previously we were running Clippy before
cargo build. Just switching their order shaved about 5 minutes off every test run! It seems like Clippy can reuse build artifacts from
cargo build, but not vice versa.
Use cargo nextest
cargo nextest is “a next-generation test runner for Rust projects.” It’s dead simple to install in CI, and it’s often faster than
cargo test. We didn’t see a huge benefit from this (maybe 30-40s faster?), but that’s because our CI time is dominated by compilation; YMMV depending on your code base and test suite.
If you’d like to see the actual changes, they’re all here. Like anything GitHub Actions, this took a lot of tries to get right; those 5 PRs are just the tip of the iceberg, there were a lot more experimental changes in my private fork. I’m hopeful that someday we’ll be able to stop programming in YAML files, but we’re not there yet!