use std::path::PathBuf;
fn repo_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.canonicalize()
.expect("resolve repo root")
}
fn run_cmd_lines(workflow: &str) -> Vec<&str> {
let mut lines = workflow.lines();
for line in lines.by_ref() {
if line.trim_start() == "runCmd: |" {
break;
}
}
let block_indent = workflow
.lines()
.find(|l| l.trim_start() == "runCmd: |")
.map(|l| l.len() - l.trim_start().len())
.expect("find runCmd: | line");
let mut cmd_lines = Vec::new();
for line in lines {
if line.trim().is_empty() {
continue;
}
let indent = line.len() - line.trim_start().len();
if indent <= block_indent {
break;
}
cmd_lines.push(line.trim());
}
cmd_lines
}
#[test]
fn devcontainer_runcmd_fails_fast_before_any_cargo_invocation() {
let path = repo_root().join(".github/workflows/devcontainer.yml");
let workflow =
std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()));
let cmd_lines = run_cmd_lines(&workflow);
assert!(
!cmd_lines.is_empty(),
"could not locate any command lines inside `runCmd: |` in {}",
path.display()
);
assert_eq!(
cmd_lines[0], "set -e",
"`runCmd`'s first command line must be exactly `set -e`, so an \
earlier failing command (e.g. `cargo test`) fails the CI job \
instead of being masked by a later command's exit code — see \
15-REVIEW.md CR-01 (commit 3918792). Found first line: {:?}\n\
full runCmd lines: {cmd_lines:#?}",
cmd_lines[0]
);
let first_cargo_idx = cmd_lines
.iter()
.position(|l| l.starts_with("cargo "))
.expect("expected at least one `cargo` invocation in runCmd");
let set_e_idx = cmd_lines
.iter()
.position(|l| *l == "set -e")
.expect("expected `set -e` in runCmd");
assert!(
set_e_idx < first_cargo_idx,
"`set -e` (line {set_e_idx}) must precede every `cargo` invocation \
(first at line {first_cargo_idx}) — see 15-REVIEW.md CR-01.\n\
full runCmd lines: {cmd_lines:#?}"
);
}