From 9d63d76198258906c334a4e1914b9ea97a025a17 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Mon, 23 Feb 2026 10:25:36 -0800 Subject: [PATCH] chore(nbd): triage --version flag ticket [c24ee8] Co-Authored-By: Claude Sonnet 4.6 --- nbd/.nbd/tickets/c24ee8.md | 126 +++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 nbd/.nbd/tickets/c24ee8.md diff --git a/nbd/.nbd/tickets/c24ee8.md b/nbd/.nbd/tickets/c24ee8.md new file mode 100644 index 0000000..69af156 --- /dev/null +++ b/nbd/.nbd/tickets/c24ee8.md @@ -0,0 +1,126 @@ ++++ +title = "Add --version flag with X.Y.Z+GitSha format" +priority = 5 +status = "todo" +ticket_type = "feature" +dependencies = [] ++++ +## Goal + +Add a `--version` / `-V` flag to `nbd` that prints the version in the format: + +``` +0.1.0+7e311d6 +``` + +where `0.1.0` comes from `Cargo.toml` and `7e311d6` is the short git SHA of the commit the binary was built from, embedded at compile time via `build.rs`. + +## Why + +Allows agents and users to confirm exactly which build of `nbd` is running without inspecting the binary separately. + +## Implementation plan + +### 1. Create `build.rs` at the crate root + +```rust +fn main() { + // Capture the short git SHA at build time. + // Falls back to "unknown" when git is unavailable (e.g. CI without repo). + 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 changes (new commits). + println\!("cargo:rerun-if-changed=.git/HEAD"); + println\!("cargo:rerun-if-changed=.git/refs"); +} +``` + +Key points: +- Uses `std::process::Command` — no extra build dependencies. +- Graceful fallback to `"unknown"` when git is absent (clean Nix sandbox builds may not have `.git/`). +- `rerun-if-changed` directives ensure the SHA is refreshed on every commit without forcing a full rebuild every run. + +### 2. Add a `VERSION` constant in `src/main.rs` + +Add near the top of `main.rs` after the existing `CLAUDE_MD_SNIPPET` constant: + +```rust +/// Full version string embedded at compile time: `"X.Y.Z+shortsha"`. +const VERSION: &str = concat\!(env\!("CARGO_PKG_VERSION"), "+", env\!("GIT_SHORT_SHA")); +``` + +### 3. Wire `VERSION` into the clap `#[command(...)]` attribute + +Change: + +```rust +#[command(name = "nbd", about = "Manage work tickets for agent workflows")] +``` + +to: + +```rust +#[command(name = "nbd", about = "Manage work tickets for agent workflows", version = VERSION)] +``` + +clap automatically handles `-V` / `--version` when `version` is set: it prints the string and exits 0. No manual subcommand or dispatch code needed. + +### 4. Update `README.md` + +Add a brief note in the **Usage** section or **Installation** section: + +```sh +nbd --version # prints e.g. 0.1.0+7e311d6 +``` + +### 5. Add an integration test in `tests/integration.rs` + +```rust +/// `nbd --version` exits 0 and stdout contains the semver. +#[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("spawn nbd"); + assert\!(output.status.success(), "--version should exit 0"); + 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 and at least one hex char after it. + assert\!(stdout.contains('+'), "--version should contain '+': {stdout}"); +} +``` + +## Files to change + +| File | Change | +|---|---| +| `build.rs` | **Create** — emit `GIT_SHORT_SHA` env var | +| `src/main.rs` | Add `VERSION` const; add `version = VERSION` to `#[command]` | +| `README.md` | Add `nbd --version` example | +| `tests/integration.rs` | Add `version_flag_exits_zero_with_semver` test | + +## Edge cases + +- **No git available (Nix sandbox):** `build.rs` falls back to `"unknown"`, producing `0.1.0+unknown`. This is acceptable for packaged builds where the version is already pinned by the derivation. +- **clap version output format:** clap 4 prints `nbd X.Y.Z+sha` (prefixed by the binary name) to stdout, then exits 0. This is standard behaviour — the test should check `stdout.contains("0.1.0")` rather than an exact match. +- **No `--json` support for `--version`:** clap intercepts `--version` before dispatch reaches the `--json` handling. This is acceptable; version output is always plain text. + +## Validation + +```sh +cargo fmt && cargo check && cargo clippy && cargo test +cargo run -- --version +# Expected output: nbd 0.1.0+ +``` \ No newline at end of file