feat(nbd): add --version flag with X.Y.Z+GitSha format [c24ee8]

build.rs captures the short git SHA at compile time via
`git rev-parse --short HEAD` and emits it as GIT_SHORT_SHA. Falls back
to "unknown" when git is unavailable (e.g. Nix sandboxed builds).

main.rs adds a VERSION const (`env!("CARGO_PKG_VERSION") + "+" + SHA`)
and passes it to clap's `version =` attribute, enabling both -V and
--version flags.

Example output: `nbd 0.1.0+8f4d25b`

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
quotesdb
Elijah Voigt 3 months ago
parent 8f4d25b141
commit 16635a908d

@ -220,6 +220,7 @@ cargo install --path .
```sh
# From the nbd/ directory
cargo run -- --version # prints e.g. nbd 0.1.0+7e311d6
cargo run -- init
cargo run -- create --title "Test ticket" --priority 7 --type bug
cargo run -- list

@ -0,0 +1,31 @@
//! Build script for `nbd`.
//!
//! Captures the short git SHA at compile time and emits it as the
//! `GIT_SHORT_SHA` environment variable, making it available via
//! `env!("GIT_SHORT_SHA")` in the crate source.
//!
//! Falls back to `"unknown"` when git is unavailable (e.g. a clean Nix
//! sandbox build where `.git/` is not present).
fn main() {
// Capture the short git SHA at build time.
let sha = std::process::Command::new("git")
.args(["rev-parse", "--short", "HEAD"])
.output()
.ok()
.and_then(|o| {
if o.status.success() {
Some(o.stdout)
} else {
None
}
})
.and_then(|b| String::from_utf8(b).ok())
.map(|s| s.trim().to_string())
.unwrap_or_else(|| "unknown".to_string());
println!("cargo:rustc-env=GIT_SHORT_SHA={sha}");
// Re-run whenever HEAD or any ref changes (new commits, branch switches).
println!("cargo:rerun-if-changed=.git/HEAD");
println!("cargo:rerun-if-changed=.git/refs");
}

@ -19,6 +19,12 @@ mod ticket;
/// ```
const CLAUDE_MD_SNIPPET: &str = include_str!("claude_md_snippet.md");
/// Full version string embedded at compile time: `"X.Y.Z+shortsha"`.
///
/// The semver comes from `Cargo.toml` via `CARGO_PKG_VERSION`; the short SHA
/// is injected by `build.rs` via `GIT_SHORT_SHA`.
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "+", env!("GIT_SHORT_SHA"));
#[cfg(test)]
mod tests;
@ -39,7 +45,7 @@ use crate::ticket::{generate_id, validate_priority, Status, Ticket, TicketType};
/// ancestor directory that contains a `.nbd/` folder, discovered by traversing
/// upward from the current working directory (like `git` finds `.git/`).
#[derive(Parser)]
#[command(name = "nbd", about = "Manage work tickets for agent workflows")]
#[command(name = "nbd", about = "Manage work tickets for agent workflows", version = VERSION)]
struct Cli {
/// Output machine-readable JSON instead of a human-readable table.
#[arg(long, global = true)]

@ -2158,3 +2158,33 @@ fn update_no_changes_prints_no_changes() {
"should print '(no changes)' when nothing changed: {stdout}"
);
}
// ── nbd --version tests ───────────────────────────────────────────────────────
/// `nbd --version` exits 0 and stdout contains the semver and a git SHA.
#[test]
fn version_flag_exits_zero_with_semver() {
let tmp = tempfile::tempdir().expect("tempdir");
let output = std::process::Command::new(env!("CARGO_BIN_EXE_nbd"))
.arg("--version")
.current_dir(tmp.path())
.output()
.expect("failed to spawn nbd");
assert!(
output.status.success(),
"--version should exit 0, stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8(output.stdout).unwrap();
// Should contain the package version (semver).
assert!(
stdout.contains("0.1.0"),
"--version should include semver: {stdout}"
);
// Should contain a '+' separator between semver and git SHA.
assert!(
stdout.contains('+'),
"--version should contain '+' separator: {stdout}"
);
}

Loading…
Cancel
Save