use std::path::Path;
use speccoder_core::FullTier;
use crate::git::Git;
use crate::{attempts, sandbox, verify};
pub const PREFIX: &str = "refs/speccoder/full";
#[must_use]
pub fn run_once(project: &Path, wt_root: &Path) -> FullTier {
let config = verify::find_config(project)
.and_then(|root| std::fs::read_to_string(root.join(verify::config::FILE)).ok())
.unwrap_or_default();
let runner = match sandbox::parse(&config).and_then(|c| sandbox::runner(wt_root, &c)) {
Ok((r, _)) => r,
Err(e) => {
return FullTier {
note: Some(format!("the full tier could not run: {e}")),
..FullTier::default()
};
}
};
match verify::run_checks(project, Some(verify::config::Tier::Full), &runner) {
Ok(report) => FullTier {
ran: report.checks.iter().map(|c| c.name.clone()).collect(),
passed: report.passed,
failed: report
.checks
.iter()
.filter(|c| c.status.is_failure())
.map(|c| c.name.clone())
.collect(),
note: None,
},
Err(e) => FullTier {
passed: true,
note: Some(if e.starts_with("no applicable checks") {
"no full-tier checks declared".to_owned()
} else {
e
}),
..FullTier::default()
},
}
}
pub fn record(wt_root: &Path, feature: &str, tier: &FullTier) {
let git = Git::at(wt_root);
let (Ok(head), Ok(tree)) = (
git.line(&["rev-parse", "--verify", "HEAD^{commit}"]),
git.line(&["rev-parse", "--verify", "HEAD^{tree}"]),
) else {
return;
};
let outcome = if tier.passed { "passed" } else { "failed" };
let message = format!(
"speccoder: full tier {outcome}\n\nTier: full\nOutcome: {outcome}\nRan: {}\n{}",
tier.ran.join(", "),
if tier.failed.is_empty() {
String::new()
} else {
format!("Failed: {}\n", tier.failed.join(", "))
}
);
let mut args = attempts::IDENTITY.to_vec();
args.extend(["commit-tree", &tree, "-p", &head, "-m", &message]);
if let Ok(commit) = git.line(&args) {
let _ = git.run(&["update-ref", &format!("{PREFIX}/{feature}"), &commit]);
}
}
#[must_use]
pub fn read(repo_root: &Path, feature: &str, branch: &str) -> Option<FullTier> {
let git = Git::at(repo_root);
let reference = format!("{PREFIX}/{feature}");
let parent = git
.line(&["rev-parse", "--verify", &format!("{reference}^1")])
.ok()?;
let head = git
.line(&["rev-parse", "--verify", &format!("{branch}^{{commit}}")])
.ok()?;
if parent != head {
return None;
}
let body = git.line(&["log", "-1", "--format=%B", &reference]).ok()?;
let field = |key: &str| {
body.lines()
.find_map(|l| l.strip_prefix(key))
.map(|v| {
v.split(", ")
.filter(|s| !s.is_empty())
.map(str::to_owned)
.collect::<Vec<_>>()
})
.unwrap_or_default()
};
Some(FullTier {
ran: field("Ran: "),
passed: body.contains("\nOutcome: passed"),
failed: field("Failed: "),
note: None,
})
}