chore(nbd): triage --version flag ticket [c24ee8]
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>quotesdb
parent
1d21a481cb
commit
9d63d76198
@ -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+<sha>
|
||||||
|
```
|
||||||
Loading…
Reference in New Issue