use std::path::{Path, PathBuf};
use std::process::Command;
fn repository_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("..")
.canonicalize()
.expect("the repository root is two directories above this crate")
}
fn bash() -> PathBuf {
let windows_directory = std::env::var_os("SystemRoot")
.map(|root| root.to_string_lossy().to_lowercase())
.filter(|root| !root.is_empty());
let Some(path) = std::env::var_os("PATH") else {
return PathBuf::from("bash");
};
for directory in std::env::split_paths(&path) {
if let Some(windows_directory) = &windows_directory
&& directory
.to_string_lossy()
.to_lowercase()
.starts_with(windows_directory.as_str())
{
continue;
}
for name in ["bash", "bash.exe"] {
let candidate = directory.join(name);
if candidate.is_file() {
return candidate;
}
}
}
PathBuf::from("bash")
}
fn is_registry_qualified(identifier: &str) -> bool {
let Some((registry, name)) = identifier.split_once(':') else {
return false;
};
!registry.is_empty()
&& registry.chars().all(|character| {
character.is_ascii_lowercase() || character.is_ascii_digit() || character == '-'
})
&& !name.is_empty()
&& !name.contains(':')
&& !name.chars().any(char::is_whitespace)
}
fn declared_target_ids(root: &Path) -> Vec<String> {
let declaration = root.join("release-targets.toml");
let text = std::fs::read_to_string(&declaration)
.unwrap_or_else(|error| panic!("could not read {}: {error}", declaration.display()));
let mut ids = Vec::new();
let mut in_target = false;
let mut targets = 0usize;
for line in text.lines() {
let line = line.trim();
if line.starts_with('#') {
continue;
}
if line.starts_with('[') {
assert_eq!(
ids.len(),
targets,
"{} has a [[target]] with no id in it",
declaration.display()
);
in_target = line.starts_with("[[target]]");
if in_target {
targets += 1;
}
continue;
}
if !in_target {
continue;
}
let Some((key, value)) = line.split_once('=') else {
continue;
};
if key.trim() != "id" {
continue;
}
let value = value.trim();
let quoted = value
.strip_prefix('"')
.and_then(|value| value.strip_suffix('"'))
.filter(|value| !value.is_empty() && !value.contains('"'));
let Some(identifier) = quoted.filter(|value| is_registry_qualified(value)) else {
panic!(
"{} gives a [[target]] the id {value}, which is not a quoted <registry>:<name>",
declaration.display()
)
};
assert_eq!(
ids.len() + 1,
targets,
"{} gives one [[target]] two ids, the second being {identifier}",
declaration.display()
);
ids.push(identifier.to_string());
}
assert_eq!(
ids.len(),
targets,
"{} has a [[target]] with no id in it",
declaration.display()
);
assert!(
!ids.is_empty(),
"{} declares no target, so there is nothing to ask a registry about",
declaration.display()
);
ids
}
#[test]
fn every_declared_target_answers_from_its_real_registry() {
let root = repository_root();
const PROBE: &str = "scripts/release-probe.sh";
let bash = bash();
let mut failures = Vec::new();
for identifier in declared_target_ids(&root) {
let answer = Command::new(&bash)
.arg(PROBE)
.arg(&identifier)
.current_dir(&root)
.output()
.unwrap_or_else(|error| panic!("could not run {PROBE}: {error}"));
let stdout = String::from_utf8_lossy(&answer.stdout).trim().to_string();
let stderr = String::from_utf8_lossy(&answer.stderr).trim().to_string();
if !answer.status.success() {
let said = match (stdout.as_str(), stderr.as_str()) {
("", "") => format!(
"nothing on either stream, so it did not reach the probe's own refusal \
— every one of those writes a reason to stderr. It was run under \
{}, so look at whether that is the bash that runs this repository's \
other scripts, and whether it could open the script at all",
bash.display()
),
("", stderr) => format!("on stderr: {stderr}"),
(stdout, "") => format!("on stdout, and nothing on stderr: {stdout}"),
(stdout, stderr) => format!("on stderr: {stderr}; and on stdout: {stdout}"),
};
failures.push(format!(
"{identifier}: not answered ({}) under {}. The probe said {said}",
answer.status,
bash.display()
));
continue;
}
if stdout.is_empty() {
failures.push(format!(
"{identifier}: its registry answered that it serves nothing. Every declared \
target here has been released, so this is the lookup reaching the wrong \
place — re-observe that registry's interface and bring \
config/registry-interfaces.toml and scripts/release-probe.sh to it. A target \
declared before its own first release is the one other way to land here."
));
continue;
}
}
assert!(
failures.is_empty(),
"the public registries did not answer for every declared target:\n {}",
failures.join("\n ")
);
}