SPEC.md and ARCHITECTURE.md were sitting at the repo root alongside Cargo.toml/Makefile.toml/packages.d — moved both into docs/ (doubleo7 already does this for its own supplementary docs, so this matches an existing convention in the fleet rather than inventing a new one). Updated every doc-comment cross-reference across src/*.rs and Makefile.toml (23 references) to the new docs/SPEC.md / docs/ ARCHITECTURE.md paths. The two files' own cross-references to each other didn't need changing — they're still same-directory relative references. Also updated the project reference memory pointing at ARCHITECTURE.md's location, so it doesn't go stale pointing at a path that no longer exists. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
122 lines
4.2 KiB
Rust
122 lines
4.2 KiB
Rust
//! Post-build correctness check: runs the freshly built binary and
|
|
//! confirms it reports the version pkgwatch believes it just built. Not a
|
|
//! security control — see docs/SPEC.md > Verification trust tiers.
|
|
|
|
use crate::config::SanityCheck;
|
|
use anyhow::{Context, Result, bail};
|
|
use regex::Regex;
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
/// Runs `check.command` with `pkg_bin_dir` prepended to `PATH`, so it
|
|
/// exercises the binary pkgwatch just built (still sitting in makepkg's
|
|
/// package staging directory, not installed system-wide) rather than
|
|
/// whatever's already on the system. Confirms `check.version_regex`'s
|
|
/// capture group matches `expected_version`.
|
|
///
|
|
/// Correctness check only, not a security control — see docs/SPEC.md >
|
|
/// Verification trust tiers. Catches checker bugs and mangled/wrong-asset
|
|
/// downloads, not malicious releases.
|
|
pub fn run(check: &SanityCheck, pkg_bin_dir: &Path, expected_version: &str) -> Result<()> {
|
|
let path_env = format!(
|
|
"{}:{}",
|
|
pkg_bin_dir.display(),
|
|
std::env::var("PATH").unwrap_or_default()
|
|
);
|
|
let output = Command::new("sh")
|
|
.arg("-c")
|
|
.arg(&check.command)
|
|
.env("PATH", path_env)
|
|
.output()
|
|
.with_context(|| format!("running sanity check command '{}'", check.command))?;
|
|
if !output.status.success() {
|
|
bail!(
|
|
"sanity check command '{}' exited with {}: {}",
|
|
check.command,
|
|
output.status,
|
|
String::from_utf8_lossy(&output.stderr).trim()
|
|
);
|
|
}
|
|
|
|
let combined = format!(
|
|
"{}{}",
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
let re = Regex::new(&check.version_regex)
|
|
.with_context(|| format!("invalid version_regex '{}'", check.version_regex))?;
|
|
let found = re
|
|
.captures(&combined)
|
|
.and_then(|caps| caps.get(1))
|
|
.with_context(|| {
|
|
format!(
|
|
"version_regex '{}' did not match sanity check output: {combined:?}",
|
|
check.version_regex
|
|
)
|
|
})?
|
|
.as_str();
|
|
|
|
if found != expected_version {
|
|
bail!(
|
|
"sanity check reported version '{found}', pkgwatch built '{expected_version}' — mismatch"
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::test_support::write_executable_script as write_fake_binary;
|
|
|
|
#[test]
|
|
fn run_passes_when_reported_version_matches() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
write_fake_binary(dir.path(), "uv", "echo 'uv 0.12.15 (abc 2026-09-01)'");
|
|
|
|
let check = SanityCheck {
|
|
command: "uv --version".to_string(),
|
|
version_regex: r"uv (\d+\.\d+\.\d+)".to_string(),
|
|
};
|
|
assert!(run(&check, dir.path(), "0.12.15").is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn run_fails_when_reported_version_differs() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
write_fake_binary(dir.path(), "uv", "echo 'uv 0.12.14 (abc 2026-08-01)'");
|
|
|
|
let check = SanityCheck {
|
|
command: "uv --version".to_string(),
|
|
version_regex: r"uv (\d+\.\d+\.\d+)".to_string(),
|
|
};
|
|
let err = run(&check, dir.path(), "0.12.15").unwrap_err();
|
|
assert!(err.to_string().contains("mismatch"));
|
|
}
|
|
|
|
#[test]
|
|
fn run_fails_when_command_exits_nonzero() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
write_fake_binary(dir.path(), "uv", "exit 1");
|
|
|
|
let check = SanityCheck {
|
|
command: "uv --version".to_string(),
|
|
version_regex: r"uv (\d+\.\d+\.\d+)".to_string(),
|
|
};
|
|
let err = run(&check, dir.path(), "0.12.15").unwrap_err();
|
|
assert!(err.to_string().contains("exited with"));
|
|
}
|
|
|
|
#[test]
|
|
fn run_fails_when_output_does_not_match_regex() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
write_fake_binary(dir.path(), "uv", "echo 'not a version'");
|
|
|
|
let check = SanityCheck {
|
|
command: "uv --version".to_string(),
|
|
version_regex: r"uv (\d+\.\d+\.\d+)".to_string(),
|
|
};
|
|
let err = run(&check, dir.path(), "0.12.15").unwrap_err();
|
|
assert!(err.to_string().contains("did not match"));
|
|
}
|
|
}
|