huskies: merge 861
This commit is contained in:
@@ -212,33 +212,6 @@ fn run_command_with_timeout(
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the documentation coverage gate for the given worktree.
|
||||
///
|
||||
/// Invokes `cargo run --quiet -p source-map-gen --bin source-map-check` in
|
||||
/// `path`, comparing the current branch against its detected base branch
|
||||
/// (`master` or `main`). Returns `(passed, combined_output)`.
|
||||
pub(crate) fn run_doc_coverage_gate(path: &Path) -> Result<(bool, String), String> {
|
||||
let base = detect_worktree_base_branch(path);
|
||||
let base_str = base.as_str();
|
||||
run_command_with_timeout(
|
||||
"cargo",
|
||||
&[
|
||||
"run",
|
||||
"--quiet",
|
||||
"-p",
|
||||
"source-map-gen",
|
||||
"--bin",
|
||||
"source-map-check",
|
||||
"--",
|
||||
"--worktree",
|
||||
".",
|
||||
"--base",
|
||||
base_str,
|
||||
],
|
||||
path,
|
||||
)
|
||||
}
|
||||
|
||||
/// Run `cargo clippy` and the project test suite (via `script/test` if present,
|
||||
/// otherwise `cargo nextest run` / `cargo test`) in the given directory.
|
||||
/// Returns `(gates_passed, combined_output)`.
|
||||
@@ -268,19 +241,6 @@ pub(crate) fn run_acceptance_gates(path: &Path) -> Result<(bool, String), String
|
||||
return Ok((false, test_out));
|
||||
}
|
||||
|
||||
// Doc coverage gate (defence in depth): verify all public items in files
|
||||
// changed since the base branch have doc comments. This gate catches
|
||||
// projects that do not include source-map-check in their script/test, and
|
||||
// provides an actionable "Doc coverage gate failed:" preamble so the agent
|
||||
// knows exactly what to fix on retry.
|
||||
let (doc_ok, doc_out) = run_doc_coverage_gate(path)?;
|
||||
if !doc_ok {
|
||||
return Ok((
|
||||
false,
|
||||
format!("{test_out}Doc coverage gate failed:\n{doc_out}"),
|
||||
));
|
||||
}
|
||||
|
||||
Ok((true, test_out))
|
||||
}
|
||||
|
||||
@@ -599,181 +559,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
// ── run_acceptance_gates doc-coverage tests ───────────────────────────────
|
||||
|
||||
/// Create a git worktree from the actual huskies workspace on a fresh
|
||||
/// branch (`<branch>`) rooted at `master`. Returns the path to the
|
||||
/// worktree. Call `cleanup_worktree` when done.
|
||||
#[cfg(unix)]
|
||||
fn create_test_worktree(branch: &str) -> std::path::PathBuf {
|
||||
let workspace = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_path_buf();
|
||||
let wt = std::env::temp_dir().join(branch.replace('/', "_"));
|
||||
// Remove any stale worktree from a previous failed run.
|
||||
let _ = Command::new("git")
|
||||
.args(["worktree", "remove", "--force", wt.to_str().unwrap()])
|
||||
.current_dir(&workspace)
|
||||
.output();
|
||||
let _ = Command::new("git")
|
||||
.args(["branch", "-D", branch])
|
||||
.current_dir(&workspace)
|
||||
.output();
|
||||
|
||||
let out = Command::new("git")
|
||||
.args([
|
||||
"worktree",
|
||||
"add",
|
||||
wt.to_str().unwrap(),
|
||||
"-b",
|
||||
branch,
|
||||
"master",
|
||||
])
|
||||
.current_dir(&workspace)
|
||||
.output()
|
||||
.expect("git worktree add");
|
||||
assert!(
|
||||
out.status.success(),
|
||||
"git worktree add failed: {}",
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
wt
|
||||
}
|
||||
|
||||
/// Remove the test worktree and its branch from the workspace.
|
||||
#[cfg(unix)]
|
||||
fn cleanup_worktree(branch: &str, wt: &std::path::Path) {
|
||||
let workspace = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_path_buf();
|
||||
let _ = Command::new("git")
|
||||
.args(["worktree", "remove", "--force", wt.to_str().unwrap()])
|
||||
.current_dir(&workspace)
|
||||
.output();
|
||||
let _ = Command::new("git")
|
||||
.args(["branch", "-D", branch])
|
||||
.current_dir(&workspace)
|
||||
.output();
|
||||
}
|
||||
|
||||
/// Write a `.cargo/config.toml` inside `wt` that redirects the build
|
||||
/// target directory to the shared workspace `target/` so `cargo run`
|
||||
/// finds the already-compiled `source-map-check` binary without
|
||||
/// recompiling.
|
||||
#[cfg(unix)]
|
||||
fn set_shared_target(wt: &std::path::Path) {
|
||||
use std::fs;
|
||||
let workspace = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_path_buf();
|
||||
let target_dir = workspace.join("target");
|
||||
let cargo_dir = wt.join(".cargo");
|
||||
fs::create_dir_all(&cargo_dir).unwrap();
|
||||
fs::write(
|
||||
cargo_dir.join("config.toml"),
|
||||
format!(
|
||||
"[build]\ntarget-dir = \"{}\"\n",
|
||||
target_dir.to_str().unwrap()
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Write and chmod a minimal `script/test` that exits 0 without running
|
||||
/// the full test suite. The file is left uncommitted so `run_project_tests`
|
||||
/// uses it but it does not appear in the doc-check diff.
|
||||
#[cfg(unix)]
|
||||
fn write_fast_script_test(wt: &std::path::Path) {
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let script = wt.join("script").join("test");
|
||||
fs::write(&script, "#!/usr/bin/env bash\nexit 0\n").unwrap();
|
||||
let mut perms = fs::metadata(&script).unwrap().permissions();
|
||||
perms.set_mode(0o755);
|
||||
fs::set_permissions(&script, perms).unwrap();
|
||||
}
|
||||
|
||||
/// Commit a single Rust file in the worktree.
|
||||
#[cfg(unix)]
|
||||
fn commit_file(wt: &std::path::Path, rel_path: &str, content: &str) {
|
||||
use std::fs;
|
||||
let full = wt.join(rel_path);
|
||||
if let Some(parent) = full.parent() {
|
||||
fs::create_dir_all(parent).unwrap();
|
||||
}
|
||||
fs::write(&full, content).unwrap();
|
||||
Command::new("git")
|
||||
.args(["add", rel_path])
|
||||
.current_dir(wt)
|
||||
.output()
|
||||
.unwrap();
|
||||
Command::new("git")
|
||||
.args([
|
||||
"-c",
|
||||
"user.email=test@test.com",
|
||||
"-c",
|
||||
"user.name=Test",
|
||||
"commit",
|
||||
"-m",
|
||||
"test: add file for doc gate test",
|
||||
])
|
||||
.current_dir(wt)
|
||||
.output()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn run_acceptance_gates_fails_on_undocumented_pub_fn() {
|
||||
let branch = format!("test/doc-gate-fail-story-860-{}", std::process::id());
|
||||
let wt = create_test_worktree(&branch);
|
||||
|
||||
set_shared_target(&wt);
|
||||
write_fast_script_test(&wt);
|
||||
commit_file(
|
||||
&wt,
|
||||
"server/src/_doc_gate_test_undoc.rs",
|
||||
"pub fn undocumented_gate_test_fn() {}\n",
|
||||
);
|
||||
|
||||
let result = run_acceptance_gates(&wt);
|
||||
cleanup_worktree(&branch, &wt);
|
||||
|
||||
let (passed, output) = result.unwrap();
|
||||
assert!(!passed, "acceptance gates should fail; output:\n{output}");
|
||||
assert!(
|
||||
output.contains("Doc coverage gate failed:"),
|
||||
"output should contain 'Doc coverage gate failed:'; got:\n{output}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn run_acceptance_gates_passes_with_documented_pub_fn() {
|
||||
let branch = format!("test/doc-gate-pass-story-860-{}", std::process::id());
|
||||
let wt = create_test_worktree(&branch);
|
||||
|
||||
set_shared_target(&wt);
|
||||
write_fast_script_test(&wt);
|
||||
commit_file(
|
||||
&wt,
|
||||
"server/src/_doc_gate_test_doc.rs",
|
||||
"//! Test module for the doc-coverage happy-path gate test.\n/// A test function that is properly documented.\npub fn documented_gate_test_fn() {}\n",
|
||||
);
|
||||
|
||||
let result = run_acceptance_gates(&wt);
|
||||
cleanup_worktree(&branch, &wt);
|
||||
|
||||
let (passed, output) = result.unwrap();
|
||||
assert!(
|
||||
passed,
|
||||
"acceptance gates should pass for documented pub fn; output:\n{output}"
|
||||
);
|
||||
}
|
||||
|
||||
// ── worktree_has_committed_work tests ─────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user