diff options
| author | Valentin Popov <valentin@popov.link> | 2026-06-25 04:00:37 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-06-25 10:45:33 +0300 |
| commit | d1b7b43dce1b7a343d9e02f2bd870f8575828793 (patch) | |
| tree | 53c21cdce33ba6869bdb7755a932a742d512570d | |
| parent | adc6c6149c290e8208837d365ba3df63ba7a5d38 (diff) | |
| download | fparkan-d1b7b43dce1b7a343d9e02f2bd870f8575828793.tar.xz fparkan-d1b7b43dce1b7a343d9e02f2bd870f8575828793.zip | |
feat(stage0): record artifact provenance metadata
| -rw-r--r-- | apps/fparkan-vulkan-smoke/src/main.rs | 27 | ||||
| -rw-r--r-- | fixtures/acceptance/coverage.tsv | 4 | ||||
| -rw-r--r-- | xtask/src/main.rs | 58 |
3 files changed, 87 insertions, 2 deletions
diff --git a/apps/fparkan-vulkan-smoke/src/main.rs b/apps/fparkan-vulkan-smoke/src/main.rs index bd7200f..13451a3 100644 --- a/apps/fparkan-vulkan-smoke/src/main.rs +++ b/apps/fparkan-vulkan-smoke/src/main.rs @@ -353,6 +353,8 @@ fn render_smoke_report_json( let fields = vec![ ("schema_version", json_string(SCHEMA_VERSION)), ("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())), ("target_triple", json_string(¤t_rustc_host_triple())), ("platform", json_string(actual_platform())), @@ -460,6 +462,16 @@ fn current_git_commit_sha() -> 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 { Command::new("rustc") .arg("-Vv") @@ -475,6 +487,21 @@ fn current_rustc_release() -> 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 { Command::new("rustc") .arg("-Vv") diff --git a/fixtures/acceptance/coverage.tsv b/fixtures/acceptance/coverage.tsv index 14d4132..a66e9dd 100644 --- a/fixtures/acceptance/coverage.tsv +++ b/fixtures/acceptance/coverage.tsv @@ -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-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-010 covered cargo xtask acceptance audit emits commit_sha, 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-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, 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-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 diff --git a/xtask/src/main.rs b/xtask/src/main.rs index e4023ce..7b02a52 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -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_error_count", 0, 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( platform, 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( platform: &str, report: &serde_json::Value, @@ -1809,6 +1824,8 @@ impl CoverageStatus { #[derive(Clone, Debug, Eq, PartialEq)] struct AcceptanceAudit { commit_sha: String, + git_dirty: bool, + runner_identity: String, rust_toolchain: String, msrv: String, required_total: usize, @@ -1957,6 +1974,8 @@ fn build_acceptance_audit( AcceptanceAudit { commit_sha: current_git_commit_sha(), + git_dirty: current_git_dirty(), + runner_identity: measured_runner_identity(), rust_toolchain: measured_rust_toolchain_version(), msrv: WORKSPACE_MSRV.to_string(), required_total: required.len(), @@ -1978,6 +1997,8 @@ fn render_audit_json(audit: &AcceptanceAudit) -> String { "{{\n", " \"schema_version\": \"fparkan-acceptance-coverage-v1\",\n", " \"commit_sha\": \"{}\",\n", + " \"git_dirty\": {},\n", + " \"runner_identity\": \"{}\",\n", " \"rust_toolchain\": \"{}\",\n", " \"msrv\": \"{}\",\n", " \"required_total\": {},\n", @@ -1999,6 +2020,8 @@ fn render_audit_json(audit: &AcceptanceAudit) -> String { "}}\n" ), 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.msrv), audit.required_total, @@ -2032,6 +2055,16 @@ fn current_git_commit_sha() -> 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 { Command::new("rustc") .args(["-Vv"]) @@ -2049,6 +2082,21 @@ fn measured_rust_toolchain_version() -> 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 { let pairs = values .iter() @@ -2437,6 +2485,8 @@ mod tests { fn audit_json_escapes_evidence() { let mut audit = AcceptanceAudit { commit_sha: "0123456789abcdef0123456789abcdef01234567".to_string(), + git_dirty: false, + runner_identity: "github-actions/12345/stage0-macos".to_string(), rust_toolchain: PINNED_RUST_TOOLCHAIN.to_string(), msrv: WORKSPACE_MSRV.to_string(), required_total: 1, @@ -2457,6 +2507,8 @@ mod tests { assert!(json.contains("quoted \\\"value\\\"")); 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("\"msrv\": \"1.87\"")); } @@ -2471,6 +2523,8 @@ mod tests { serde_json::json!({ "schema_version": "fparkan-native-smoke-v1", "commit_sha": "0123456789abcdef0123456789abcdef01234567", + "git_dirty": false, + "runner_identity": "github-actions/12345/stage0-macos", "rust_toolchain": measured_rust_toolchain_version(), "target_triple": format!("{platform}-test-target"), "platform": platform, @@ -2510,6 +2564,8 @@ mod tests { serde_json::json!({ "schema_version": "fparkan-native-smoke-v1", "commit_sha": "0123456789abcdef0123456789abcdef01234567", + "git_dirty": "dirty", + "runner_identity": "", "rust_toolchain": measured_rust_toolchain_version(), "target_triple": "aarch64-apple-darwin", "platform": "macos", @@ -2536,6 +2592,8 @@ mod tests { assert!( 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: validation_error_count must be an unsigned integer".to_string())); |
