From 30e5abb6aa8478d87d3ef93a462a2a537f198ccc Mon Sep 17 00:00:00 2001 From: Brummel Date: Fri, 5 Jun 2026 00:10:42 +0200 Subject: [PATCH] feat(aura-cli): self-identifying RunManifest.commit via build.rs git capture RunManifest.commit read option_env!("AURA_COMMIT").unwrap_or("unknown"), so a normal build's manifest identity field was a bare placeholder -- C8's audit trail (this run = this commit) had nothing to point back at once runs are archived (C18 registry). Add crates/aura-cli/build.rs: at compile time it shells out to the `git` already in PATH -- `rev-parse HEAD`, plus a `-dirty` suffix when `status --porcelain` is non-empty -- and emits `cargo:rustc-env=AURA_COMMIT=`. The existing option_env! read is untouched and now picks it up. Chose shelling to git over a libgit crate to keep the zero-external-dependency commitment (C14/C18); the firewall crate is aura-ingest, and a build-time process call adds no runtime dep. No-git case (packaged source tree, no .git): build.rs returns early and swallows git errors, leaving AURA_COMMIT unset so "unknown" still holds. Staleness guard: build.rs emits rerun-if-changed on .git/HEAD and .git/logs/HEAD (logs/HEAD grows on every HEAD move incl. branch switch), so the captured sha is rebuilt when HEAD moves rather than going stale. Determinism (C1) is intact: AURA_COMMIT is fixed at compile time, so two runs of one build still produce byte-identical manifests. Two tests that pinned the old "unknown" placeholder are relaxed to the new structural contract (not scope creep -- they were the old contract): the cli_run.rs JSON-shape prefix no longer pins the commit value, and the main.rs unit test asserts commit is non-empty and stable across runs instead of equal to "unknown". The headline contract (manifest.commit contains the real HEAD sha) is pinned by run_manifest_commit_carries_ real_git_head (4552d0c). Verified: cargo test -p aura-cli green (5), clippy --all-targets -D warnings clean. closes #15 --- crates/aura-cli/build.rs | 53 ++++++++++++++++++++++++++++++++ crates/aura-cli/src/main.rs | 7 ++++- crates/aura-cli/tests/cli_run.rs | 5 ++- 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 crates/aura-cli/build.rs diff --git a/crates/aura-cli/build.rs b/crates/aura-cli/build.rs new file mode 100644 index 0000000..8247015 --- /dev/null +++ b/crates/aura-cli/build.rs @@ -0,0 +1,53 @@ +//! Capture the git HEAD sha at compile time and expose it as `AURA_COMMIT`, so +//! the binary's RunManifest is self-identifying (C8/C18 audit trail: this run = +//! this commit). When there is no git (e.g. a packaged source tree), `AURA_COMMIT` +//! is left unset and `main.rs`'s `option_env!(...).unwrap_or("unknown")` holds. +//! +//! Zero runtime/build dependency by commitment (C14/C18): we shell out to the +//! `git` already in PATH rather than pulling in a libgit crate. + +use std::path::Path; +use std::process::Command; + +fn main() { + // build.rs runs with CWD = the crate dir (crates/aura-cli); the repo root, + // which owns `.git`, is two levels up. + let repo_root = Path::new("../.."); + let git_dir = repo_root.join(".git"); + + if !git_dir.exists() { + // No git: emit nothing; the `"unknown"` fallback in main.rs holds. + return; + } + + // Re-run when HEAD moves or its working tree changes so AURA_COMMIT never + // goes stale across commits. `.git/logs/HEAD` grows on every HEAD move + // (commit, checkout, reset), covering branch switches without resolving the + // current branch's ref file by hand. + println!("cargo:rerun-if-changed=../../.git/HEAD"); + println!("cargo:rerun-if-changed=../../.git/logs/HEAD"); + + let head = run_git(repo_root, &["rev-parse", "HEAD"]); + let Some(head) = head else { return }; + + // Append `-dirty` when the working tree has uncommitted changes, so a run + // built off an unclean tree is not silently attributed to the clean HEAD. + let dirty = run_git(repo_root, &["status", "--porcelain"]) + .map(|s| !s.is_empty()) + .unwrap_or(false); + + let commit = if dirty { format!("{head}-dirty") } else { head }; + println!("cargo:rustc-env=AURA_COMMIT={commit}"); +} + +/// Run `git ` in `dir`, returning trimmed stdout on success, `None` on any +/// failure (git missing, non-zero exit, non-utf8). Never panics: a build must +/// not fail because git is unavailable. +fn run_git(dir: &Path, args: &[&str]) -> Option { + let out = Command::new("git").current_dir(dir).args(args).output().ok()?; + if !out.status.success() { + return None; + } + let s = String::from_utf8(out.stdout).ok()?; + Some(s.trim().to_string()) +} diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index 3de1f1d..8e8c66c 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -156,6 +156,11 @@ mod tests { // manifest carries the sample's known configuration. let (from, to) = r1.manifest.window; assert_eq!((from.0, to.0), (1, 7)); - assert_eq!(r1.manifest.commit, "unknown"); + // commit is the build's git identity (or the no-git "unknown" fallback); + // either way it is non-empty and fixed at compile time, so it is stable + // across runs of the same build (C1 determinism, already asserted above + // via `to_json()`). + assert!(!r1.manifest.commit.is_empty()); + assert_eq!(r1.manifest.commit, r2.manifest.commit); } } diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index c76f8cd..012d290 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -18,7 +18,10 @@ fn run_prints_json_and_exits_zero() { let line = stdout.trim_end(); // canonical cycle-0009 JSON shape: nested manifest + metrics, stable keys. - assert!(line.starts_with("{\"manifest\":{\"commit\":\"unknown\","), "got: {line}"); + // The manifest leads with a `commit` field; its value is the build's git + // identity (asserted by `run_manifest_commit_carries_real_git_head`), so + // this shape check pins only the surrounding structure, not the value. + assert!(line.starts_with("{\"manifest\":{\"commit\":\""), "got: {line}"); assert!(line.contains("\"broker\":\"sim-optimal(pip_size=0.0001)\""), "got: {line}"); assert!(line.contains("\"window\":[1,7]"), "got: {line}"); assert!(line.contains("\"metrics\":{\"total_pips\":"), "got: {line}");