use std::fs;
use std::path::Path;
use std::process::Command;
use path_slash::PathExt as _;
#[test]
fn vergen_env_vars_are_read_optionally() {
let src_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut violations = Vec::new();
scan_directory(&src_dir, &src_dir, &mut violations);
assert!(
violations.is_empty(),
"Found `env!` on a build-script `VERGEN_*` variable:\n\n{}\n\n\
These variables are absent when building the crates.io source archive \
(no `.git`), so `env!` fails to compile and breaks `cargo install` \
(#3123). Use `option_env!` and fall back to a default instead.",
violations.join("\n")
);
}
fn scan_directory(dir: &Path, src_dir: &Path, violations: &mut Vec<String>) {
let Ok(entries) = fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
scan_directory(&path, src_dir, violations);
} else if path.extension().and_then(|s| s.to_str()) == Some("rs") {
check_file(&path, src_dir, violations);
}
}
}
fn check_file(path: &Path, src_dir: &Path, violations: &mut Vec<String>) {
let Ok(contents) = fs::read_to_string(path) else {
return;
};
let relative = path.strip_prefix(src_dir).unwrap_or(path).to_slash_lossy();
for (i, line) in contents.lines().enumerate() {
if has_bare_env_vergen(line) {
violations.push(format!("{relative}:{}: {}", i + 1, line.trim()));
}
}
}
fn has_bare_env_vergen(line: &str) -> bool {
line.match_indices("env!(\"VERGEN_")
.any(|(idx, _)| !line[..idx].ends_with("option_"))
}
#[test]
#[ignore = "slow: full from-scratch crate build; run in the nightly workflow via --ignored"]
fn crate_io_archive_builds_and_versions_without_git() {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
let pkg_version = env!("CARGO_PKG_VERSION");
let status = Command::new(&cargo)
.args(["package", "-p", "worktrunk", "--no-verify", "--allow-dirty"])
.current_dir(manifest_dir)
.status()
.expect("spawn cargo package");
assert!(status.success(), "cargo package failed");
let crate_file = manifest_dir
.join("target/package")
.join(format!("worktrunk-{pkg_version}.crate"));
assert!(crate_file.exists(), "expected archive at {crate_file:?}");
let temp = tempfile::tempdir().expect("create tempdir");
let untar = Command::new("tar")
.arg("xzf")
.arg(&crate_file)
.arg("-C")
.arg(temp.path())
.status()
.expect("spawn tar");
assert!(untar.success(), "tar extraction failed");
let extracted = temp.path().join(format!("worktrunk-{pkg_version}"));
assert!(extracted.is_dir(), "extracted dir missing: {extracted:?}");
let build = Command::new(&cargo)
.args(["build", "--bin", "wt"])
.current_dir(&extracted)
.env("GIT_CEILING_DIRECTORIES", temp.path())
.env_remove("VERGEN_GIT_DESCRIBE")
.status()
.expect("spawn cargo build");
assert!(
build.success(),
"building the crates.io archive without `.git` failed (this is #3123)"
);
let wt = extracted.join("target/debug/wt");
let version = Command::new(&wt)
.arg("--version")
.output()
.expect("spawn wt --version");
assert!(version.status.success(), "`wt --version` exited non-zero");
let stdout = String::from_utf8_lossy(&version.stdout);
assert!(
stdout.contains(pkg_version),
"expected `wt --version` to fall back to {pkg_version}, got: {stdout:?}"
);
}