Skip to content

Commit

Permalink
Include git commit hash in version output
Browse files Browse the repository at this point in the history
To better identify issues or just get an idea which change exactly a
given binary was built from, this patch makes sure to contain the Git
commit hash (or tag, if present) in the version output we provide via
the -V/--version flag.
  • Loading branch information
d-e-s-o committed Jan 23, 2022
1 parent 0775637 commit 25241ec
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 5 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ path = "var/shell-complete.rs"
name = "nitrocli-otp-cache"
path = "ext/otp_cache.rs"

[build-dependencies]
anyhow = "1.0"

[profile.release]
opt-level = "z"
lto = true
Expand Down
115 changes: 115 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (C) 2021-2022 The Nitrocli Developers
// SPDX-License-Identifier: GPL-3.0-or-later

use std::process::Command;

use anyhow::bail;
use anyhow::Context;
use anyhow::Result;

const GIT: &str = "git";

/// Format a git command with the given list of arguments as a string.
fn git_command(args: &[&str]) -> String {
args.iter().fold(GIT.to_string(), |mut cmd, arg| {
cmd += " ";
cmd += arg;
cmd
})
}

/// Run git with the provided arguments and read the output it emits.
fn git_output(args: &[&str]) -> Result<String> {
let git = Command::new(GIT)
.args(args)
.output()
.with_context(|| format!("failed to run `{}`", git_command(args)))?;

if !git.status.success() {
let code = if let Some(code) = git.status.code() {
format!(" ({})", code)
} else {
String::new()
};

bail!(
"`{}` reported non-zero exit-status{}",
git_command(args),
code
);
}

let output = String::from_utf8(git.stdout).with_context(|| {
format!(
"failed to read `{}` output as UTF-8 string",
git_command(args)
)
})?;

Ok(output)
}

/// Run git with the provided arguments and report the status of the
/// command.
fn git_run(args: &[&str]) -> Result<bool> {
Command::new(GIT)
.args(args)
.status()
.with_context(|| format!("failed to run `{}`", git_command(args)))
.map(|status| status.success())
}

/// Retrieve a git revision identifier that either includes the tag we
/// are on or the shortened SHA-1. It also contains an indication
/// whether local changes were present.
fn git_revision() -> Result<Option<String>> {
// As a first step we check whether we are in a git repository and
// whether git is working to begin with. If not, we can't do much; yet
// we still want to allow the build to continue, so we merely print a
// warning and continue without a git revision. But once these checks
// are through, we treat subsequent failures as unexpected and fatal.
match git_run(&["rev-parse", "--git-dir"]) {
Ok(true) => (),
Ok(false) => {
println!("cargo:warning=Not in a git repository; unable to embed git revision");
return Ok(None);
}
Err(err) => {
println!(
"cargo:warning=Failed to invoke `git`; unable to embed git revision: {}",
err
);
return Ok(None);
}
}

let local_changes = git_output(&["status", "--porcelain", "--untracked-files=no"])?;
let modified = !local_changes.is_empty();

// If we are on a tag then just include the tag name. Otherwise use
// the shortened SHA-1.
let revision = if let Ok(tag) = git_output(&["describe", "--exact-match", "--tags", "HEAD"]) {
tag
} else {
git_output(&["rev-parse", "--short", "HEAD"])?
};
let revision = format!("{}{}", revision.trim(), if modified { "+" } else { "" });
Ok(Some(revision))
}

fn main() -> Result<()> {
if let Some(git_revision) = git_revision()? {
println!("cargo:rustc-env=NITROCLI_GIT_REVISION={}", git_revision);
}
// Make sure to run this script again if any of our sources files or
// any relevant version control files changes (e.g., when creating a
// commit or a tag).
println!("cargo:rerun-if-changed=.git/index");
println!("cargo:rerun-if-changed=.git/refs/");
println!("cargo:rerun-if-changed=Cargo.lock");
println!("cargo:rerun-if-changed=Cargo.toml");
println!("cargo:rerun-if-changed=ext/");
println!("cargo:rerun-if-changed=src/");
println!("cargo:rerun-if-changed=var/");
Ok(())
}
17 changes: 12 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// main.rs

// Copyright (C) 2017-2021 The Nitrocli Developers
// Copyright (C) 2017-2022 The Nitrocli Developers
// SPDX-License-Identifier: GPL-3.0-or-later

#![warn(
Expand Down Expand Up @@ -163,11 +163,18 @@ fn handle_arguments(ctx: &mut Context<'_>, argv: Vec<String>) -> anyhow::Result<

fn get_version_string() -> String {
let version = env!("CARGO_PKG_VERSION");
if let Ok(library_version) = nitrokey::get_library_version() {
format!("{} using libnitrokey {}", version, library_version)
let built_from = if let Some(git_revision) = option_env!("NITROCLI_GIT_REVISION") {
format!(" (built from {})", git_revision)
} else {
format!("{} using an undetectable libnitrokey version", version)
}
"".to_string()
};
let libnitrokey = if let Ok(library_version) = nitrokey::get_library_version() {
format!("libnitrokey {}", library_version)
} else {
"an undetectable libnitrokey version".to_string()
};

format!("{}{} using {}", version, built_from, libnitrokey)
}

/// The context used when running the program.
Expand Down

0 comments on commit 25241ec

Please sign in to comment.