#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::collections::BTreeSet;
use std::fmt::Write as _;
use std::fs;
use std::process::Command;
const ROOT: &str = "../..";
const KNOWN_HOSTS: &str = "dist/homebrew/github_known_hosts";
fn read(rel: &str) -> String {
let path = format!("{ROOT}/{rel}");
fs::read_to_string(&path).unwrap_or_else(|e| panic!("{path}: {e}"))
}
struct BuildTarget {
triple: String,
runner: String,
npm_dir: String,
}
fn build_matrix() -> Vec<BuildTarget> {
let yml = read(".github/workflows/release.yml");
let mut targets: Vec<BuildTarget> = Vec::new();
for line in yml.lines() {
let line = line.trim();
if let Some(triple) = line.strip_prefix("- target:") {
targets.push(BuildTarget {
triple: triple.trim().to_owned(),
runner: String::new(),
npm_dir: String::new(),
});
continue;
}
let (field, value) = if let Some(v) = line.strip_prefix("os:") {
("os", v)
} else if let Some(v) = line.strip_prefix("npm:") {
("npm", v)
} else {
continue;
};
let current = targets
.last_mut()
.unwrap_or_else(|| panic!("a matrix `{field}:` must follow a `- target:` row"));
let slot = if field == "os" {
&mut current.runner
} else {
&mut current.npm_dir
};
assert!(
slot.is_empty(),
"matrix row `{}` has two `{field}:` values",
current.triple
);
value.trim().clone_into(slot);
}
for target in &targets {
assert!(
!target.runner.is_empty(),
"matrix row `{}` has no `os:` runner",
target.triple
);
assert!(
!target.npm_dir.is_empty(),
"matrix row `{}` has no `npm:` platform package",
target.triple
);
}
assert_eq!(
targets.len(),
4,
"expected 4 release targets, got {:?}",
targets.iter().map(|t| &t.triple).collect::<Vec<_>>()
);
targets
}
fn run_generator(args: &[&str]) -> std::process::Output {
Command::new("bash")
.arg("dist/homebrew/gen-formula.sh")
.args(args)
.current_dir(ROOT)
.output()
.expect("gen-formula.sh should be runnable with bash")
}
fn generate_formula(args: &[&str]) -> String {
let out = run_generator(args);
assert!(
out.status.success(),
"gen-formula.sh {args:?} failed: {}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8(out.stdout).expect("the formula should be UTF-8")
}
fn manifest_for(tag: &str, version: &str, targets: &[String]) -> (std::path::PathBuf, Vec<String>) {
let dir = std::env::temp_dir().join(format!("pristine-dist-{tag}-{}", std::process::id()));
fs::create_dir_all(&dir).expect("temp dir should be creatable");
let mut digests = Vec::with_capacity(targets.len());
let mut body = String::new();
for (i, target) in targets.iter().enumerate() {
let nth = u32::try_from(i).expect("a handful of targets fits a u32");
let digest = std::char::from_digit(nth + 1, 16)
.expect("index fits a hex digit")
.to_string()
.repeat(64);
writeln!(body, "{digest} ./pristine-{version}-{target}.tar.gz")
.expect("writing to a String cannot fail");
digests.push(digest);
}
let path = dir.join("SHA256SUMS");
fs::write(&path, body).expect("manifest should be writable");
(path, digests)
}
#[test]
fn homebrew_formula_links_every_release_target() {
let version = env!("CARGO_PKG_VERSION");
let formula = generate_formula(&["--version", version]);
for target in build_matrix() {
let asset = format!("pristine-{version}-{}.tar.gz", target.triple);
assert!(
formula.contains(&asset),
"the generated formula has no URL for {asset}"
);
}
}
#[test]
fn homebrew_formula_is_named_for_the_binary_not_the_crate() {
let formula = generate_formula(&["--version", env!("CARGO_PKG_VERSION")]);
assert!(
formula.contains("class Pristine < Formula"),
"the formula class must be `Pristine`, so the tap installs as `pristine`:\n{formula}"
);
assert!(
!formula.contains("pristine-cli"),
"the crate name leaked into the formula, which only ever names the binary:\n{formula}"
);
assert!(
formula.contains(r#"bin.install "pristine""#),
"the formula must install the `pristine` binary out of the tarball root:\n{formula}"
);
}
#[test]
fn homebrew_formula_emits_one_wellformed_sha_per_target() {
let formula = generate_formula(&["--version", env!("CARGO_PKG_VERSION")]);
let digests: Vec<&str> = formula
.lines()
.filter_map(|l| l.trim().strip_prefix("sha256 \""))
.filter_map(|rest| rest.strip_suffix('"'))
.collect();
assert_eq!(
digests.len(),
4,
"expected one sha256 per release target, got {digests:?}"
);
for digest in digests {
assert_eq!(digest.len(), 64, "sha256 {digest:?} is not 64 characters");
assert!(
digest.chars().all(|c| c.is_ascii_hexdigit()),
"sha256 {digest:?} is not hex"
);
}
}
#[test]
fn homebrew_formula_takes_its_digests_from_the_manifest() {
let version = env!("CARGO_PKG_VERSION");
let targets: Vec<String> = build_matrix().into_iter().map(|t| t.triple).collect();
let (manifest, digests) = manifest_for("complete", version, &targets);
let formula = generate_formula(&[
"--version",
version,
"--checksums",
manifest.to_str().expect("temp path should be UTF-8"),
]);
fs::remove_dir_all(manifest.parent().expect("manifest has a parent")).ok();
for digest in &digests {
assert!(
formula.contains(digest.as_str()),
"the formula did not pick up {digest} from the manifest:\n{formula}"
);
}
assert!(
!formula.contains(&"0".repeat(64)),
"a sentinel digest survived a run with a real manifest:\n{formula}"
);
}
#[test]
fn homebrew_formula_refuses_a_manifest_with_a_target_missing() {
let version = env!("CARGO_PKG_VERSION");
let mut targets: Vec<String> = build_matrix().into_iter().map(|t| t.triple).collect();
let dropped = targets.pop().expect("the matrix has targets");
let (manifest, _) = manifest_for("partial", version, &targets);
let out = run_generator(&[
"--version",
version,
"--checksums",
manifest.to_str().expect("temp path should be UTF-8"),
]);
fs::remove_dir_all(manifest.parent().expect("manifest has a parent")).ok();
let formula = String::from_utf8_lossy(&out.stdout);
assert!(
!formula.contains("sha256 \"\""),
"the generator emitted an empty digest for {dropped} instead of failing:\n{formula}"
);
assert!(
!out.status.success(),
"the generator exited 0 with no digest for {dropped}; the release step would \
call that a success and push the formula"
);
}
fn npm_targets() -> BTreeSet<String> {
let js = read("npm/pristine/lib/platform.cjs");
let packages: BTreeSet<String> = js
.lines()
.filter_map(|line| line.split_once("package: '"))
.filter_map(|(_, rest)| rest.split_once('\''))
.map(|(name, _)| name.to_owned())
.collect();
assert!(
!packages.is_empty(),
"found no `package: '…'` entries in platform.cjs — did the TARGETS shape change?"
);
packages
}
#[test]
fn the_release_matrix_covers_every_npm_platform_package() {
let matrix = build_matrix();
let from_matrix: BTreeSet<String> = matrix
.iter()
.map(|t| format!("@agentender/{}", t.npm_dir))
.collect();
assert_eq!(
from_matrix,
npm_targets(),
"release.yml's `npm:` rows and platform.cjs's TARGETS name different packages"
);
for target in &matrix {
let dir = format!("{ROOT}/npm/{}", target.npm_dir);
assert!(
fs::metadata(format!("{dir}/package.json")).is_ok(),
"{} builds into npm/{}, which has no package.json",
target.triple,
target.npm_dir
);
}
}
#[test]
fn every_target_is_built_on_a_runner_of_its_own_platform() {
for target in build_matrix() {
let expected_host = if target.triple.contains("apple-darwin") {
"macos"
} else {
"ubuntu"
};
assert!(
target.runner.starts_with(expected_host),
"{} is built on {}, which cannot run the binary it produces",
target.triple,
target.runner
);
}
}
#[test]
fn the_release_proves_each_platform_tarball_carries_its_binary() {
let yml = read(".github/workflows/release.yml");
assert!(
yml.contains("tar -tzf") && yml.contains("package/pristine"),
"release.yml must inspect each packed platform tarball for `package/pristine` \
before publishing it; a `files` entry for a missing binary is skipped in silence"
);
}
#[test]
fn npm_pack_is_never_handed_an_owner_slash_repo_shorthand() {
let yml = read(".github/workflows/release.yml");
for line in yml.lines() {
let trimmed = line.trim();
if trimmed.starts_with('#') {
continue;
}
let Some((_, rest)) = trimmed.split_once("npm pack ") else {
continue;
};
let spec = rest.split_whitespace().next().unwrap_or_default();
assert!(
!spec.starts_with("npm/"),
"`npm pack {spec}` is a GitHub owner/repo shorthand, not a path. \
Write `./{spec}`:\n {trimmed}"
);
}
}
#[test]
fn the_tap_push_checks_the_host_key_against_pinned_content() {
let yml = read(".github/workflows/release.yml");
for line in yml.lines() {
let trimmed = line.trim();
assert!(
trimmed.starts_with('#') || !trimmed.contains("ssh-keyscan"),
"`ssh-keyscan` learns the host key from the connection it is checking, which is \
not a check. Pin the keys instead:\n {trimmed}"
);
}
assert!(
yml.contains("StrictHostKeyChecking=yes"),
"the tap push must set StrictHostKeyChecking=yes; `accept-new` is the same \
trust-on-first-use the pinned file exists to remove"
);
assert!(
yml.contains("UserKnownHostsFile=") && yml.contains(KNOWN_HOSTS),
"the tap push must point UserKnownHostsFile at {KNOWN_HOSTS}"
);
let pinned = read(KNOWN_HOSTS);
let entries: Vec<&str> = pinned
.lines()
.map(str::trim)
.filter(|l| !l.is_empty() && !l.starts_with('#'))
.collect();
assert!(
!entries.is_empty(),
"{KNOWN_HOSTS} has no entries; an empty known_hosts under \
StrictHostKeyChecking=yes fails every push"
);
for entry in &entries {
let mut fields = entry.split_whitespace();
let host = fields.next().unwrap_or_default();
let algorithm = fields.next().unwrap_or_default();
let key = fields.next().unwrap_or_default();
assert_eq!(
host, "github.com",
"{KNOWN_HOSTS} pins a host other than github.com: {entry}"
);
assert!(
algorithm.starts_with("ssh-") || algorithm.starts_with("ecdsa-"),
"{KNOWN_HOSTS} entry has no key algorithm: {entry}"
);
assert!(
key.len() > 40,
"{KNOWN_HOSTS} entry has no key material: {entry}"
);
}
}
#[test]
fn the_documented_tap_repair_procedure_stages_the_formula() {
let docs = read("docs/releasing.md");
let block = docs
.split("```")
.find(|b| b.contains("gh release download"))
.expect("docs/releasing.md must document how to repair the tap by hand");
assert!(
block.contains("git add "),
"the repair procedure must `git add` the formula. It is a new file in the tap, so \
`commit -a` stages nothing, exits 0, and a chained push pushes nothing:\n{block}"
);
assert!(
!block.contains("commit -a"),
"`git commit -a` cannot stage a formula the tap does not track yet, and it exits 0 \
while doing nothing:\n{block}"
);
assert!(
block.contains("mkdir -p Formula"),
"create Formula/ explicitly rather than relying on `gh release download --output` to \
make the parent directory; that is gh behaviour, not a documented guarantee:\n{block}"
);
for file in [".github/workflows/release.yml", "docs/releasing.md"] {
assert!(
!read(file).contains("update-formula"),
"{file} still points at update-formula.yml, which was deleted from the tap"
);
}
}
#[test]
fn a_prerelease_tag_does_not_touch_the_tap_or_the_registries() {
let yml = read(".github/workflows/release.yml");
let guards = yml.matches("!contains(").count();
assert!(
guards >= 3,
"expected the tap push, the crate publish and the npm publish to each be gated \
off pre-release tags, found {guards} `!contains(` guards in release.yml"
);
}
#[test]
fn the_crate_publishes_over_oidc_rather_than_a_stored_token() {
let yml = read(".github/workflows/release.yml");
assert!(
yml.contains("cargo publish"),
"release.yml must actually run `cargo publish`, or nothing ships the crate"
);
assert!(
yml.contains("crates-io-auth-action"),
"the crate publish must mint its token over OIDC rather than read a stored \
CARGO_REGISTRY_TOKEN"
);
assert!(
!yml.contains("secrets.CARGO_REGISTRY_TOKEN"),
"a stored CARGO_REGISTRY_TOKEN defeats the point of the OIDC exchange"
);
}
fn manifest(source: &str, what: &str) -> toml::Table {
source
.parse()
.unwrap_or_else(|e| panic!("{what} should be valid TOML: {e}"))
}
fn cargo_string_array(cargo: &toml::Table, key: &str) -> Vec<String> {
cargo
.get("package")
.and_then(|package| package.get(key))
.unwrap_or_else(|| panic!("Cargo.toml [package] has no `{key}`"))
.as_array()
.unwrap_or_else(|| panic!("Cargo.toml [package] `{key}` is not an array"))
.iter()
.map(|entry| {
entry
.as_str()
.unwrap_or_else(|| panic!("Cargo.toml [package] `{key}` holds a non-string"))
.to_owned()
})
.collect()
}
#[test]
fn crate_metadata_is_crates_io_publishable() {
let cargo = manifest(
&fs::read_to_string("Cargo.toml").expect("the crate manifest should be readable"),
"the crate manifest",
);
let workspace = manifest(&read("Cargo.toml"), "the workspace manifest");
let keywords = cargo_string_array(&cargo, "keywords");
assert!(
(1..=5).contains(&keywords.len()),
"crates.io allows at most 5 keywords, found {}: {keywords:?}",
keywords.len()
);
for kw in &keywords {
assert!(
!kw.is_empty() && kw.len() <= 20,
"crates.io caps a keyword at 20 characters: {kw:?}"
);
assert!(
kw.starts_with(|c: char| c.is_ascii_alphanumeric()),
"a keyword must start alphanumeric: {kw:?}"
);
}
assert!(
!cargo_string_array(&cargo, "categories").is_empty(),
"declare at least one crates.io category"
);
for field in ["readme", "repository", "license", "description"] {
let declared = cargo
.get("package")
.and_then(|package| package.get(field))
.unwrap_or_else(|| {
panic!(
"Cargo.toml [package] is missing `{field}`, which `cargo install` users read"
)
});
if declared.is_str() {
continue;
}
assert!(
declared
.get("workspace")
.and_then(toml::Value::as_bool)
.unwrap_or(false),
"Cargo.toml [package] `{field}` is neither a string nor `{field}.workspace = true`"
);
assert!(
workspace
.get("workspace")
.and_then(|w| w.get("package"))
.and_then(|package| package.get(field))
.is_some_and(toml::Value::is_str),
"the crate inherits `{field}` from the workspace, which does not declare it"
);
}
}
#[test]
fn every_third_party_action_is_pinned_to_a_commit() {
let dir = format!("{ROOT}/.github/workflows");
let entries = fs::read_dir(&dir).unwrap_or_else(|e| panic!("{dir}: {e}"));
let mut checked = 0;
for entry in entries {
let path = entry.expect("a readable directory entry").path();
if path.extension().is_none_or(|e| e != "yml") {
continue;
}
let name = path.display().to_string();
let body = fs::read_to_string(&path).unwrap_or_else(|e| panic!("{name}: {e}"));
for line in body.lines() {
let line = line.trim();
if line.starts_with('#') {
continue;
}
let Some(reference) = line
.strip_prefix("- uses:")
.or_else(|| line.strip_prefix("uses:"))
else {
continue;
};
let reference = reference.trim();
if reference.starts_with("./") {
continue;
}
checked += 1;
let (action, rest) = reference
.split_once('@')
.unwrap_or_else(|| panic!("{name}: `uses: {reference}` has no version at all"));
let (revision, comment) = match rest.split_once('#') {
Some((rev, c)) => (rev.trim(), c.trim()),
None => (rest.trim(), ""),
};
let pinned = revision.len() == 40 && revision.chars().all(|c| c.is_ascii_hexdigit());
assert!(
pinned,
"{name}: `{action}@{revision}` is a mutable tag. Pin it to the commit \
that tag points at today:\n \
gh api repos/{action}/commits/{revision} --jq .sha"
);
assert!(
comment.starts_with('v'),
"{name}: `{action}` is pinned to {revision} with no `# vX.Y.Z` comment, \
so nothing says which release it is or whether it is current"
);
}
}
assert!(
checked >= 10,
"only found {checked} third-party action references — did the scan stop working?"
);
}