mod common;
fn dist_config() -> String {
common::without_comments("dist-workspace.toml")
}
fn rust_workflow() -> String {
common::without_comments(".github/workflows/rust.yml")
}
fn mutation_workflow() -> String {
common::without_comments(".github/workflows/mutants.yml")
}
fn release_workflow() -> String {
common::without_comments(".github/workflows/release.yml")
}
fn release_build_setup() -> String {
common::without_comments(".github/build-setup.yml")
}
fn parsed_toml(relative: &str) -> toml::Table {
toml::from_str(&common::read(relative)).unwrap_or_else(|e| panic!("{relative} must parse: {e}"))
}
fn workflow_job<'a>(workflow: &'a str, name: &str) -> &'a str {
let marker = format!("\n {name}:\n");
let start = workflow
.find(&marker)
.unwrap_or_else(|| panic!("workflow must declare a {name} job"))
+ marker.len();
let tail = &workflow[start..];
let end = tail
.match_indices('\n')
.find_map(|(offset, _)| {
let next_line = &tail[offset + 1..];
(next_line.starts_with(" ") && !next_line.starts_with(" ")).then_some(offset)
})
.unwrap_or(tail.len());
&tail[..end]
}
const SAME_REPOSITORY_PR_GUARD: &str = "github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository";
struct ReleaseTarget {
triple: &'static str,
runner: &'static str,
host: Option<&'static str>,
}
const RELEASE_TARGETS: [ReleaseTarget; 4] = [
ReleaseTarget {
triple: "aarch64-apple-darwin",
runner: "drep-macos",
host: None,
},
ReleaseTarget {
triple: "aarch64-unknown-linux-gnu",
runner: "drep-linux",
host: Some("x86_64-unknown-linux-gnu"),
},
ReleaseTarget {
triple: "x86_64-apple-darwin",
runner: "drep-macos",
host: None,
},
ReleaseTarget {
triple: "x86_64-unknown-linux-gnu",
runner: "drep-linux",
host: None,
},
];
#[test]
fn github_ci_uses_only_guarded_homelab_runners() {
let workflow = rust_workflow();
let expected_guard = format!(" if: {SAME_REPOSITORY_PR_GUARD}");
for (job_name, runner) in [
("linux", "[self-hosted, linux, x64, drep-linux]"),
("test-macos", "[self-hosted, macos, arm64, drep-macos]"),
] {
let job = workflow_job(&workflow, job_name);
assert!(
job.contains(&format!("runs-on: {runner}")),
"{job_name} must run on its repository-scoped homelab runner"
);
assert!(
job.lines().any(|line| line == expected_guard),
"{job_name} must reject forked pull requests before using a homelab runner"
);
}
let linux = workflow_job(&workflow, "linux");
assert!(
linux.contains("cargo fmt --all --check")
&& linux.contains("cargo clippy --all-targets --all-features")
&& linux.contains("cargo test --all-targets --all-features")
&& linux.contains("cargo +1.88.0 check"),
"the single Strix lane must retain format, clippy, test and MSRV gates"
);
let macos = workflow_job(&workflow, "test-macos");
assert!(
macos.contains("components: clippy"),
"the Mac test lane must install clippy because the suite exercises configured Rust compilers"
);
assert!(
!workflow.contains("\n lint:\n")
&& !workflow.contains("\n test-linux:\n")
&& !workflow.contains("\n msrv:\n"),
"serial Strix validation must not repeat runner and checkout setup across jobs"
);
assert!(
!workflow.contains("ubuntu-latest") && !workflow.contains("macos-latest"),
"validation must not consume GitHub-hosted runner minutes"
);
}
#[test]
fn mutation_ci_is_main_only_on_the_pinned_homelab_runner() {
let validation = rust_workflow();
assert!(
!validation.contains("\n mutants:\n"),
"the validation workflow must not duplicate the full mutation sweep"
);
let workflow = mutation_workflow();
let trigger = workflow
.split_once("\njobs:")
.map(|(trigger, _)| trigger)
.expect("the mutation workflow must declare jobs");
assert!(
trigger.contains("workflow_run:")
&& trigger.contains("workflows: [rust]")
&& trigger.contains("types: [completed]")
&& trigger.contains("branches: [main]"),
"full mutation CI must follow completed main-branch Rust validation"
);
assert!(
!trigger.contains("pull_request")
&& !trigger.contains("workflow_dispatch")
&& !trigger.contains("\n push:"),
"public or manually selected code must never be routed to the homelab runner"
);
let mutants = workflow_job(&workflow, "mutants");
assert!(
mutants.contains("github.event.workflow_run.event == 'push'")
&& mutants.contains("github.event.workflow_run.conclusion == 'success'")
&& mutants.contains("ref: ${{ github.event.workflow_run.head_sha }}"),
"mutation testing must follow a successful push validation and check out that exact commit"
);
assert!(
mutants.contains("runs-on: [self-hosted, linux, x64, drep-linux]"),
"the full sweep must require the repository-scoped Strix label"
);
assert!(
mutants.contains("timeout-minutes: 90"),
"the full sweep needs headroom above its observed runtime while still releasing a wedged runner"
);
assert!(
mutants.contains("tool: cargo-mutants@27.1.0"),
"the mutation gate must retain its verified cargo-mutants version"
);
assert!(
mutants.contains("clean: false")
&& mutants
.contains("git status --porcelain=v1 --untracked-files=all --ignored=matching")
&& mutants.contains("^!! target/$"),
"the warm target cache must be retained only behind a fail-closed workspace check"
);
assert!(
mutants.contains("./scripts/mutants-run.sh"),
"homelab CI and local hooks must share one mutation verdict implementation"
);
}
#[test]
fn remote_full_mutation_sweep_passes_no_phantom_argument() {
let script = common::without_comments("scripts/mutants-remote.sh");
assert!(
script.contains("if [ \"$#\" -gt 0 ]; then")
&& script.contains("printf -v REMOTE_ARGS ' %q' \"$@\"")
&& script.contains("./scripts/mutants-run.sh$REMOTE_ARGS"),
"the remote wrapper must append quoted arguments only when at least one exists"
);
assert!(
!script.contains("$(printf '%q ' \"$@\")"),
"empty positional parameters must not be formatted into a literal empty argument"
);
}
#[test]
fn every_supported_platform_is_built() {
let manifest = parsed_toml("dist-workspace.toml");
let targets = manifest["dist"]["targets"]
.as_array()
.expect("cargo-dist targets must be an array");
for target in RELEASE_TARGETS {
assert!(
targets
.iter()
.any(|value| value.as_str() == Some(target.triple)),
"a release no longer builds for {}",
target.triple
);
}
}
#[test]
fn every_release_job_uses_a_homelab_runner() {
let manifest = parsed_toml("dist-workspace.toml");
let dist = manifest["dist"]
.as_table()
.expect("cargo-dist configuration must be a table");
let runners = dist["github-custom-runners"]
.as_table()
.expect("cargo-dist custom runners must be a table");
for target in RELEASE_TARGETS {
match target.host {
Some(host) => {
let mapping = runners[target.triple]
.as_table()
.expect("a cross-runner mapping must be a table");
assert_eq!(mapping["runner"].as_str(), Some(target.runner));
assert_eq!(mapping["host"].as_str(), Some(host));
}
None => assert_eq!(runners[target.triple].as_str(), Some(target.runner)),
}
}
assert_eq!(runners["global"].as_str(), Some("drep-linux"));
assert_eq!(dist["pr-run-mode"].as_str(), Some("skip"));
let workflow = release_workflow();
let trigger = workflow
.split_once("\njobs:")
.map(|(trigger, _)| trigger)
.expect("the release workflow must declare jobs");
assert!(
!trigger.contains("pull_request"),
"the generated release workflow must be tag-only"
);
assert!(
workflow.contains("runs-on: \"drep-linux\"")
&& workflow.contains("runs-on: ${{ matrix.runner }}"),
"global jobs and target builds must use cargo-dist's homelab runner mapping"
);
assert!(
!workflow.lines().any(|line| {
let line = line.trim();
line.starts_with("runs-on:")
&& (line.contains("ubuntu-")
|| line.contains("macos-")
|| line.contains("windows-"))
}),
"release.yml must not contain a GitHub-hosted runs-on label"
);
}
#[test]
fn arm64_linux_cross_build_tools_are_reproducibly_provisioned() {
let manifest = parsed_toml("dist-workspace.toml");
assert_eq!(
manifest["dist"]["github-build-setup"].as_str(),
Some("../build-setup.yml"),
"cargo-dist must inject the repository-owned cross-build setup"
);
let setup = release_build_setup();
for target in RELEASE_TARGETS
.iter()
.filter(|target| target.host.is_some())
{
let condition = format!("contains(matrix.targets, '{}')", target.triple);
assert!(
setup.contains(&condition),
"the cross-build setup must select {}",
target.triple
);
}
assert!(
setup.contains("uses: mlugg/setup-zig@v2.2.1")
&& setup.contains("version: 0.16.0")
&& setup.contains("uses: taiki-e/install-action@v2")
&& setup.contains("tool: cargo-zigbuild@0.23.0"),
"only the arm64 Linux lane must install the pinned Zig cross-build tools"
);
let workflow = release_workflow();
let local_build = workflow_job(&workflow, "build-local-artifacts");
assert!(
local_build.contains("mlugg/setup-zig@v2.2.1")
&& local_build.contains("cargo-zigbuild@0.23.0"),
"the generated release workflow must include the configured cross-build setup"
);
}
#[test]
fn linux_release_tls_is_self_contained_for_cross_compilation() {
let manifest = parsed_toml("Cargo.toml");
let native_features = manifest["dependencies"]["reqwest"]["features"]
.as_array()
.expect("reqwest features must be an array");
assert!(
native_features
.iter()
.all(|feature| feature.as_str() != Some("native-tls-vendored")),
"native targets must not compile a vendored OpenSSL"
);
let cross_features = manifest["target"]
["cfg(all(target_os = \"linux\", target_arch = \"aarch64\"))"]["dependencies"]
["reqwest"]["features"]
.as_array()
.expect("arm64 Linux reqwest features must be an array");
assert!(
cross_features
.iter()
.any(|feature| feature.as_str() == Some("native-tls-vendored")),
"Linux release builds must compile OpenSSL instead of depending on a target sysroot"
);
}
#[test]
fn the_homebrew_formula_is_pushed_to_the_tap() {
let config = dist_config();
assert!(
config.contains(r#"installers = ["shell", "homebrew"]"#),
"a release must publish both the shell installer and the formula"
);
assert!(
config.contains(r#"tap = "slb350/homebrew-tap""#),
"the formula has no tap to be pushed to"
);
assert!(
config.contains(r#"publish-jobs = ["homebrew"]"#),
"without the homebrew publish job the formula is built and never pushed"
);
assert!(
config.contains(r#"formula = "drep""#),
"the formula must be named for the binary, not for the crate"
);
}
#[test]
fn the_pinned_dist_version_is_the_one_ci_installs() {
let config = dist_config();
let pinned = config
.lines()
.find_map(|line| line.strip_prefix("cargo-dist-version = "))
.expect("dist-workspace.toml must pin a dist version")
.trim()
.trim_matches('"');
let workflow = release_workflow();
assert!(
workflow.contains(&format!("cargo-dist/releases/download/v{pinned}/")),
"release.yml installs a different dist than the config pins ({pinned}) - run `dist init`"
);
}
#[test]
fn release_workflow_transfers_artifacts_between_jobs() {
let workflow = release_workflow();
assert!(
workflow.contains("uses: actions/upload-artifact@"),
"release.yml must upload build artifacts"
);
assert!(
workflow.contains("uses: actions/download-artifact@"),
"release.yml must download artifacts between jobs"
);
}
#[test]
fn release_workflow_needs_no_manual_ci_exception() {
let config = dist_config();
assert!(
!config.contains("allow-dirty"),
"the generated workflow must not require a hand-maintained CI exception"
);
}
#[test]
fn released_binaries_inherit_the_tuned_release_profile() {
let manifest = parsed_toml("Cargo.toml");
let profile = manifest["profile"]
.get("dist")
.and_then(toml::Value::as_table)
.expect("Cargo.toml must declare the profile dist builds with");
assert_eq!(
profile.keys().map(String::as_str).collect::<Vec<_>>(),
["inherits"],
"[profile.dist] must add nothing to [profile.release]"
);
assert_eq!(profile["inherits"].as_str(), Some("release"));
}