When you have software out in the wild being used by lots of people, it’s inevitable that there will be pretty large spread across the versions of your software being used.

If that software is aimed towards technical folks and power users, or if you publish nightly releases, in addition to the spread across official version releases, you will also have many people running on any number of commit hashes.

It has been very valuable for me to have commit information available when a user runs my software on Windows with the --version flag as this allows me to quickly narrow down if a reported issue has already been addressed in the current stable or nightly release.

I have done this using the excellent shadow-rs crate, which queries various git information at build time, and provides a convenient CLAP_LONG_VERSION which can be used when building a CLI with clap to provide detailed build information.

komorebi 0.1.38
branch:master
commit_hash:21cb5e1e
build_time:2025-06-10 00:26:27 +02:00
build_env:rustc 1.85.0 (4d91de4e4 2025-02-17),stable-x86_64-pc-windows-msvc

One of the things that I miss when developing for Windows is having reproducible builds using a Nix flake, so naturally, one of the highlights of porting my software to macOS has been finally being able to build my project with nix build.

When building Rust projects I typically use crane to do all of the heavy lifting, including filtering my repository for the relevant files needed to compile the project crates themselves.

Typically when filtering relevant files for any language when building with Nix, the .git directory is excluded, which means that shadow-rs will not be able to extract information about the current commit at build time.

komorebi 0.1.0
branch:
commit_hash:
build_time:1980-01-01 00:00:00 +00:00
build_env:rustc 1.90.0 (1159e78c4 2025-09-14),

Although I prefer to ship binaries built using nix build, users who compile from source will often use cargo install, meaning that whatever path I take for getting more detailed information returned from --version, it has to work both when building using cargo directly and when using nix build.

Luckily, a Nix flake will expose its current git revision via inputs.self.rev, and this can be set as a build-time environment variable in flake.nix.

{
    commonArgs = {
      inherit src version;
      strictDeps = true;

      COMMIT_HASH = self.rev or (lib.removeSuffix "-dirty" self.dirtyRev);

      buildInputs = [
        pkgs.gcc
        pkgs.libiconv
      ];
    };
}

I was not, however, able to find a way to get the current git branch name from a Nix flake.

The next step is to modify the build.rs which was previously just calling shadow-rs to behave accordingly when being called via nix build.

Running the shadow-rs builder and seeing if one of the git-sourced values such as BRANCH was populated is one way to determine if we are inside of a nix build.

If BRANCH was not populated, we can run the shadow-rs builder again with a custom hook which will populate the commit_hash value of the --version output using env!("COMMIT_HASH") - the environment variable used to expose inputs.self.rev in flake.nix.

use std::fs::File;
use std::io::Write;

use shadow_rs::SdResult;
use shadow_rs::ShadowBuilder;

fn main() {
    let shadow_build = ShadowBuilder::builder()
        .hook(raw_hook)
        .build()
        .unwrap();

    let is_flake_build = shadow_build
        .map
        .get("BRANCH")
        .map_or_else(|| true, |entry| entry.v.is_empty());

    if is_flake_build {
        ShadowBuilder::builder().hook(flake_hook).build().unwrap();
    }
}

const RAW_VERSION_CONST: &str = r##"pub const LONG_VERSION:&str = shadow_rs::formatcp!(r#"{}
branch:{}
commit_hash:{}
build_env:{}"#,PKG_VERSION, BRANCH, COMMIT_HASH, RUST_VERSION
);
"##;

fn raw_hook(mut file: &File) -> SdResult<()> {
    writeln!(file, "{RAW_VERSION_CONST}")?;
    Ok(())
}

const FLAKE_VERSION_CONST: &str = r##"pub const LONG_VERSION:&str = shadow_rs::formatcp!(r#"{}
commit_hash:{}
build_env:{}"#,PKG_VERSION, env!("COMMIT_HASH"), RUST_VERSION
);"##;

fn flake_hook(mut file: &File) -> SdResult<()> {
    writeln!(file, "{FLAKE_VERSION_CONST}")?;
    Ok(())
}

Note that both the RAW_VERSION_CONST and FLAKE_VERSION_CONST hooks will generate a pub const LONG_VERSION. This ensures that it’s possible to reference a single version = LONG_VERSION value when creating a CLI with clap.

#[derive(Parser)]
#[clap(author, about, version = LONG_VERSION)]
struct Opts {
    #[clap(subcommand)]
    subcmd: SubCommand,
}

The end result is that binaries built both with cargo build and nix build will now be able to return detailed version information.

With nix build:

❯ ./result/bin/komorebi --version
komorebi 0.1.0
commit_hash:4e8caf2b2a777cf8dbc75fbaa87d98406e6bc390
build_env:rustc 1.91.0 (f8297e351 2025-10-28)

With cargo build:

❯ ./target/debug/komorebi --version
komorebi 0.1.0
branch:master
commit_hash:4e8caf2b2a777cf8dbc75fbaa87d98406e6bc390
build_env:rustc 1.91.0 (f8297e351 2025-10-28)

If anyone has any ideas on how the branch name could also be exposed from a Nix flake, let me know!

Finally, a big thank you to both Isabel and Ivan for pointing me in the right direction and leading me to this solution.


If you have any questions or comments you can reach out to me on Bluesky and Mastodon.

If you’re interested in what I read to come up with software like komorebi, you can subscribe to my Software Development RSS feed.

If you’d like to watch me writing code while explaining what I’m doing, you can also subscribe to my YouTube channel.

If you would like early access to komorebi for Mac, you can sponsor me on GitHub.