Using Nix to Simplify Rust CI

Use Nix to simplify testing and Rust workflows!

In my quest to learn Rust, I picked up Zero to Production in Rust. My goal is to learn the “workflow” piece of Rust in web development. One of these pieces is writing CI to make deployment easier however, I quickly ran into an issue, actions-rs is not very well maintained in GitHub actions. So I started to see if there were other ways of handling this short of writing my own actions. Then it hit me, why not just Nix! Nix would allow for me to have a single way of managing CI and developer environment. Furthermore, this would allow for me to port my CI to any system that would allow me to install Nix!

To get started, I used this article from determinate systems as a starting point. This showed me the basic structure of setting up a flake and how a project might use it. My goal was to enable all the pieces from Zero to Production in my Nix flake. So I adapted my flake to install and use cargo-tarpaulin and cargo-audit. Nix uses hashes to determine its reproducibility and one of those is handled by a crate hash. This approach is fine, but it creates and issue when you are changing dependencies and code regularly. Initially, I started by using the hash from the example as seen below:

zero2prod = pkgs.rustPlatform.buildRustPackage {
    pname = name;
    inherit version;
    src = ./.;
    cargoSha256 = "sha256-nLnEn3jcSO4ChsXuCq0AwQCrq/0KWvw/xWK1s79+zBs=";
    release = true;
};

This build failed very quickly and forced me to switch to something that pulls the hash from the Cargo.lock file instead.

zero2prod = pkgs.rustPlatform.buildRustPackage {
    pname = name;
    inherit version;
    src = ./.;
    cargoLock = {
        lockFile = ./Cargo.lock;
    };
    release = true;
};

One thing I don’t have fully fleshed out yet is the dev shell. I still need to add some dev tools such as rust-analyzer to make this complete but for the time being, this works well enough for me. Furthermore, I don’t have this project open, but I will update this post when I do open up this project.

UPDATE: 05/25/2023

Upon further testing of Nix I quickly ran into an issue when compiling project, what do I do about OpenSSL and what the heck is pkg-config? Compiling an actix-web project either requires the use of Rust TLS or bindings to native TLS. When using native my deployments quickly started to fail, what was missing? One quick way to fix this was to add OpenSSL as a build input to allow for my tests to compile in devShells. Great! This solved my testing problem but what about builds? Well it turns out that an important tool for Rust builds is pkg-config. I added this to my flake like the following:

zero2prod = pkgs.rustPlatform.buildRustPackage {
    pname = name;
    inherit version;
    src = ./.;
    cargoLock = {
        lockFile = ./Cargo.lock;
    };
    nativeBuildInputs = [pks.pkg-config]
    PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig"
    release = true;
};

Lastly, another issue I ran into while troubleshooting is that Nix requires that referenced files in git repos be added to the history. So make sure to always add Nix files in existing git repos!