use std::env;
use std::fs;
use std::path::Path;
use std::process::{Command, ExitCode};
use zccache::ci::{reap_orphan_daemons, resolve_timeout, StageOutcome, StageRunner};
use zccache::core::NormalizedPath;
fn project_root() -> NormalizedPath {
let current = env::current_dir().expect("cannot determine working directory");
let mut dir = current.as_path();
loop {
if dir.join("Cargo.toml").exists() {
if let Ok(content) = fs::read_to_string(dir.join("Cargo.toml")) {
if content.contains("[workspace]") {
return dir.into();
}
}
}
dir = match dir.parent() {
Some(p) => p,
None => return current.into(),
};
}
}
fn current_fingerprint(root: &Path) -> Option<String> {
let output = Command::new("git")
.args(["status", "--porcelain"])
.current_dir(root)
.output()
.ok()?;
if !output.status.success() {
return None;
}
let stdout = String::from_utf8_lossy(&output.stdout);
if stdout.trim().is_empty() {
return None;
}
Some(format!("{:x}", md5::compute(stdout.as_bytes())))
}
fn session_fingerprint(root: &Path) -> Option<String> {
let path = root.join(".cache").join("session_fingerprint.json");
let content = fs::read_to_string(path).ok()?;
let v: serde_json::Value = serde_json::from_str(&content).ok()?;
v.get("fingerprint")?.as_str().map(String::from)
}
fn check_level(root: &Path) -> CheckLevel {
match current_fingerprint(root) {
None => CheckLevel::Skip, Some(current) => match session_fingerprint(root) {
None => CheckLevel::Full, Some(session) if current == session => CheckLevel::QuickCheck, Some(_) => CheckLevel::Full, },
}
}
#[derive(Debug, PartialEq)]
enum CheckLevel {
Skip,
QuickCheck,
Full,
}
fn activate_rustup_toolchain(root: &Path) {
let project_cargo_home = root.join(".cargo");
let project_rustup_home = root.join(".rustup");
env::set_var("CARGO_HOME", &project_cargo_home);
env::set_var("RUSTUP_HOME", &project_rustup_home);
let mut candidates: Vec<NormalizedPath> =
vec![NormalizedPath::new(project_cargo_home.join("bin"))];
if let Some(home) = if cfg!(windows) {
env::var("USERPROFILE").ok()
} else {
env::var("HOME").ok()
} {
candidates.push(NormalizedPath::new(
Path::new(&home).join(".cargo").join("bin"),
));
}
for cargo_bin in candidates {
if cargo_bin.is_dir() {
let sep = if cfg!(windows) { ";" } else { ":" };
let current = env::var("PATH").unwrap_or_default();
env::set_var("PATH", format!("{}{sep}{current}", cargo_bin.display()));
break;
}
}
}
fn is_target_installed(target: &str) -> bool {
Command::new("rustup")
.args(["target", "list", "--installed"])
.output()
.map(|o| {
o.status.success()
&& String::from_utf8_lossy(&o.stdout)
.lines()
.any(|line| line.trim() == target)
})
.unwrap_or(false)
}
fn build_cmd(root: &Path, program: &str, args: &[&str]) -> Command {
use std::process::Stdio;
let mut c = Command::new(program);
c.args(args)
.current_dir(root)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());
c
}
fn handle_outcome(stage: &str, outcome: StageOutcome) -> Option<ExitCode> {
match outcome {
StageOutcome::Exited(0) => None,
StageOutcome::Exited(code) => {
eprintln!("{stage} failed (exit {code})");
Some(ExitCode::from(2))
}
StageOutcome::GlobalTimeout => {
eprintln!("{stage} hit the wall-clock timeout — failing");
Some(ExitCode::from(2))
}
StageOutcome::SpawnFailed => {
eprintln!("{stage} could not be spawned — failing");
Some(ExitCode::from(2))
}
}
}
fn main() -> ExitCode {
let root = project_root();
let level = check_level(&root);
if level == CheckLevel::Skip {
eprintln!("Skipping stop checks (no uncommitted changes)");
return ExitCode::SUCCESS;
}
activate_rustup_toolchain(&root);
let _ = reap_orphan_daemons();
let timeout = resolve_timeout();
let mut runner = StageRunner::new(timeout);
if level == CheckLevel::QuickCheck {
eprintln!("Pre-existing dirty files — running cargo check");
let mut cmd = build_cmd(
&root,
"soldr",
&["cargo", "check", "--workspace", "--all-targets"],
);
let outcome = runner.run("quick-check", &mut cmd);
if let Some(code) = handle_outcome("quick-check", outcome) {
return code;
}
runner.finish();
eprintln!("Quick check passed");
return ExitCode::SUCCESS;
}
eprintln!("Running full workspace checks (changes detected)");
let mut lint_cmd = build_cmd(&root, "uv", &["run", "python", "-m", "ci.lint", "--fix"]);
let lint_outcome = runner.run("lint", &mut lint_cmd);
if let Some(code) = handle_outcome("lint", lint_outcome) {
return code;
}
if cfg!(windows) {
let target = "x86_64-unknown-linux-musl";
if is_target_installed(target) {
let mut xcheck_cmd = build_cmd(
&root,
"soldr",
&[
"cargo",
"check",
"--target",
target,
"--workspace",
"--all-targets",
],
);
let xcheck_outcome = runner.run("cross-check-linux", &mut xcheck_cmd);
if let Some(code) = handle_outcome("cross-check-linux", xcheck_outcome) {
return code;
}
} else {
eprintln!(
"Skipping cross-compile check: target {target} not installed. \
Run `rustup target add {target}` to enable."
);
}
}
let mut test_cmd = build_cmd(
&root,
"soldr",
&[
"cargo",
"test",
"--workspace",
"--lib",
"--exclude",
"zccache-daemon",
],
);
let test_outcome = runner.run("test", &mut test_cmd);
if let Some(code) = handle_outcome("test", test_outcome) {
return code;
}
runner.finish();
eprintln!("All checks passed");
ExitCode::SUCCESS
}