#!/usr/bin/env python3 """Pin the high-risk contracts in Tangled workflow manifests.""" from __future__ import annotations import subprocess from pathlib import Path ROOT = Path(__file__).resolve().parent.parent WORKFLOWS = ROOT / ".tangled/workflows" RUST_SHARDS = { "core": (WORKFLOWS / "check.yml").read_text(encoding="utf-8"), "bevy tests": (WORKFLOWS / "check-bevy-test.yml").read_text(encoding="utf-8"), "bevy clippy": (WORKFLOWS / "check-bevy-clippy.yml").read_text( encoding="utf-8" ), "bevy build": (WORKFLOWS / "check-bevy-build.yml").read_text( encoding="utf-8" ), } CHECK = RUST_SHARDS["core"] CORPUS = (WORKFLOWS / "corpus.yml").read_text(encoding="utf-8") SPEC = (WORKFLOWS / "spec-check.yml").read_text(encoding="utf-8") PKG_CONFIG = (ROOT / "tools/ci-pkg-config.sh").read_text(encoding="utf-8") CARGO = (ROOT / "Cargo.toml").read_text(encoding="utf-8") def require(haystack: str, needle: str, contract: str) -> None: if needle not in haystack: raise AssertionError(f"{contract}: missing {needle!r}") def push_constraint(workflow: str) -> str: return workflow.split(' - event: ["push"]', 1)[1].split( ' - event: ["pull_request"]', 1 )[0] def main() -> int: expected_shards = { "check.yml", "check-bevy-test.yml", "check-bevy-clippy.yml", "check-bevy-build.yml", } actual_shards = {path.name for path in WORKFLOWS.glob("check*.yml")} if actual_shards != expected_shards: raise AssertionError( "Rust shard inventory differs: " f"expected {sorted(expected_shards)}, got {sorted(actual_shards)}" ) for shard, workflow in RUST_SHARDS.items(): push = push_constraint(workflow) for pattern in ( "Cargo.toml", "Cargo.lock", "build.rs", "rust-toolchain*", ".cargo/**", "crates/**", "src/**", "tests/**", "benches/**", "examples/**", "assets/plots/**", "scenarios/**", "tools/scenario.py", "tools/ci-pkg-config.sh", ".tangled/workflows/check*.yml", ): require( push, f' - "{pattern}"', f"{shard} Rust-impacting push paths", ) require( workflow, ' - event: ["manual"]', f"{shard} manual safety trigger", ) if "classify Rust impact" in workflow or "run-rust-gate" in workflow: raise AssertionError(f"{shard} restored the shallow HEAD^ classifier") push_contracts = {push_constraint(workflow) for workflow in RUST_SHARDS.values()} if len(push_contracts) != 1: raise AssertionError("Rust shards do not share one exact push path inventory") require( CHECK, "cargo test -p misaligned-core -p misaligned-terminal --quiet", "core and terminal test shard", ) require( CHECK, "cargo clippy -p misaligned-core -p misaligned-terminal --all-targets", "core and terminal Clippy shard", ) require(CHECK, " - python3", "scenario runtime dependency") require(CHECK, "python3 tools/scenario.py", "executable scenario coverage") if "cargo test --workspace" in CHECK: raise AssertionError("check.yml restored the deadline-consuming workspace test") ci_profile = CARGO.split("[profile.ci]", 1)[1].split( '[profile.ci.package."*"]', 1 )[0] for setting in ( 'inherits = "dev"', "opt-level = 0", "debug = 0", "incremental = false", ): require(ci_profile, setting, "cold hosted CI profile") ci_dependencies = CARGO.split('[profile.ci.package."*"]', 1)[1] require(ci_dependencies, "opt-level = 0", "cold hosted dependency profile") bevy_commands = { "bevy tests": "cargo test --profile ci -p misaligned-bevy -p misaligned-assets --quiet", "bevy clippy": "cargo clippy -p misaligned-bevy -p misaligned-assets --all-targets", "bevy build": "cargo build --profile ci -p misaligned-bevy", } for shard, cargo_command in bevy_commands.items(): workflow = RUST_SHARDS[shard] if workflow.count(" if cargo ") != 1: raise AssertionError( f"{shard} must contain exactly one deadline-expensive Cargo command" ) setup = workflow.index(' - name: "setup pkg-config"') cargo = workflow.index(cargo_command) if setup > cargo: raise AssertionError(f"{shard} runs before pkg-config setup") require( workflow, "bash tools/ci-pkg-config.sh /tangled/workspace/setup-pc.sh", f"{shard} shared Linux pkg-config setup", ) require( workflow, ". /tangled/workspace/setup-pc.sh", f"{shard} uses Linux pkg-config shims", ) for package in ( "pkg-config", "wayland", "libxkbcommon", "alsa-lib", "eudev", ): require(workflow, f" - {package}", f"{shard} Linux dependency") for pc_name in ( "wayland-client", "wayland-server", "wayland-cursor", "xkbcommon", "alsa asound", "libudev udev", ): require(PKG_CONFIG, f"gen_pc {pc_name}", "shared pkg-config shim") require(PKG_CONFIG, "PKG_CONFIG_PATH", "shared pkg-config environment") syntax = subprocess.run( ["bash", "-n", str(ROOT / "tools/ci-pkg-config.sh")], text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=False, ) if syntax.returncode != 0: raise AssertionError(f"shared pkg-config helper syntax: {syntax.stdout}") require(SPEC, " - gnugrep", "design gate grep dependency") require(SPEC, ' - event: ["manual"]', "manual design-gate trigger") require(SPEC, "--no-renames --diff-filter=ACMRD", "deletion-safe changed paths") require( SPEC, "bash tools/design_amendment_gate.sh", "shared design-amendment policy", ) require( CORPUS, "bash tools/test_design_amendment_gate.sh", "design-amendment fixtures in CI", ) require( CORPUS, "python3 tools/test_ci_workflows.py", "workflow-contract fixtures in CI", ) print("CI workflow fixtures: OK") return 0 if __name__ == "__main__": raise SystemExit(main())