Invalidate stale build provenance
This commit is contained in:
parent
07351a709f
commit
3e367c99cd
4 changed files with 84 additions and 11 deletions
|
|
@ -1,30 +1,63 @@
|
|||
use std::process::Command;
|
||||
use std::{env, path::Path, process::Command};
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-env-changed=GHOSTLIGHT_BUILD_COMMIT");
|
||||
println!("cargo:rerun-if-changed=../../.git/HEAD");
|
||||
let manifest_dir =
|
||||
env::var("CARGO_MANIFEST_DIR").expect("Cargo must supply CARGO_MANIFEST_DIR");
|
||||
let repo_root = Path::new(&manifest_dir).join("../..");
|
||||
emit_git_rerun_paths(&repo_root);
|
||||
// Release tooling injects the clean-tree commit. Git discovery is only a
|
||||
// convenience for local builds because Cargo cannot reliably observe a
|
||||
// moving branch ref through `.git/HEAD` alone.
|
||||
let commit = std::env::var("GHOSTLIGHT_BUILD_COMMIT")
|
||||
// convenience for local builds. The exact ref and reflog dependencies above
|
||||
// keep that convenience from silently publishing stale provenance.
|
||||
let commit = env::var("GHOSTLIGHT_BUILD_COMMIT")
|
||||
.ok()
|
||||
.filter(|value| is_commit(value))
|
||||
.or_else(git_commit)
|
||||
.or_else(|| git_commit(&repo_root))
|
||||
.expect("GhostlightDungeon builds require an exact 40-hex source commit");
|
||||
println!("cargo:rustc-env=GHOSTLIGHT_BUILD_COMMIT={commit}");
|
||||
}
|
||||
|
||||
fn git_commit() -> Option<String> {
|
||||
fn emit_git_rerun_paths(repo_root: &Path) {
|
||||
for git_path in ["HEAD", "logs/HEAD", "packed-refs"] {
|
||||
if let Some(path) = git_output(repo_root, &["rev-parse", "--git-path", git_path]) {
|
||||
emit_rerun_path(repo_root, &path);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(reference) = git_output(repo_root, &["symbolic-ref", "-q", "HEAD"])
|
||||
&& let Some(path) = git_output(repo_root, &["rev-parse", "--git-path", &reference])
|
||||
{
|
||||
emit_rerun_path(repo_root, &path);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_rerun_path(repo_root: &Path, git_path: &str) {
|
||||
let path = Path::new(git_path);
|
||||
let resolved = if path.is_absolute() {
|
||||
path.to_path_buf()
|
||||
} else {
|
||||
repo_root.join(path)
|
||||
};
|
||||
println!("cargo:rerun-if-changed={}", resolved.display());
|
||||
}
|
||||
|
||||
fn git_commit(repo_root: &Path) -> Option<String> {
|
||||
git_output(repo_root, &["rev-parse", "HEAD"]).filter(|value| is_commit(value))
|
||||
}
|
||||
|
||||
fn git_output(repo_root: &Path, args: &[&str]) -> Option<String> {
|
||||
let output = Command::new("git")
|
||||
.args(["rev-parse", "HEAD"])
|
||||
.current_dir("../..")
|
||||
.args(args)
|
||||
.current_dir(repo_root)
|
||||
.output()
|
||||
.ok()?;
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
let value = String::from_utf8(output.stdout).ok()?.trim().to_owned();
|
||||
is_commit(&value).then_some(value)
|
||||
String::from_utf8(output.stdout)
|
||||
.ok()
|
||||
.map(|value| value.trim().to_owned())
|
||||
.filter(|value| !value.is_empty())
|
||||
}
|
||||
|
||||
fn is_commit(value: &str) -> bool {
|
||||
|
|
|
|||
22
crates/ghostlight-dungeon/tests/build_provenance.rs
Normal file
22
crates/ghostlight-dungeon/tests/build_provenance.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
use std::{path::Path, process::Command};
|
||||
|
||||
#[test]
|
||||
fn embedded_commit_matches_checkout_head() {
|
||||
let repo_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
|
||||
let output = Command::new("git")
|
||||
.args(["rev-parse", "HEAD"])
|
||||
.current_dir(repo_root)
|
||||
.output()
|
||||
.expect("git must be available for the provenance regression test");
|
||||
assert!(output.status.success(), "git rev-parse HEAD failed");
|
||||
|
||||
let checkout_head = String::from_utf8(output.stdout)
|
||||
.expect("git commit must be UTF-8")
|
||||
.trim()
|
||||
.to_owned();
|
||||
assert_eq!(
|
||||
env!("GHOSTLIGHT_BUILD_COMMIT"),
|
||||
checkout_head,
|
||||
"the binary embedded stale Git provenance"
|
||||
);
|
||||
}
|
||||
|
|
@ -758,6 +758,14 @@ atomic pointer switch, recorded in CultCache, and the rollback runbook lives in
|
|||
Task Scheduler may be added when tester availability makes that useful; native
|
||||
Windows service machinery is outside the MVP.
|
||||
|
||||
The binary owns its build provenance. Release tooling injects the clean-tree
|
||||
commit through `GHOSTLIGHT_BUILD_COMMIT`; local builds derive it from Git while
|
||||
the build script watches `HEAD`, its symbolic branch ref, the HEAD reflog, and
|
||||
packed refs. The health projection only publishes that compile-time value.
|
||||
Launchers and manifests may verify it but may not rewrite it. Activation is
|
||||
rejected unless checkout HEAD, embedded commit, immutable manifest, binary
|
||||
hash, and the running health projection agree.
|
||||
|
||||
Live VoidBot integration uses the independently verified Yggdrasil host and the
|
||||
restored Starfire loopback crossing at `127.0.0.1:17875/mcp`. The repaired
|
||||
tunnel task uses the live `F:\Projects` body. Retired local VoidBot/Qdrant
|
||||
|
|
|
|||
|
|
@ -23,6 +23,16 @@ Vault evidence receipts + typed campaign snapshot
|
|||
Ghostlight owns the generalized projection organ. Epiphany and other consumers
|
||||
own their canonical Persona state and consequence commits.
|
||||
|
||||
Build provenance has one owner: `crates/ghostlight-dungeon/build.rs` binds the
|
||||
exact source commit into the binary. Its inputs are either the release tool's
|
||||
explicit clean-tree commit or Git HEAD; it watches the actual symbolic ref,
|
||||
HEAD reflog, and packed refs so an ordinary commit invalidates Cargo's cached
|
||||
value. Health derives its commit from that embedded value. Release manifests,
|
||||
launch scripts, and CultCache deployment receipts are verifiers and records,
|
||||
not alternate writers. Local runs and immutable releases use this same binding,
|
||||
and the regression test compares it to the checkout's live `git rev-parse
|
||||
HEAD`. A mismatch blocks activation rather than being repaired after launch.
|
||||
|
||||
Away-time agency follows a separate proposal/commit seam:
|
||||
|
||||
```text
|
||||
|
|
|
|||
Loading…
Reference in a new issue