feat(stage0): record artifact provenance metadata
This commit is contained in:
@@ -353,6 +353,8 @@ fn render_smoke_report_json(
|
|||||||
let fields = vec![
|
let fields = vec![
|
||||||
("schema_version", json_string(SCHEMA_VERSION)),
|
("schema_version", json_string(SCHEMA_VERSION)),
|
||||||
("commit_sha", json_string(¤t_git_commit_sha())),
|
("commit_sha", json_string(¤t_git_commit_sha())),
|
||||||
|
("git_dirty", bool_json(current_git_dirty())),
|
||||||
|
("runner_identity", json_string(&measured_runner_identity())),
|
||||||
("rust_toolchain", json_string(¤t_rustc_release())),
|
("rust_toolchain", json_string(¤t_rustc_release())),
|
||||||
("target_triple", json_string(¤t_rustc_host_triple())),
|
("target_triple", json_string(¤t_rustc_host_triple())),
|
||||||
("platform", json_string(actual_platform())),
|
("platform", json_string(actual_platform())),
|
||||||
@@ -460,6 +462,16 @@ fn current_git_commit_sha() -> String {
|
|||||||
.unwrap_or_else(|| "unknown".to_string())
|
.unwrap_or_else(|| "unknown".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_git_dirty() -> bool {
|
||||||
|
Command::new("git")
|
||||||
|
.args(["status", "--short"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|output| output.status.success())
|
||||||
|
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||||
|
.is_some_and(|output| !output.trim().is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
fn current_rustc_release() -> String {
|
fn current_rustc_release() -> String {
|
||||||
Command::new("rustc")
|
Command::new("rustc")
|
||||||
.arg("-Vv")
|
.arg("-Vv")
|
||||||
@@ -475,6 +487,21 @@ fn current_rustc_release() -> String {
|
|||||||
.unwrap_or_else(|| "unknown".to_string())
|
.unwrap_or_else(|| "unknown".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn measured_runner_identity() -> String {
|
||||||
|
if std::env::var_os("GITHUB_ACTIONS").is_some() {
|
||||||
|
let run_id = std::env::var("GITHUB_RUN_ID").unwrap_or_else(|_| "unknown-run".to_string());
|
||||||
|
let job = std::env::var("GITHUB_JOB").unwrap_or_else(|_| "unknown-job".to_string());
|
||||||
|
format!("github-actions/{run_id}/{job}")
|
||||||
|
} else if std::env::var_os("CI").is_some() {
|
||||||
|
let job = std::env::var("CI_JOB_NAME")
|
||||||
|
.or_else(|_| std::env::var("BUILD_ID"))
|
||||||
|
.unwrap_or_else(|_| "generic-ci".to_string());
|
||||||
|
format!("ci/{job}")
|
||||||
|
} else {
|
||||||
|
format!("local/{}", std::env::consts::OS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn current_rustc_host_triple() -> String {
|
fn current_rustc_host_triple() -> String {
|
||||||
Command::new("rustc")
|
Command::new("rustc")
|
||||||
.arg("-Vv")
|
.arg("-Vv")
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ S0-ARCH-006 covered cargo xtask policy rejects non-fparkan package directories u
|
|||||||
S0-ARCH-007 covered cargo xtask ci runs fmt, policy, workspace test, clippy, rustdoc warnings, cargo-deny with reviewed deny.toml, and strict acceptance audit; built-in supply-chain fallback is opt-in local-only and forbidden when CI is set
|
S0-ARCH-007 covered cargo xtask ci runs fmt, policy, workspace test, clippy, rustdoc warnings, cargo-deny with reviewed deny.toml, and strict acceptance audit; built-in supply-chain fallback is opt-in local-only and forbidden when CI is set
|
||||||
S0-ARCH-008 covered cargo xtask policy rejects moving Rust toolchains and workspace rust-version drift
|
S0-ARCH-008 covered cargo xtask policy rejects moving Rust toolchains and workspace rust-version drift
|
||||||
S0-ARCH-009 covered .github/workflows/ci.yml runs a pinned MSRV backend-neutral crate job
|
S0-ARCH-009 covered .github/workflows/ci.yml runs a pinned MSRV backend-neutral crate job
|
||||||
S0-ARCH-010 covered cargo xtask acceptance audit emits commit_sha, rust_toolchain, and msrv metadata into the JSON artifact
|
S0-ARCH-010 covered cargo xtask acceptance audit emits measured commit_sha, git_dirty, runner_identity, rust_toolchain, and msrv metadata into the JSON artifact
|
||||||
S0-ARCH-011 covered .github/workflows/ci.yml runs cargo run -p fparkan-vulkan-smoke --locked -- --out target/fparkan/native-smoke/macos.json and cargo xtask native-smoke audit enforces a passed macOS 300-frame report with measured resize/recreate and validation=0
|
S0-ARCH-011 covered .github/workflows/ci.yml runs cargo run -p fparkan-vulkan-smoke --locked -- --out target/fparkan/native-smoke/macos.json and cargo xtask native-smoke audit enforces a passed macOS 300-frame report with measured resize/recreate, validation=0, and provenance fields for git_dirty and runner_identity
|
||||||
S0-DIAG-001 covered cargo test -p fparkan-diagnostics --offline diagnostic_chain_preserves_context
|
S0-DIAG-001 covered cargo test -p fparkan-diagnostics --offline diagnostic_chain_preserves_context
|
||||||
S0-DIAG-002 covered cargo test -p fparkan-diagnostics --offline json_is_stable
|
S0-DIAG-002 covered cargo test -p fparkan-diagnostics --offline json_is_stable
|
||||||
S0-CORPUS-001 covered cargo test -p fparkan-corpus --offline deterministic_traversal_is_creation_order_independent
|
S0-CORPUS-001 covered cargo test -p fparkan-corpus --offline deterministic_traversal_is_creation_order_independent
|
||||||
|
|||||||
|
@@ -1640,6 +1640,8 @@ fn validate_native_smoke_report(
|
|||||||
expect_u64_field(platform, report, "validation_warning_count", 0, failures);
|
expect_u64_field(platform, report, "validation_warning_count", 0, failures);
|
||||||
expect_u64_field(platform, report, "validation_error_count", 0, failures);
|
expect_u64_field(platform, report, "validation_error_count", 0, failures);
|
||||||
expect_nonempty_string(platform, report, "commit_sha", failures);
|
expect_nonempty_string(platform, report, "commit_sha", failures);
|
||||||
|
expect_bool_field(platform, report, "git_dirty", failures);
|
||||||
|
expect_nonempty_string(platform, report, "runner_identity", failures);
|
||||||
expect_string_field(
|
expect_string_field(
|
||||||
platform,
|
platform,
|
||||||
report,
|
report,
|
||||||
@@ -1712,6 +1714,19 @@ fn expect_nonempty_string(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expect_bool_field(
|
||||||
|
platform: &str,
|
||||||
|
report: &serde_json::Value,
|
||||||
|
field: &str,
|
||||||
|
failures: &mut Vec<String>,
|
||||||
|
) {
|
||||||
|
match report.get(field) {
|
||||||
|
Some(serde_json::Value::Bool(_)) => {}
|
||||||
|
Some(_) => failures.push(format!("{platform}: {field} must be a boolean")),
|
||||||
|
None => failures.push(format!("{platform}: missing {field}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn expect_u64_at_least(
|
fn expect_u64_at_least(
|
||||||
platform: &str,
|
platform: &str,
|
||||||
report: &serde_json::Value,
|
report: &serde_json::Value,
|
||||||
@@ -1809,6 +1824,8 @@ impl CoverageStatus {
|
|||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
struct AcceptanceAudit {
|
struct AcceptanceAudit {
|
||||||
commit_sha: String,
|
commit_sha: String,
|
||||||
|
git_dirty: bool,
|
||||||
|
runner_identity: String,
|
||||||
rust_toolchain: String,
|
rust_toolchain: String,
|
||||||
msrv: String,
|
msrv: String,
|
||||||
required_total: usize,
|
required_total: usize,
|
||||||
@@ -1957,6 +1974,8 @@ fn build_acceptance_audit(
|
|||||||
|
|
||||||
AcceptanceAudit {
|
AcceptanceAudit {
|
||||||
commit_sha: current_git_commit_sha(),
|
commit_sha: current_git_commit_sha(),
|
||||||
|
git_dirty: current_git_dirty(),
|
||||||
|
runner_identity: measured_runner_identity(),
|
||||||
rust_toolchain: measured_rust_toolchain_version(),
|
rust_toolchain: measured_rust_toolchain_version(),
|
||||||
msrv: WORKSPACE_MSRV.to_string(),
|
msrv: WORKSPACE_MSRV.to_string(),
|
||||||
required_total: required.len(),
|
required_total: required.len(),
|
||||||
@@ -1978,6 +1997,8 @@ fn render_audit_json(audit: &AcceptanceAudit) -> String {
|
|||||||
"{{\n",
|
"{{\n",
|
||||||
" \"schema_version\": \"fparkan-acceptance-coverage-v1\",\n",
|
" \"schema_version\": \"fparkan-acceptance-coverage-v1\",\n",
|
||||||
" \"commit_sha\": \"{}\",\n",
|
" \"commit_sha\": \"{}\",\n",
|
||||||
|
" \"git_dirty\": {},\n",
|
||||||
|
" \"runner_identity\": \"{}\",\n",
|
||||||
" \"rust_toolchain\": \"{}\",\n",
|
" \"rust_toolchain\": \"{}\",\n",
|
||||||
" \"msrv\": \"{}\",\n",
|
" \"msrv\": \"{}\",\n",
|
||||||
" \"required_total\": {},\n",
|
" \"required_total\": {},\n",
|
||||||
@@ -1999,6 +2020,8 @@ fn render_audit_json(audit: &AcceptanceAudit) -> String {
|
|||||||
"}}\n"
|
"}}\n"
|
||||||
),
|
),
|
||||||
json_escape(&audit.commit_sha),
|
json_escape(&audit.commit_sha),
|
||||||
|
if audit.git_dirty { "true" } else { "false" },
|
||||||
|
json_escape(&audit.runner_identity),
|
||||||
json_escape(&audit.rust_toolchain),
|
json_escape(&audit.rust_toolchain),
|
||||||
json_escape(&audit.msrv),
|
json_escape(&audit.msrv),
|
||||||
audit.required_total,
|
audit.required_total,
|
||||||
@@ -2032,6 +2055,16 @@ fn current_git_commit_sha() -> String {
|
|||||||
.unwrap_or_else(|| "unknown".to_string())
|
.unwrap_or_else(|| "unknown".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_git_dirty() -> bool {
|
||||||
|
Command::new("git")
|
||||||
|
.args(["status", "--short"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|output| output.status.success())
|
||||||
|
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||||
|
.is_some_and(|output| !output.trim().is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
fn measured_rust_toolchain_version() -> String {
|
fn measured_rust_toolchain_version() -> String {
|
||||||
Command::new("rustc")
|
Command::new("rustc")
|
||||||
.args(["-Vv"])
|
.args(["-Vv"])
|
||||||
@@ -2049,6 +2082,21 @@ fn measured_rust_toolchain_version() -> String {
|
|||||||
.unwrap_or_else(|| PINNED_RUST_TOOLCHAIN.to_string())
|
.unwrap_or_else(|| PINNED_RUST_TOOLCHAIN.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn measured_runner_identity() -> String {
|
||||||
|
if std::env::var_os("GITHUB_ACTIONS").is_some() {
|
||||||
|
let run_id = std::env::var("GITHUB_RUN_ID").unwrap_or_else(|_| "unknown-run".to_string());
|
||||||
|
let job = std::env::var("GITHUB_JOB").unwrap_or_else(|_| "unknown-job".to_string());
|
||||||
|
format!("github-actions/{run_id}/{job}")
|
||||||
|
} else if std::env::var_os("CI").is_some() {
|
||||||
|
let job = std::env::var("CI_JOB_NAME")
|
||||||
|
.or_else(|_| std::env::var("BUILD_ID"))
|
||||||
|
.unwrap_or_else(|_| "generic-ci".to_string());
|
||||||
|
format!("ci/{job}")
|
||||||
|
} else {
|
||||||
|
format!("local/{}", std::env::consts::OS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_string_usize_map(values: &BTreeMap<String, usize>) -> String {
|
fn render_string_usize_map(values: &BTreeMap<String, usize>) -> String {
|
||||||
let pairs = values
|
let pairs = values
|
||||||
.iter()
|
.iter()
|
||||||
@@ -2437,6 +2485,8 @@ mod tests {
|
|||||||
fn audit_json_escapes_evidence() {
|
fn audit_json_escapes_evidence() {
|
||||||
let mut audit = AcceptanceAudit {
|
let mut audit = AcceptanceAudit {
|
||||||
commit_sha: "0123456789abcdef0123456789abcdef01234567".to_string(),
|
commit_sha: "0123456789abcdef0123456789abcdef01234567".to_string(),
|
||||||
|
git_dirty: false,
|
||||||
|
runner_identity: "github-actions/12345/stage0-macos".to_string(),
|
||||||
rust_toolchain: PINNED_RUST_TOOLCHAIN.to_string(),
|
rust_toolchain: PINNED_RUST_TOOLCHAIN.to_string(),
|
||||||
msrv: WORKSPACE_MSRV.to_string(),
|
msrv: WORKSPACE_MSRV.to_string(),
|
||||||
required_total: 1,
|
required_total: 1,
|
||||||
@@ -2457,6 +2507,8 @@ mod tests {
|
|||||||
|
|
||||||
assert!(json.contains("quoted \\\"value\\\""));
|
assert!(json.contains("quoted \\\"value\\\""));
|
||||||
assert!(json.contains("\"commit_sha\": \"0123456789abcdef0123456789abcdef01234567\""));
|
assert!(json.contains("\"commit_sha\": \"0123456789abcdef0123456789abcdef01234567\""));
|
||||||
|
assert!(json.contains("\"git_dirty\": false"));
|
||||||
|
assert!(json.contains("\"runner_identity\": \"github-actions/12345/stage0-macos\""));
|
||||||
assert!(json.contains("\"rust_toolchain\": \"1.87.0\""));
|
assert!(json.contains("\"rust_toolchain\": \"1.87.0\""));
|
||||||
assert!(json.contains("\"msrv\": \"1.87\""));
|
assert!(json.contains("\"msrv\": \"1.87\""));
|
||||||
}
|
}
|
||||||
@@ -2471,6 +2523,8 @@ mod tests {
|
|||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"schema_version": "fparkan-native-smoke-v1",
|
"schema_version": "fparkan-native-smoke-v1",
|
||||||
"commit_sha": "0123456789abcdef0123456789abcdef01234567",
|
"commit_sha": "0123456789abcdef0123456789abcdef01234567",
|
||||||
|
"git_dirty": false,
|
||||||
|
"runner_identity": "github-actions/12345/stage0-macos",
|
||||||
"rust_toolchain": measured_rust_toolchain_version(),
|
"rust_toolchain": measured_rust_toolchain_version(),
|
||||||
"target_triple": format!("{platform}-test-target"),
|
"target_triple": format!("{platform}-test-target"),
|
||||||
"platform": platform,
|
"platform": platform,
|
||||||
@@ -2510,6 +2564,8 @@ mod tests {
|
|||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"schema_version": "fparkan-native-smoke-v1",
|
"schema_version": "fparkan-native-smoke-v1",
|
||||||
"commit_sha": "0123456789abcdef0123456789abcdef01234567",
|
"commit_sha": "0123456789abcdef0123456789abcdef01234567",
|
||||||
|
"git_dirty": "dirty",
|
||||||
|
"runner_identity": "",
|
||||||
"rust_toolchain": measured_rust_toolchain_version(),
|
"rust_toolchain": measured_rust_toolchain_version(),
|
||||||
"target_triple": "aarch64-apple-darwin",
|
"target_triple": "aarch64-apple-darwin",
|
||||||
"platform": "macos",
|
"platform": "macos",
|
||||||
@@ -2536,6 +2592,8 @@ mod tests {
|
|||||||
assert!(
|
assert!(
|
||||||
failures.contains(&"macos: status expected \"passed\", found \"blocked\"".to_string())
|
failures.contains(&"macos: status expected \"passed\", found \"blocked\"".to_string())
|
||||||
);
|
);
|
||||||
|
assert!(failures.contains(&"macos: git_dirty must be a boolean".to_string()));
|
||||||
|
assert!(failures.contains(&"macos: runner_identity must be non-empty".to_string()));
|
||||||
assert!(failures.contains(&"macos: frames expected >= 300, found 0".to_string()));
|
assert!(failures.contains(&"macos: frames expected >= 300, found 0".to_string()));
|
||||||
assert!(failures
|
assert!(failures
|
||||||
.contains(&"macos: validation_error_count must be an unsigned integer".to_string()));
|
.contains(&"macos: validation_error_count must be an unsigned integer".to_string()));
|
||||||
|
|||||||
Reference in New Issue
Block a user