Merge v3-t78: the test bundle cache is keyed on its source inputs (T78)
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
//! Shared fixture copies and builds. A mutex protects cwd-init and the brush checkout.
|
||||
use std::{
|
||||
hash::{Hash, Hasher},
|
||||
os::unix::{ffi::OsStrExt, fs::PermissionsExt},
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, ExitStatus},
|
||||
sync::{Mutex, OnceLock},
|
||||
@@ -31,6 +33,79 @@ pub fn target_dir() -> PathBuf {
|
||||
})
|
||||
}
|
||||
|
||||
/// Fingerprint the snapshot inputs and their current working-tree contents.
|
||||
pub fn bundle_key(repo: &Path) -> anyhow::Result<String> {
|
||||
const INPUTS: &[&str] = &[
|
||||
"host",
|
||||
"vfs",
|
||||
"userland",
|
||||
"agents",
|
||||
"skills",
|
||||
"extensions",
|
||||
"Cargo.toml",
|
||||
"Cargo.lock",
|
||||
"flake.nix",
|
||||
"flake.lock",
|
||||
"README.md",
|
||||
"AGENTS.md",
|
||||
".gitignore",
|
||||
];
|
||||
let tree = Command::new("git")
|
||||
.current_dir(repo)
|
||||
.args(["ls-tree", "HEAD", "--"])
|
||||
.args(INPUTS)
|
||||
.output()?;
|
||||
anyhow::ensure!(
|
||||
tree.status.success(),
|
||||
"git ls-tree: {}",
|
||||
String::from_utf8_lossy(&tree.stderr)
|
||||
);
|
||||
let files = Command::new("git")
|
||||
.current_dir(repo)
|
||||
.args([
|
||||
"ls-files",
|
||||
"-z",
|
||||
"--cached",
|
||||
"--others",
|
||||
"--exclude-standard",
|
||||
"--",
|
||||
])
|
||||
.args(INPUTS)
|
||||
.output()?;
|
||||
anyhow::ensure!(
|
||||
files.status.success(),
|
||||
"git ls-files: {}",
|
||||
String::from_utf8_lossy(&files.stderr)
|
||||
);
|
||||
let mut hash = std::collections::hash_map::DefaultHasher::new();
|
||||
tree.stdout.hash(&mut hash);
|
||||
let mut names: Vec<_> = files
|
||||
.stdout
|
||||
.split(|byte| *byte == 0)
|
||||
.filter(|name| !name.is_empty())
|
||||
.collect();
|
||||
names.sort_unstable();
|
||||
for name in names {
|
||||
name.hash(&mut hash);
|
||||
let path = repo.join(std::ffi::OsStr::from_bytes(name));
|
||||
match std::fs::symlink_metadata(&path) {
|
||||
Ok(metadata) => {
|
||||
metadata.permissions().mode().hash(&mut hash);
|
||||
if metadata.file_type().is_symlink() {
|
||||
std::fs::read_link(path)?.hash(&mut hash);
|
||||
} else {
|
||||
std::fs::read(path)?.hash(&mut hash);
|
||||
}
|
||||
}
|
||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
|
||||
"deleted".hash(&mut hash);
|
||||
}
|
||||
Err(error) => return Err(error.into()),
|
||||
}
|
||||
}
|
||||
Ok(format!("{:016x}\n", hash.finish()))
|
||||
}
|
||||
|
||||
pub fn cwd_init() {
|
||||
static INIT: OnceLock<()> = OnceLock::new();
|
||||
INIT.get_or_init(|| {
|
||||
|
||||
@@ -7,9 +7,15 @@ fn bundle() -> anyhow::Result<PathBuf> {
|
||||
let repo = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..");
|
||||
let bundle = crate::common::target_dir().join("bundle");
|
||||
let _guard = crate::common::BUILD.lock().unwrap();
|
||||
let key = crate::common::bundle_key(&repo)?;
|
||||
let stamp = bundle.join(".source-key");
|
||||
if !bundle.join("bin/sh.wasm").exists()
|
||||
|| !bundle.join("src/sausage/agents/models.json").exists()
|
||||
|| std::fs::read_to_string(&stamp).ok().as_deref() != Some(key.as_str())
|
||||
{
|
||||
if stamp.exists() {
|
||||
std::fs::remove_file(&stamp)?;
|
||||
}
|
||||
assert!(
|
||||
std::process::Command::new("nu")
|
||||
.arg(repo.join("userland/bundle.nu"))
|
||||
@@ -17,6 +23,7 @@ fn bundle() -> anyhow::Result<PathBuf> {
|
||||
.status()?
|
||||
.success()
|
||||
);
|
||||
std::fs::write(stamp, key)?;
|
||||
}
|
||||
Ok(bundle)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user