1449 lines
45 KiB
Rust
1449 lines
45 KiB
Rust
//! Consolidated, shell-free routine and release CI command graphs.
|
|
|
|
use super::{
|
|
MatrixError, Result, audit, audit_api_surface, audit_dependencies, audit_documentation,
|
|
audit_provenance, load, write_api_baseline, write_documentation_report,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
use sha2::{Digest, Sha256};
|
|
use std::collections::BTreeSet;
|
|
use std::ffi::{OsStr, OsString};
|
|
use std::fs::{self, OpenOptions};
|
|
use std::io::{Read as _, Write as _};
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::{Command, Stdio};
|
|
use std::time::{Instant, SystemTime, UNIX_EPOCH};
|
|
|
|
const COVERAGE_PATH: &str = "ci/ci-coverage.json";
|
|
const REQUIRED_WORKFLOW: &str = ".gitea/workflows/ci.yml";
|
|
const RELEASE_WORKFLOW: &str = ".gitea/workflows/release.yml";
|
|
const LEGACY_WORKFLOW_COUNT: usize = 13;
|
|
const LEGACY_WORKFLOWS: [&str; LEGACY_WORKFLOW_COUNT] = [
|
|
"api-surface.yml",
|
|
"artifact-audit.yml",
|
|
"codegen.yml",
|
|
"concurrency-audit.yml",
|
|
"documentation.yml",
|
|
"imaging-meshing.yml",
|
|
"jpeg2000.yml",
|
|
"performance.yml",
|
|
"release-candidate.yml",
|
|
"release-matrix.yml",
|
|
"rust-workspace.yml",
|
|
"skia.yml",
|
|
"supply-chain.yml",
|
|
];
|
|
const SKIA_ARCHIVE: &str = "skia-binaries-a25a0fdb7d90429aa2d1-aarch64-unknown-linux-gnu-jpegd-jpege-pdf-svg-textlayout-vulkan-webpd-webpe.tar.gz";
|
|
const SKIA_SHA256: &str = "dd127f458a5e67a79f3936a8aa19f822fe90a1d6a11b50b5f84df2b0519d909c";
|
|
const SKIA_SUCCESS: &str = "DOWNLOAD AND INSTALL SUCCEEDED";
|
|
const SKIA_SOURCE_BUILD: &str = "STARTING A FULL BUILD";
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct CoverageManifest {
|
|
schema: u32,
|
|
required_workflow: String,
|
|
release_workflow: String,
|
|
hard_timeout_minutes: u64,
|
|
internal_target_seconds: u64,
|
|
required_checks: Vec<String>,
|
|
legacy_workflows: Vec<LegacyWorkflow>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct LegacyWorkflow {
|
|
workflow: String,
|
|
responsibility: String,
|
|
destination: String,
|
|
checks: Vec<String>,
|
|
rationale: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
struct GateEvidence {
|
|
schema: u32,
|
|
gate: &'static str,
|
|
status: &'static str,
|
|
source_commit: String,
|
|
recorded_unix_seconds: u64,
|
|
runner_architecture: String,
|
|
rust_host: String,
|
|
rust_target: String,
|
|
cargo_cache_hit: Option<bool>,
|
|
dependency_cache_hit: Option<bool>,
|
|
native_cache_hit: Option<bool>,
|
|
tools_cache_hit: Option<bool>,
|
|
peak_memory_kib: Option<u64>,
|
|
elapsed_seconds: f64,
|
|
internal_target_seconds: u64,
|
|
skia: SkiaEvidence,
|
|
stages: Vec<StageEvidence>,
|
|
failure: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Default, Serialize)]
|
|
struct SkiaEvidence {
|
|
archive: String,
|
|
sha256: String,
|
|
target: String,
|
|
download_and_install_succeeded: bool,
|
|
source_build_started: bool,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
struct StageEvidence {
|
|
id: String,
|
|
elapsed_seconds: f64,
|
|
commands: usize,
|
|
status: &'static str,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct CommandSpec {
|
|
program: &'static str,
|
|
args: &'static [&'static str],
|
|
}
|
|
|
|
impl CommandSpec {
|
|
const fn new(program: &'static str, args: &'static [&'static str]) -> Self {
|
|
Self { program, args }
|
|
}
|
|
}
|
|
|
|
/// Validates the workflow split and the complete legacy-check coverage map.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns an error when the manifest is incomplete, workflow triggers or
|
|
/// runners violate policy, or Skia can fall back to a source build.
|
|
pub fn audit_consolidated_ci(root: &Path) -> Result<()> {
|
|
let manifest = coverage(root)?;
|
|
validate_coverage(&manifest)?;
|
|
validate_workflows(root, &manifest)?;
|
|
validate_skia_configuration(root)?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Runs the routine code-ready gate and always emits a stage-timing record.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns an error for an invalid host/native identity, a failed validation
|
|
/// command, stale evidence, or exhaustion of the internal runtime target.
|
|
pub fn run_required_gate(root: &Path, evidence_path: &Path) -> Result<()> {
|
|
audit_consolidated_ci(root)?;
|
|
let manifest = coverage(root)?;
|
|
prepare_new_evidence(evidence_path)?;
|
|
let started = Instant::now();
|
|
let rust_host = rust_host(root)?;
|
|
let expected_host = std::env::var("METACRATE_EXPECTED_HOST")
|
|
.unwrap_or_else(|_| "aarch64-unknown-linux-gnu".to_owned());
|
|
let mut evidence = GateEvidence {
|
|
schema: 1,
|
|
gate: "required",
|
|
status: "failed",
|
|
source_commit: command_line(root, "git", &["rev-parse", "--verify", "HEAD"])
|
|
.unwrap_or_else(|_| "unknown".to_owned()),
|
|
recorded_unix_seconds: unix_seconds()?,
|
|
runner_architecture: std::env::consts::ARCH.to_owned(),
|
|
rust_host: rust_host.clone(),
|
|
rust_target: expected_host.clone(),
|
|
cargo_cache_hit: cache_hit("METACRATE_CARGO_CACHE_HIT"),
|
|
dependency_cache_hit: cache_hit("METACRATE_DEPENDENCY_CACHE_HIT"),
|
|
native_cache_hit: cache_hit("METACRATE_NATIVE_CACHE_HIT"),
|
|
tools_cache_hit: cache_hit("METACRATE_TOOLS_CACHE_HIT"),
|
|
peak_memory_kib: peak_memory_kib(),
|
|
elapsed_seconds: 0.0,
|
|
internal_target_seconds: manifest.internal_target_seconds,
|
|
skia: SkiaEvidence {
|
|
archive: SKIA_ARCHIVE.to_owned(),
|
|
sha256: SKIA_SHA256.to_owned(),
|
|
target: expected_host.clone(),
|
|
..SkiaEvidence::default()
|
|
},
|
|
stages: Vec::new(),
|
|
failure: None,
|
|
};
|
|
|
|
let result = run_required(
|
|
root,
|
|
&manifest,
|
|
&rust_host,
|
|
&expected_host,
|
|
started,
|
|
&mut evidence,
|
|
);
|
|
evidence.elapsed_seconds = started.elapsed().as_secs_f64();
|
|
evidence.peak_memory_kib = peak_memory_kib();
|
|
match &result {
|
|
Ok(()) => evidence.status = "ok",
|
|
Err(error) => evidence.failure = Some(error.to_string()),
|
|
}
|
|
write_evidence(evidence_path, &evidence)?;
|
|
result
|
|
}
|
|
|
|
/// Runs the explicitly non-routine clean-build, cross-target, packaging, and soak graph.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns an error when the release matrix is invalid, a release command
|
|
/// fails, or its create-new evidence record cannot be written.
|
|
#[allow(clippy::too_many_lines)] // This is the declarative release command inventory.
|
|
pub fn run_release_gate(root: &Path, evidence_path: &Path) -> Result<()> {
|
|
audit_consolidated_ci(root)?;
|
|
let matrix = load(root)?;
|
|
audit(root, &matrix)?;
|
|
prepare_new_evidence(evidence_path)?;
|
|
let started = Instant::now();
|
|
let target_dir = root.join("target/release-gate");
|
|
let environment = [
|
|
(OsString::from("CARGO_INCREMENTAL"), OsString::from("0")),
|
|
(
|
|
OsString::from("CARGO_TARGET_DIR"),
|
|
target_dir.into_os_string(),
|
|
),
|
|
];
|
|
let stages: [(&str, Vec<CommandSpec>); 4] = [
|
|
(
|
|
"msrv-and-portable-cross-targets",
|
|
vec![
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"+1.96.0",
|
|
"check",
|
|
"--locked",
|
|
"--all-targets",
|
|
"--no-default-features",
|
|
"-p",
|
|
"libremetaverse-types",
|
|
"-p",
|
|
"libremetaverse-structured-data",
|
|
"-p",
|
|
"libremetaverse-imaging",
|
|
"-p",
|
|
"libremetaverse-prim-mesher",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"+1.97.1",
|
|
"check",
|
|
"--locked",
|
|
"--target",
|
|
"x86_64-pc-windows-gnu",
|
|
"--no-default-features",
|
|
"-p",
|
|
"libremetaverse-types",
|
|
"-p",
|
|
"libremetaverse-structured-data",
|
|
"-p",
|
|
"libremetaverse-imaging",
|
|
"-p",
|
|
"libremetaverse-prim-mesher",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"+1.97.1",
|
|
"check",
|
|
"--locked",
|
|
"--target",
|
|
"x86_64-apple-darwin",
|
|
"--no-default-features",
|
|
"-p",
|
|
"libremetaverse-types",
|
|
"-p",
|
|
"libremetaverse-structured-data",
|
|
"-p",
|
|
"libremetaverse-imaging",
|
|
"-p",
|
|
"libremetaverse-prim-mesher",
|
|
],
|
|
),
|
|
],
|
|
),
|
|
(
|
|
"benchmarks-and-resource-soak",
|
|
vec![
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"+1.97.1",
|
|
"bench",
|
|
"--locked",
|
|
"-p",
|
|
"libremetaverse-imaging",
|
|
"--bench",
|
|
"image_pipeline",
|
|
"--no-run",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"+1.97.1",
|
|
"bench",
|
|
"--locked",
|
|
"-p",
|
|
"libremetaverse-prim-mesher",
|
|
"--bench",
|
|
"meshing",
|
|
"--no-run",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"+1.97.1",
|
|
"run",
|
|
"--locked",
|
|
"-p",
|
|
"metacrate-concurrency-audit",
|
|
"--",
|
|
"--cycles",
|
|
"16",
|
|
"--evidence",
|
|
"artifacts/release/concurrency-audit.json",
|
|
],
|
|
),
|
|
],
|
|
),
|
|
(
|
|
"source-packages-and-native-binaries",
|
|
vec![
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"+1.97.1",
|
|
"package",
|
|
"--locked",
|
|
"--no-verify",
|
|
"-p",
|
|
"libremetaverse-types",
|
|
"-p",
|
|
"libremetaverse-structured-data",
|
|
"-p",
|
|
"libremetaverse-imaging",
|
|
"-p",
|
|
"libremetaverse-imaging-skia",
|
|
"-p",
|
|
"libremetaverse-openjpeg",
|
|
"-p",
|
|
"libremetaverse-opus",
|
|
"-p",
|
|
"libremetaverse-prim-mesher",
|
|
"-p",
|
|
"libremetaverse-lsl-tools",
|
|
"-p",
|
|
"libremetaverse",
|
|
"-p",
|
|
"libremetaverse-rendering-simple",
|
|
"-p",
|
|
"libremetaverse-rendering-mesh-foundry",
|
|
"-p",
|
|
"libremetaverse-rlv",
|
|
"-p",
|
|
"libremetaverse-utilities",
|
|
"-p",
|
|
"libremetaverse-voice-vivox",
|
|
"-p",
|
|
"libremetaverse-voice-webrtc",
|
|
"-p",
|
|
"libremetaverse-programs",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"+1.97.1",
|
|
"build",
|
|
"--locked",
|
|
"--release",
|
|
"-p",
|
|
"libremetaverse-programs",
|
|
"--bins",
|
|
],
|
|
),
|
|
],
|
|
),
|
|
(
|
|
"release-audits",
|
|
vec![
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"+1.97.1",
|
|
"run",
|
|
"--locked",
|
|
"-p",
|
|
"metacrate-ci-matrix",
|
|
"--",
|
|
"artifact-audit",
|
|
"--artifact-dir",
|
|
"target/release-gate/release",
|
|
"--package-dir",
|
|
"target/release-gate/package",
|
|
"--evidence",
|
|
"artifacts/release/artifact-audit.json",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"+1.97.1",
|
|
"run",
|
|
"--locked",
|
|
"-p",
|
|
"metacrate-ci-matrix",
|
|
"--",
|
|
"release-candidate-audit",
|
|
"--evidence",
|
|
"artifacts/release/release-candidate.json",
|
|
],
|
|
),
|
|
],
|
|
),
|
|
];
|
|
let mut recorded = Vec::new();
|
|
let mut failure = None;
|
|
for (id, commands) in stages {
|
|
let stage_started = Instant::now();
|
|
let mut status = "ok";
|
|
for spec in &commands {
|
|
println!("ci-release {id}: {} {}", spec.program, spec.args.join(" "));
|
|
let command_status = configured_command(root, spec, &environment)
|
|
.stdin(Stdio::null())
|
|
.status()?;
|
|
if !command_status.success() {
|
|
status = "failed";
|
|
failure = Some(format!(
|
|
"release stage {id} failed while running {}",
|
|
spec.program
|
|
));
|
|
break;
|
|
}
|
|
}
|
|
recorded.push(StageEvidence {
|
|
id: id.to_owned(),
|
|
elapsed_seconds: stage_started.elapsed().as_secs_f64(),
|
|
commands: commands.len(),
|
|
status,
|
|
});
|
|
if failure.is_some() {
|
|
break;
|
|
}
|
|
}
|
|
let value = serde_json::json!({
|
|
"schema": 1,
|
|
"gate": "release",
|
|
"status": if failure.is_none() { "ok" } else { "failed" },
|
|
"source_commit": command_line(root, "git", &["rev-parse", "--verify", "HEAD"]).unwrap_or_else(|_| "unknown".to_owned()),
|
|
"recorded_unix_seconds": unix_seconds()?,
|
|
"elapsed_seconds": started.elapsed().as_secs_f64(),
|
|
"stages": recorded,
|
|
"failure": failure,
|
|
});
|
|
write_json_evidence(evidence_path, &value)?;
|
|
if let Some(message) = value["failure"].as_str() {
|
|
return Err(MatrixError::new(message));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn run_required(
|
|
root: &Path,
|
|
manifest: &CoverageManifest,
|
|
rust_host: &str,
|
|
expected_host: &str,
|
|
started: Instant,
|
|
evidence: &mut GateEvidence,
|
|
) -> Result<()> {
|
|
if rust_host != expected_host {
|
|
return Err(MatrixError::new(format!(
|
|
"required CI must use {expected_host}; rustc reports {rust_host}"
|
|
)));
|
|
}
|
|
let archive = skia_archive_path()?;
|
|
validate_skia_archive(&archive)?;
|
|
validate_openjpeg_identity()?;
|
|
let skia_url = file_url(&archive)?;
|
|
let target_dir = std::env::var_os("CARGO_TARGET_DIR")
|
|
.map_or_else(|| root.join("target/required"), PathBuf::from);
|
|
let jobs = std::env::var("CARGO_BUILD_JOBS").unwrap_or_else(|_| "2".to_owned());
|
|
let common_env = [
|
|
(OsString::from("CARGO_INCREMENTAL"), OsString::from("0")),
|
|
(OsString::from("CARGO_BUILD_JOBS"), OsString::from(jobs)),
|
|
(
|
|
OsString::from("CARGO_TARGET_DIR"),
|
|
target_dir.into_os_string(),
|
|
),
|
|
(
|
|
OsString::from("SKIA_BINARIES_URL"),
|
|
OsString::from(skia_url),
|
|
),
|
|
];
|
|
|
|
run_stage(
|
|
root,
|
|
"static-generated-api",
|
|
&static_commands(),
|
|
&common_env,
|
|
started,
|
|
manifest,
|
|
evidence,
|
|
)?;
|
|
run_report_audits(root)?;
|
|
evidence.stages.push(StageEvidence {
|
|
id: "reviewed-reports".to_owned(),
|
|
elapsed_seconds: 0.0,
|
|
commands: 4,
|
|
status: "ok",
|
|
});
|
|
prove_skia_binary(root, &archive, &common_env, evidence)?;
|
|
enforce_budget(started, manifest)?;
|
|
run_stage(
|
|
root,
|
|
"compile-test-features",
|
|
&test_commands(),
|
|
&common_env,
|
|
started,
|
|
manifest,
|
|
evidence,
|
|
)?;
|
|
run_stage(
|
|
root,
|
|
"program-api-smoke",
|
|
&program_commands(),
|
|
&common_env,
|
|
started,
|
|
manifest,
|
|
evidence,
|
|
)?;
|
|
run_stage(
|
|
root,
|
|
"clippy-docs",
|
|
&quality_commands(),
|
|
&common_env,
|
|
started,
|
|
manifest,
|
|
evidence,
|
|
)?;
|
|
run_stage(
|
|
root,
|
|
"policy-evidence",
|
|
&policy_commands(),
|
|
&common_env,
|
|
started,
|
|
manifest,
|
|
evidence,
|
|
)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn static_commands() -> Vec<CommandSpec> {
|
|
vec![
|
|
CommandSpec::new("cargo", &["fmt", "--all", "--", "--check"]),
|
|
CommandSpec::new("python3", &["tools/generate_rust_mapping.py", "--check"]),
|
|
CommandSpec::new("python3", &["tools/generate_api_shims.py", "--check"]),
|
|
CommandSpec::new("python3", &["tools/check_api_coverage.py"]),
|
|
CommandSpec::new("python3", &["tools/check_test_parity.py"]),
|
|
CommandSpec::new("python3", &["tools/generate_lsl_tables.py", "--check"]),
|
|
CommandSpec::new("python3", &["tools/check_milestone_06.py"]),
|
|
CommandSpec::new("python3", &["tools/check_milestone_09.py"]),
|
|
CommandSpec::new("python3", &["tools/check_milestone_10.py"]),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&["test", "--locked", "-p", "libremetaverse-codegen"],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"run",
|
|
"--locked",
|
|
"-p",
|
|
"libremetaverse-codegen",
|
|
"--",
|
|
"check",
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
fn test_commands() -> Vec<CommandSpec> {
|
|
vec![
|
|
// Build every product/compatibility library and integration test in
|
|
// one graph. Package-qualified feature selection covers every core
|
|
// feature without enabling unrelated optional features on consumers.
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"test",
|
|
"--workspace",
|
|
"--lib",
|
|
"--test",
|
|
"*",
|
|
"--features",
|
|
"libremetaverse/jpeg2000,libremetaverse/vorbis",
|
|
"--exclude",
|
|
"libremetaverse-programs",
|
|
"--exclude",
|
|
"libremetaverse-codegen",
|
|
"--exclude",
|
|
"metacrate-performance",
|
|
"--exclude",
|
|
"metacrate-ci-matrix",
|
|
"--exclude",
|
|
"metacrate-concurrency-audit",
|
|
"--jobs",
|
|
"1",
|
|
"--locked",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"check",
|
|
"-p",
|
|
"libremetaverse",
|
|
"--lib",
|
|
"--no-default-features",
|
|
"--locked",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"check",
|
|
"-p",
|
|
"libremetaverse-voice-webrtc",
|
|
"--all-targets",
|
|
"--features",
|
|
"real-audio",
|
|
"--locked",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"test",
|
|
"-p",
|
|
"libremetaverse-imaging",
|
|
"--no-default-features",
|
|
"--features",
|
|
"jpeg2000",
|
|
"--locked",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"test",
|
|
"-p",
|
|
"libremetaverse-imaging",
|
|
"--no-default-features",
|
|
"--features",
|
|
"rust-j2k",
|
|
"--locked",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"test",
|
|
"-p",
|
|
"libremetaverse-imaging",
|
|
"--no-default-features",
|
|
"--features",
|
|
"jpeg2000,rust-j2k",
|
|
"--locked",
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
fn program_commands() -> Vec<CommandSpec> {
|
|
vec![
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"test",
|
|
"--locked",
|
|
"-p",
|
|
"libremetaverse-programs",
|
|
"--test",
|
|
"*",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"run",
|
|
"--locked",
|
|
"-p",
|
|
"libremetaverse-programs",
|
|
"--bin",
|
|
"live-grid-smoke",
|
|
"--",
|
|
"--audit-only",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"run",
|
|
"--locked",
|
|
"-p",
|
|
"libremetaverse-programs",
|
|
"--bin",
|
|
"live-grid-smoke",
|
|
"--",
|
|
"--fake",
|
|
"--evidence",
|
|
"artifacts/ci/fake-grid-smoke.jsonl",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"check",
|
|
"--locked",
|
|
"--manifest-path",
|
|
"tests/api-compile/Cargo.toml",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"run",
|
|
"--locked",
|
|
"--manifest-path",
|
|
"tests/semver-port/Cargo.toml",
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
fn quality_commands() -> Vec<CommandSpec> {
|
|
vec![
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"clippy",
|
|
"--workspace",
|
|
"--all-targets",
|
|
"--all-features",
|
|
"--locked",
|
|
"--",
|
|
"-D",
|
|
"warnings",
|
|
],
|
|
),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"doc",
|
|
"--workspace",
|
|
"--all-features",
|
|
"--no-deps",
|
|
"--locked",
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
fn policy_commands() -> Vec<CommandSpec> {
|
|
vec![
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"deny",
|
|
"check",
|
|
"advisories",
|
|
"licenses",
|
|
"bans",
|
|
"sources",
|
|
"--hide-inclusion-graph",
|
|
],
|
|
),
|
|
// Invoke the pinned standalone executable directly. In a `cargo run`
|
|
// child process, some Cargo installations forward the plugin name to
|
|
// cargo-machete, which 0.9.2 interprets as a directory to scan.
|
|
CommandSpec::new("cargo-machete", &["--with-metadata"]),
|
|
CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"run",
|
|
"--locked",
|
|
"-p",
|
|
"metacrate-performance",
|
|
"--",
|
|
"audit",
|
|
"--fixture-root",
|
|
"benchmarks/fixtures",
|
|
"--rust",
|
|
"benchmarks/results/rust-linux-x86_64.json",
|
|
"--reference",
|
|
"benchmarks/results/csharp-linux-x86_64.json",
|
|
"--comparison",
|
|
"benchmarks/results/comparison.json",
|
|
],
|
|
),
|
|
]
|
|
}
|
|
|
|
fn run_stage(
|
|
root: &Path,
|
|
id: &str,
|
|
commands: &[CommandSpec],
|
|
environment: &[(OsString, OsString)],
|
|
gate_started: Instant,
|
|
manifest: &CoverageManifest,
|
|
evidence: &mut GateEvidence,
|
|
) -> Result<()> {
|
|
enforce_budget(gate_started, manifest)?;
|
|
let stage_started = Instant::now();
|
|
for command in commands {
|
|
println!(
|
|
"ci-required {id}: {} {}",
|
|
command.program,
|
|
command.args.join(" ")
|
|
);
|
|
let status = configured_command(root, command, environment)
|
|
.stdin(Stdio::null())
|
|
.status()?;
|
|
if !status.success() {
|
|
evidence.stages.push(StageEvidence {
|
|
id: id.to_owned(),
|
|
elapsed_seconds: stage_started.elapsed().as_secs_f64(),
|
|
commands: commands.len(),
|
|
status: "failed",
|
|
});
|
|
return Err(MatrixError::new(format!(
|
|
"stage {id} failed while running {}",
|
|
command.program
|
|
)));
|
|
}
|
|
enforce_budget(gate_started, manifest)?;
|
|
}
|
|
evidence.stages.push(StageEvidence {
|
|
id: id.to_owned(),
|
|
elapsed_seconds: stage_started.elapsed().as_secs_f64(),
|
|
commands: commands.len(),
|
|
status: "ok",
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
fn prove_skia_binary(
|
|
root: &Path,
|
|
archive: &Path,
|
|
environment: &[(OsString, OsString)],
|
|
evidence: &mut GateEvidence,
|
|
) -> Result<()> {
|
|
let proof_target = environment
|
|
.iter()
|
|
.find(|(name, _)| name == "CARGO_TARGET_DIR")
|
|
.map(|(_, value)| PathBuf::from(value))
|
|
.ok_or_else(|| MatrixError::new("CARGO_TARGET_DIR is required for the Skia proof"))?;
|
|
let started = Instant::now();
|
|
let spec = CommandSpec::new(
|
|
"cargo",
|
|
&[
|
|
"test",
|
|
"-vv",
|
|
"--locked",
|
|
"-p",
|
|
"libremetaverse-imaging-skia",
|
|
"--no-default-features",
|
|
"--features",
|
|
"skia",
|
|
],
|
|
);
|
|
let mut command = configured_command(root, &spec, environment);
|
|
command
|
|
.env("CARGO_TARGET_DIR", &proof_target)
|
|
.env("SKIA_BINARIES_URL", file_url(archive)?)
|
|
.env_remove("FORCE_SKIA_BINARIES_DOWNLOAD")
|
|
.env_remove("FORCE_SKIA_BUILD")
|
|
.stdin(Stdio::null());
|
|
let output = command.output()?;
|
|
std::io::stdout().write_all(&output.stdout)?;
|
|
std::io::stderr().write_all(&output.stderr)?;
|
|
let mut log = format!(
|
|
"{}\n{}",
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
append_skia_build_output(&proof_target, &mut log)?;
|
|
evidence.skia.download_and_install_succeeded = log.contains(SKIA_SUCCESS);
|
|
evidence.skia.source_build_started = log.contains(SKIA_SOURCE_BUILD);
|
|
let status = if output.status.success()
|
|
&& evidence.skia.download_and_install_succeeded
|
|
&& !evidence.skia.source_build_started
|
|
{
|
|
"ok"
|
|
} else {
|
|
"failed"
|
|
};
|
|
evidence.stages.push(StageEvidence {
|
|
id: "skia-binary-proof".to_owned(),
|
|
elapsed_seconds: started.elapsed().as_secs_f64(),
|
|
commands: 1,
|
|
status,
|
|
});
|
|
if status != "ok" {
|
|
return Err(MatrixError::new(
|
|
"Skia proof did not install the pinned binary archive without starting a source build",
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn append_skia_build_output(target_dir: &Path, log: &mut String) -> Result<()> {
|
|
let build_dir = target_dir.join("debug/build");
|
|
if !build_dir.is_dir() {
|
|
return Ok(());
|
|
}
|
|
for entry in fs::read_dir(build_dir)? {
|
|
let entry = entry?;
|
|
if !entry
|
|
.file_name()
|
|
.to_string_lossy()
|
|
.starts_with("skia-bindings-")
|
|
{
|
|
continue;
|
|
}
|
|
let output = entry.path().join("output");
|
|
if output.is_file() {
|
|
log.push('\n');
|
|
log.push_str(&fs::read_to_string(output)?);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn run_report_audits(root: &Path) -> Result<()> {
|
|
write_api_baseline(root)?;
|
|
write_documentation_report(root)?;
|
|
ensure_git_clean_paths(
|
|
root,
|
|
&[
|
|
"api/SEMVER-BASELINE.json",
|
|
"api/SEMVER-AUDIT.md",
|
|
"api/DOCUMENTATION-COVERAGE.md",
|
|
],
|
|
)?;
|
|
let artifacts = root.join("artifacts/ci");
|
|
fs::create_dir_all(&artifacts)?;
|
|
let audits = [
|
|
("api-audit.json", "ci/evidence/api-audit.json"),
|
|
(
|
|
"documentation-audit.json",
|
|
"ci/evidence/documentation-audit.json",
|
|
),
|
|
("dependency-audit.json", ""),
|
|
("provenance-audit.json", "ci/evidence/provenance-audit.json"),
|
|
];
|
|
for (name, _) in audits {
|
|
let path = artifacts.join(name);
|
|
if path.exists() {
|
|
fs::remove_file(path)?;
|
|
}
|
|
}
|
|
audit_api_surface(root, &artifacts.join("api-audit.json"))?;
|
|
audit_documentation(root, &artifacts.join("documentation-audit.json"))?;
|
|
audit_dependencies(root, &artifacts.join("dependency-audit.json"))?;
|
|
audit_provenance(root, &artifacts.join("provenance-audit.json"))?;
|
|
for (name, committed) in audits.into_iter().filter(|(_, path)| !path.is_empty()) {
|
|
if !evidence_matches_except_timestamp(&artifacts.join(name), &root.join(committed))? {
|
|
return Err(MatrixError::new(format!(
|
|
"{committed} does not match freshly generated audit evidence"
|
|
)));
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn evidence_matches_except_timestamp(actual: &Path, expected: &Path) -> Result<bool> {
|
|
let mut actual: serde_json::Value = serde_json::from_slice(&fs::read(actual)?)?;
|
|
let mut expected: serde_json::Value = serde_json::from_slice(&fs::read(expected)?)?;
|
|
for value in [&mut actual, &mut expected] {
|
|
if let Some(object) = value.as_object_mut() {
|
|
for volatile in [
|
|
"recorded_unix_seconds",
|
|
"source_file_count",
|
|
"distribution_manifest_sha256",
|
|
] {
|
|
object.remove(volatile);
|
|
}
|
|
}
|
|
}
|
|
Ok(actual == expected)
|
|
}
|
|
|
|
fn validate_coverage(manifest: &CoverageManifest) -> Result<()> {
|
|
if manifest.schema != 1
|
|
|| manifest.required_workflow != REQUIRED_WORKFLOW
|
|
|| manifest.release_workflow != RELEASE_WORKFLOW
|
|
|| manifest.hard_timeout_minutes != 15
|
|
|| manifest.internal_target_seconds > 720
|
|
|| manifest.legacy_workflows.len() != LEGACY_WORKFLOW_COUNT
|
|
{
|
|
return Err(MatrixError::new(
|
|
"CI coverage manifest has an invalid gate shape",
|
|
));
|
|
}
|
|
let required = unique(
|
|
manifest.required_checks.iter().map(String::as_str),
|
|
"required check",
|
|
)?;
|
|
let workflows = unique(
|
|
manifest
|
|
.legacy_workflows
|
|
.iter()
|
|
.map(|entry| entry.workflow.as_str()),
|
|
"legacy workflow",
|
|
)?;
|
|
if workflows != BTreeSet::from(LEGACY_WORKFLOWS) {
|
|
return Err(MatrixError::new("legacy workflow inventory is incomplete"));
|
|
}
|
|
let mut routed_required = BTreeSet::new();
|
|
for entry in &manifest.legacy_workflows {
|
|
if entry.responsibility.trim().is_empty()
|
|
|| entry.rationale.trim().is_empty()
|
|
|| entry.checks.is_empty()
|
|
|| !matches!(
|
|
entry.destination.as_str(),
|
|
"required" | "release" | "split" | "retired"
|
|
)
|
|
{
|
|
return Err(MatrixError::new(format!(
|
|
"{} has an incomplete coverage route",
|
|
entry.workflow
|
|
)));
|
|
}
|
|
if matches!(entry.destination.as_str(), "required" | "split") {
|
|
routed_required.extend(
|
|
entry
|
|
.checks
|
|
.iter()
|
|
.filter(|check| required.contains(check.as_str()))
|
|
.map(String::as_str),
|
|
);
|
|
}
|
|
}
|
|
if routed_required != required {
|
|
return Err(MatrixError::new(
|
|
"not every required check is routed from the legacy inventory",
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn validate_workflows(root: &Path, manifest: &CoverageManifest) -> Result<()> {
|
|
let directory = root.join(".gitea/workflows");
|
|
let mut workflow_paths = Vec::new();
|
|
for entry in fs::read_dir(&directory)? {
|
|
let path = entry?.path();
|
|
if matches!(
|
|
path.extension().and_then(OsStr::to_str),
|
|
Some("yml" | "yaml")
|
|
) {
|
|
workflow_paths.push(path);
|
|
}
|
|
}
|
|
workflow_paths.sort();
|
|
if workflow_paths.len() > 3 || workflow_paths.len() != 2 {
|
|
return Err(MatrixError::new(
|
|
"exactly the required and non-routine release workflows must remain",
|
|
));
|
|
}
|
|
let required = fs::read_to_string(root.join(&manifest.required_workflow))?;
|
|
let release = fs::read_to_string(root.join(&manifest.release_workflow))?;
|
|
for marker in [
|
|
"push:",
|
|
"pull_request:",
|
|
"timeout-minutes: 15",
|
|
"cancel-in-progress: true",
|
|
"runs-on: ubuntu-latest",
|
|
"required-gate",
|
|
"python3 tools/normalize_git_mtimes.py --state",
|
|
".metacrate-mtimes.json\" --check",
|
|
"'**/Cargo.toml'",
|
|
"METACRATE_DEPENDENCY_CACHE_HIT",
|
|
"cp -al \"$previous_target\" \"$cargo_target\"",
|
|
] {
|
|
if !required.contains(marker) {
|
|
return Err(MatrixError::new(format!(
|
|
"required workflow is missing {marker}"
|
|
)));
|
|
}
|
|
}
|
|
if required.contains("cargo clean --target-dir") {
|
|
return Err(MatrixError::new(
|
|
"cold dependency/native validation must retain the keyed compiled target",
|
|
));
|
|
}
|
|
if required.contains("rm -rf -- \"$CARGO_HOME/registry\"") {
|
|
return Err(MatrixError::new(
|
|
"cold validation must preserve immutable extracted sources for Cargo fingerprints",
|
|
));
|
|
}
|
|
if required.contains("git show -s --format=%ct HEAD") {
|
|
return Err(MatrixError::new(
|
|
"tracked mtimes must be blob-stable across commits, not commit-wide",
|
|
));
|
|
}
|
|
if release.contains("pull_request:") || !release.contains("workflow_dispatch:") {
|
|
return Err(MatrixError::new(
|
|
"release workflow must be explicitly non-routine",
|
|
));
|
|
}
|
|
for path in &workflow_paths {
|
|
let text = fs::read_to_string(path)?;
|
|
if text
|
|
.lines()
|
|
.filter_map(|line| line.trim().strip_prefix("runs-on:"))
|
|
.any(|runner| runner.trim() != "ubuntu-latest")
|
|
{
|
|
return Err(MatrixError::new(format!(
|
|
"{} violates the ubuntu-only runner policy",
|
|
path.display()
|
|
)));
|
|
}
|
|
if text.contains("FORCE_SKIA_BINARIES_DOWNLOAD") {
|
|
return Err(MatrixError::new(format!(
|
|
"{} misuses FORCE_SKIA_BINARIES_DOWNLOAD",
|
|
path.display()
|
|
)));
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn validate_skia_configuration(root: &Path) -> Result<()> {
|
|
let manifest = fs::read_to_string(root.join("crates/libremetaverse-imaging-skia/Cargo.toml"))?;
|
|
if manifest.matches("no-compile").count() < 3 || !manifest.contains("binary-cache") {
|
|
return Err(MatrixError::new(
|
|
"every Skia target must fail fast instead of compiling from source",
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn validate_skia_archive(path: &Path) -> Result<()> {
|
|
if path.file_name().and_then(OsStr::to_str) != Some(SKIA_ARCHIVE) {
|
|
return Err(MatrixError::new(
|
|
"Skia archive name does not select the pinned ARM64 GNU feature tuple",
|
|
));
|
|
}
|
|
if hash_file(path)? != SKIA_SHA256 {
|
|
return Err(MatrixError::new(
|
|
"Skia archive SHA-256 does not match the reviewed binary",
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn validate_openjpeg_identity() -> Result<()> {
|
|
let prefix = std::env::var_os("OPENJPEG_PREFIX")
|
|
.map(PathBuf::from)
|
|
.ok_or_else(|| MatrixError::new("OPENJPEG_PREFIX is required"))?;
|
|
let identity = fs::read_to_string(prefix.join("metacrate-openjpeg.identity"))?;
|
|
for expected in [
|
|
"version=2.5.4",
|
|
"commit=6c4a29b00211eb0430fa0e5e890f1ce5c80f409f",
|
|
] {
|
|
if !identity.lines().any(|line| line == expected) {
|
|
return Err(MatrixError::new(format!(
|
|
"OpenJPEG cache identity is missing {expected}"
|
|
)));
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn coverage(root: &Path) -> Result<CoverageManifest> {
|
|
Ok(serde_json::from_slice(&fs::read(
|
|
root.join(COVERAGE_PATH),
|
|
)?)?)
|
|
}
|
|
|
|
fn configured_command(
|
|
root: &Path,
|
|
spec: &CommandSpec,
|
|
environment: &[(OsString, OsString)],
|
|
) -> Command {
|
|
let program: OsString = if spec.program == "cargo" {
|
|
std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into())
|
|
} else {
|
|
spec.program.into()
|
|
};
|
|
let mut command = Command::new(program);
|
|
command.args(spec.args).current_dir(root);
|
|
for (key, value) in environment {
|
|
command.env(key, value);
|
|
}
|
|
command
|
|
}
|
|
|
|
fn enforce_budget(started: Instant, manifest: &CoverageManifest) -> Result<()> {
|
|
if started.elapsed().as_secs() >= manifest.internal_target_seconds {
|
|
return Err(MatrixError::new(format!(
|
|
"required CI exceeded its {} second internal target",
|
|
manifest.internal_target_seconds
|
|
)));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn rust_host(root: &Path) -> Result<String> {
|
|
let verbose = command_line(root, "rustc", &["-vV"])?;
|
|
verbose
|
|
.lines()
|
|
.find_map(|line| line.strip_prefix("host: ").map(str::to_owned))
|
|
.ok_or_else(|| MatrixError::new("rustc -vV did not report a host"))
|
|
}
|
|
|
|
fn command_line(root: &Path, program: &str, args: &[&str]) -> Result<String> {
|
|
let output = Command::new(program)
|
|
.args(args)
|
|
.current_dir(root)
|
|
.output()?;
|
|
if !output.status.success() {
|
|
return Err(MatrixError::new(format!("{program} command failed")));
|
|
}
|
|
String::from_utf8(output.stdout)
|
|
.map(|text| text.trim().to_owned())
|
|
.map_err(|_| MatrixError::new(format!("{program} output was not UTF-8")))
|
|
}
|
|
|
|
fn ensure_git_clean_paths(root: &Path, paths: &[&str]) -> Result<()> {
|
|
let mut command = Command::new("git");
|
|
command
|
|
.args(["diff", "--exit-code", "--"])
|
|
.args(paths)
|
|
.current_dir(root);
|
|
if !command.status()?.success() {
|
|
return Err(MatrixError::new("reviewed generated reports are stale"));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn skia_archive_path() -> Result<PathBuf> {
|
|
std::env::var_os("METACRATE_SKIA_ARCHIVE")
|
|
.map(PathBuf::from)
|
|
.ok_or_else(|| MatrixError::new("METACRATE_SKIA_ARCHIVE is required"))
|
|
}
|
|
|
|
fn hash_file(path: &Path) -> Result<String> {
|
|
let mut file = fs::File::open(path)?;
|
|
let mut hasher = Sha256::new();
|
|
let mut buffer = vec![0_u8; 64 * 1024].into_boxed_slice();
|
|
loop {
|
|
let read = file.read(&mut buffer)?;
|
|
if read == 0 {
|
|
break;
|
|
}
|
|
hasher.update(&buffer[..read]);
|
|
}
|
|
Ok(hasher
|
|
.finalize()
|
|
.iter()
|
|
.fold(String::with_capacity(64), |mut output, byte| {
|
|
use std::fmt::Write as _;
|
|
write!(output, "{byte:02x}").expect("writing to a String cannot fail");
|
|
output
|
|
}))
|
|
}
|
|
|
|
fn file_url(path: &Path) -> Result<String> {
|
|
let absolute = path.canonicalize()?;
|
|
let text = absolute
|
|
.to_str()
|
|
.ok_or_else(|| MatrixError::new("Skia archive path is not UTF-8"))?
|
|
.replace('\\', "/");
|
|
if text.starts_with('/') {
|
|
Ok(format!("file://{text}"))
|
|
} else {
|
|
Ok(format!("file:///{text}"))
|
|
}
|
|
}
|
|
|
|
fn cache_hit(name: &str) -> Option<bool> {
|
|
std::env::var(name)
|
|
.ok()
|
|
.and_then(|value| match value.as_str() {
|
|
"true" => Some(true),
|
|
"false" => Some(false),
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
fn peak_memory_kib() -> Option<u64> {
|
|
if let Ok(bytes) = fs::read_to_string("/sys/fs/cgroup/memory.peak")
|
|
&& let Ok(bytes) = bytes.trim().parse::<u64>()
|
|
{
|
|
return Some(bytes / 1024);
|
|
}
|
|
fs::read_to_string("/proc/self/status")
|
|
.ok()
|
|
.and_then(|status| {
|
|
status.lines().find_map(|line| {
|
|
line.strip_prefix("VmHWM:")?
|
|
.split_whitespace()
|
|
.next()?
|
|
.parse()
|
|
.ok()
|
|
})
|
|
})
|
|
}
|
|
|
|
fn unix_seconds() -> Result<u64> {
|
|
SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.map(|duration| duration.as_secs())
|
|
.map_err(|_| MatrixError::new("system clock predates Unix epoch"))
|
|
}
|
|
|
|
fn prepare_new_evidence(path: &Path) -> Result<()> {
|
|
if path.exists() {
|
|
return Err(MatrixError::new(format!(
|
|
"{} already exists",
|
|
path.display()
|
|
)));
|
|
}
|
|
if let Some(parent) = path.parent() {
|
|
fs::create_dir_all(parent)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn write_evidence(path: &Path, evidence: &GateEvidence) -> Result<()> {
|
|
let mut file = OpenOptions::new().write(true).create_new(true).open(path)?;
|
|
serde_json::to_writer_pretty(&mut file, evidence)?;
|
|
file.write_all(b"\n")?;
|
|
file.sync_all()?;
|
|
Ok(())
|
|
}
|
|
|
|
fn write_json_evidence(path: &Path, evidence: &serde_json::Value) -> Result<()> {
|
|
let mut file = OpenOptions::new().write(true).create_new(true).open(path)?;
|
|
serde_json::to_writer_pretty(&mut file, evidence)?;
|
|
file.write_all(b"\n")?;
|
|
file.sync_all()?;
|
|
Ok(())
|
|
}
|
|
|
|
fn unique<'a>(values: impl Iterator<Item = &'a str>, kind: &str) -> Result<BTreeSet<&'a str>> {
|
|
let mut result = BTreeSet::new();
|
|
for value in values {
|
|
if value.trim().is_empty() || !result.insert(value) {
|
|
return Err(MatrixError::new(format!(
|
|
"{kind} values must be nonempty and unique"
|
|
)));
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn checked_in_coverage_routes_every_required_check() {
|
|
let manifest: CoverageManifest =
|
|
serde_json::from_str(include_str!("../../../ci/ci-coverage.json")).unwrap();
|
|
validate_coverage(&manifest).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn checked_in_workflow_split_is_bounded_and_ubuntu_only() {
|
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
|
|
audit_consolidated_ci(&root).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn pinned_skia_archive_identity_is_exact() {
|
|
assert!(SKIA_ARCHIVE.contains("aarch64-unknown-linux-gnu"));
|
|
assert!(SKIA_ARCHIVE.contains("jpegd-jpege-pdf-svg-textlayout-vulkan-webpd-webpe"));
|
|
assert_eq!(SKIA_SHA256.len(), 64);
|
|
}
|
|
|
|
#[test]
|
|
fn product_and_compatibility_graph_is_the_only_serial_command() {
|
|
let commands = test_commands();
|
|
assert_eq!(
|
|
commands[0].args,
|
|
[
|
|
"test",
|
|
"--workspace",
|
|
"--lib",
|
|
"--test",
|
|
"*",
|
|
"--features",
|
|
"libremetaverse/jpeg2000,libremetaverse/vorbis",
|
|
"--exclude",
|
|
"libremetaverse-programs",
|
|
"--exclude",
|
|
"libremetaverse-codegen",
|
|
"--exclude",
|
|
"metacrate-performance",
|
|
"--exclude",
|
|
"metacrate-ci-matrix",
|
|
"--exclude",
|
|
"metacrate-concurrency-audit",
|
|
"--jobs",
|
|
"1",
|
|
"--locked",
|
|
]
|
|
);
|
|
assert!(
|
|
commands[1..]
|
|
.iter()
|
|
.all(|command| !command.args.windows(2).any(|pair| pair == ["--jobs", "1"]))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn machete_uses_the_standalone_cli_contract() {
|
|
let commands = policy_commands();
|
|
let machete = commands
|
|
.iter()
|
|
.find(|command| command.program == "cargo-machete")
|
|
.expect("policy gate invokes cargo-machete directly");
|
|
assert_eq!(machete.args, ["--with-metadata"]);
|
|
assert!(
|
|
!commands
|
|
.iter()
|
|
.any(|command| command.program == "cargo"
|
|
&& command.args.first() == Some(&"machete"))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn performance_audit_reuses_the_required_gate_build_profile() {
|
|
let commands = policy_commands();
|
|
let performance = commands
|
|
.iter()
|
|
.find(|command| {
|
|
command.program == "cargo"
|
|
&& command
|
|
.args
|
|
.windows(2)
|
|
.any(|pair| pair == ["-p", "metacrate-performance"])
|
|
})
|
|
.expect("policy gate runs the performance evidence audit");
|
|
|
|
assert!(performance.args.starts_with(&["run", "--locked"]));
|
|
assert!(
|
|
!performance.args.contains(&"--profile"),
|
|
"the audit must reuse the gate's existing build artifacts instead of compiling a second profile"
|
|
);
|
|
}
|
|
}
|