Skip to content

Instantly share code, notes, and snippets.

@ejpcmac
Last active September 29, 2022 20:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ejpcmac/e2e544ffc4bcd99b0216040769578d14 to your computer and use it in GitHub Desktop.
Save ejpcmac/e2e544ffc4bcd99b0216040769578d14 to your computer and use it in GitHub Desktop.
Define a VERSION_WITH_GIT environment variable for Rust projects.
use std::{io, process::Command};
fn main() {
define_version_with_git();
}
/// Defines a variable containing the version with the Git revision.
///
/// `VERSION_WITH_GIT` contains at least the cargo version, even when Git is not
/// available. When Git is available, the current Git revision and dirty state
/// is added to the version as a tag, except in the following cases:
///
/// * the build is done from a clean worktree checked out at a tag matching
/// *exactly* the cargo version prefixed by `v`,
/// * the build is done from a cargo checkout (via `cargo install --git`) and
/// the version does not contain `-dev`.
///
/// For instance:
///
/// * Cargo version 1.0.0 on tag v1.0.0, clean state => `1.0.0`
/// * Cargo version 1.0.0 on tag v1.0.0, dirty state =>
/// `1.0.0+abcd1234-modified`
/// * Cargo version 1.1.0-dev on any commit, clean state => `1.1.0-dev+abcd1234`
fn define_version_with_git() {
let cargo_version = env!("CARGO_PKG_VERSION");
let version = version_with_git(cargo_version)
.unwrap_or_else(|_| String::from(cargo_version));
println!("cargo:rustc-env=VERSION_WITH_GIT={version}");
}
fn version_with_git(cargo_version: &str) -> io::Result<String> {
if git_describe()? == format!("v{cargo_version}")
|| is_cargo_checkout()? && !is_dev_version(cargo_version)
{
Ok(String::from(cargo_version))
} else {
let revision = git_revision_and_state()?;
Ok(format!("{cargo_version}+{revision}"))
}
}
fn git_describe() -> io::Result<String> {
let output = Command::new("git")
.args(&["describe", "--always", "--dirty=-modified"])
.output()?;
Ok(String::from_utf8(output.stdout).unwrap().trim().to_owned())
}
fn git_revision_and_state() -> io::Result<String> {
let revision = git_revision()?;
if git_is_dirty()? && !is_cargo_checkout()? {
Ok(format!("{revision}-modified"))
} else {
Ok(revision)
}
}
fn git_revision() -> io::Result<String> {
let output = Command::new("git")
.args(&["rev-parse", "--short", "HEAD"])
.output()?;
Ok(String::from_utf8(output.stdout).unwrap().trim().to_owned())
}
fn git_is_dirty() -> io::Result<bool> {
let output = Command::new("git")
.args(&["status", "--porcelain"])
.output()?;
Ok(!output.stdout.is_empty())
}
fn is_cargo_checkout() -> io::Result<bool> {
let output = Command::new("git")
.args(&["status", "--porcelain"])
.output()?;
Ok(output.stdout == b"?? .cargo-ok\n")
}
fn is_dev_version(cargo_version: &str) -> bool {
cargo_version.contains("-dev")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment