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,
|
/// Run `cargo clippy` and the project test suite (via `script/test` if present,
|
||||||
/// otherwise `cargo nextest run` / `cargo test`) in the given directory.
|
/// otherwise `cargo nextest run` / `cargo test`) in the given directory.
|
||||||
/// Returns `(gates_passed, combined_output)`.
|
/// 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));
|
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))
|
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 ─────────────────────────────────────
|
// ── worktree_has_committed_work tests ─────────────────────────────────────
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user