crates/nornir-viz-web/build.rs
// build.rs — stamp the commit identity into the `nornir-viz-web` binary, PURE
// `std::fs` (NO `Command::new("git")`), per nornir's pure-Rust / zero-shell law.
//
// This crate is NOT a member of the main `nornir` crate, so it cannot read
// `nornir::VERSION_LINE`; it stamps its OWN `NORNIR_VIZ_WEB_GIT_HASH` the same
// way nornir's own `build.rs` does — read `.git/HEAD` + the resolved ref off disk
// (handling a symbolic/detached HEAD, `packed-refs`, and linked worktrees). The
// only twist vs. nornir's build.rs: this crate lives at `nornir/crates/…`, so the
// git dir is up the tree, not in the crate's own CWD — `find_git_dir` walks up
// from `CARGO_MANIFEST_DIR` to locate it. Emitted UNCONDITIONALLY, every build.
use std::path::{Path, PathBuf};
fn main() {
let hash = git_short_hash().unwrap_or_else(|| "unknown".to_string());
println!("cargo:rustc-env=NORNIR_VIZ_WEB_GIT_HASH={hash}");
println!("cargo:rerun-if-changed=build.rs");
}
/// The short (7-char) commit hash, read from git metadata on disk. `None` off a
/// git checkout.
fn git_short_hash() -> Option<String> {
let (gitdir, commondir) = resolve_git_dirs()?;
println!("cargo:rerun-if-changed={}", gitdir.join("HEAD").display());
let head = std::fs::read_to_string(gitdir.join("HEAD")).ok()?;
let head = head.trim();
let full = if let Some(ref_name) = head.strip_prefix("ref: ") {
resolve_ref(ref_name.trim(), &gitdir, &commondir)?
} else {
head.to_string()
};
let full = full.trim();
if full.len() < 7 || !full.bytes().all(|b| b.is_ascii_hexdigit()) {
return None;
}
Some(full[..7].to_string())
}
/// Walk up from `CARGO_MANIFEST_DIR` to the first ancestor holding a `.git`
/// entry (dir for a normal checkout, file for a linked worktree). Returns the
/// resolved `(gitdir, commondir)`.
fn resolve_git_dirs() -> Option<(PathBuf, PathBuf)> {
let start = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR")?);
let dot_git = start
.ancestors()
.map(|p| p.join(".git"))
.find(|p| p.exists())?;
let meta = std::fs::metadata(&dot_git).ok()?;
let gitdir = if meta.is_dir() {
dot_git
} else {
println!("cargo:rerun-if-changed={}", dot_git.display());
let contents = std::fs::read_to_string(&dot_git).ok()?;
PathBuf::from(contents.trim().strip_prefix("gitdir:")?.trim())
};
let commondir = match std::fs::read_to_string(gitdir.join("commondir")) {
Ok(rel) => gitdir.join(rel.trim()),
Err(_) => gitdir.clone(),
};
Some((gitdir, commondir))
}
/// Resolve a symbolic ref to an object id: loose ref file (gitdir, then common
/// dir), then `packed-refs`.
fn resolve_ref(ref_name: &str, gitdir: &Path, commondir: &Path) -> Option<String> {
for base in [gitdir, commondir] {
let loose = base.join(ref_name);
println!("cargo:rerun-if-changed={}", loose.display());
if let Ok(s) = std::fs::read_to_string(&loose) {
let s = s.trim();
if !s.is_empty() && !s.starts_with("ref: ") {
return Some(s.to_string());
}
}
}
let packed = commondir.join("packed-refs");
println!("cargo:rerun-if-changed={}", packed.display());
let packed = std::fs::read_to_string(&packed).ok()?;
for line in packed.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') || line.starts_with('^') {
continue;
}
if let Some((sha, name)) = line.split_once(' ') {
if name.trim() == ref_name {
return Some(sha.trim().to_string());
}
}
}
None
}