update
This commit is contained in:
Executable
+704
@@ -0,0 +1,704 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
umask 022
|
||||
|
||||
gate_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
|
||||
root=$(CDPATH= cd -- "$gate_dir/../../../.." && pwd -P)
|
||||
input=$gate_dir/P15-031-input.json
|
||||
base=
|
||||
output=
|
||||
deadline=60
|
||||
|
||||
while test "$#" -gt 0; do
|
||||
case "$1" in
|
||||
--base)
|
||||
test "$#" -ge 2 || { printf '%s\n' '--base requires a commit' >&2; exit 20; }
|
||||
base=$2
|
||||
shift 2
|
||||
;;
|
||||
--output)
|
||||
test "$#" -ge 2 || { printf '%s\n' '--output requires a directory' >&2; exit 20; }
|
||||
output=$2
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
printf 'unknown argument: %s\n' "$1" >&2
|
||||
exit 20
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
test -n "$base" || { printf '%s\n' '--base is required' >&2; exit 20; }
|
||||
test -n "$output" || { printf '%s\n' '--output is required' >&2; exit 20; }
|
||||
test -f "$input" || { printf 'missing input: %s\n' "$input" >&2; exit 20; }
|
||||
for tool in git mktemp python3 sha256sum timeout; do
|
||||
command -v "$tool" >/dev/null 2>&1 || {
|
||||
printf 'missing required host tool: %s\n' "$tool" >&2
|
||||
exit 21
|
||||
}
|
||||
done
|
||||
case "$output" in
|
||||
/*) ;;
|
||||
*) output=$PWD/$output ;;
|
||||
esac
|
||||
test ! -e "$output" || { printf 'refusing existing output: %s\n' "$output" >&2; exit 20; }
|
||||
mkdir -p "$output"
|
||||
work=$(mktemp -d "${TMPDIR:-/tmp}/P15-031.XXXXXX")
|
||||
|
||||
cleanup_trap()
|
||||
{
|
||||
rm -rf -- "$work"
|
||||
}
|
||||
trap cleanup_trap EXIT HUP INT TERM
|
||||
|
||||
python3 -B - "$output/command-argv.json" "$0" --base "$base" --output "$output" <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
Path(sys.argv[1]).write_text(
|
||||
json.dumps(sys.argv[2:], ensure_ascii=True, separators=(",", ":")) + "\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
PY
|
||||
python3 -B - "$output/ownership.json" "$work" "$output" <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
Path(sys.argv[1]).write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"owned_temporary_paths": [sys.argv[2]],
|
||||
"persistent_output": sys.argv[3],
|
||||
"owned_processes": [],
|
||||
"owned_ports": [],
|
||||
"owned_overlays": [],
|
||||
"qemu": "NOT_RUN",
|
||||
},
|
||||
ensure_ascii=True,
|
||||
indent=2,
|
||||
sort_keys=True,
|
||||
) + "\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
PY
|
||||
|
||||
set +e
|
||||
timeout -k 5 "$deadline" python3 -B - \
|
||||
"$root" "$input" "$base" "$output" "$deadline" <<'PY' \
|
||||
>"$output/stdout.log" 2>"$output/stderr.log"
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
|
||||
ROOT = Path(sys.argv[1])
|
||||
INPUT = Path(sys.argv[2])
|
||||
REQUESTED_BASE = sys.argv[3]
|
||||
OUTPUT = Path(sys.argv[4])
|
||||
DEADLINE = int(sys.argv[5])
|
||||
SPEC = json.loads(INPUT.read_text(encoding="utf-8"))
|
||||
START_UTC = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
||||
|
||||
|
||||
class GateFailure(RuntimeError):
|
||||
def __init__(self, status: str, reason: str):
|
||||
super().__init__(reason)
|
||||
self.status = status
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def sha256_bytes(data: bytes) -> str:
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
|
||||
|
||||
def canonical_sha256(value: Any) -> str:
|
||||
raw = json.dumps(
|
||||
value, ensure_ascii=True, separators=(",", ":"), sort_keys=True
|
||||
).encode("ascii")
|
||||
return sha256_bytes(raw)
|
||||
|
||||
|
||||
def write_json(path: Path, value: Any) -> None:
|
||||
path.write_text(
|
||||
json.dumps(value, ensure_ascii=True, indent=2, sort_keys=True) + "\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
|
||||
|
||||
def run_bytes(argv: list[str], allowed: set[int] | None = None) -> bytes:
|
||||
try:
|
||||
completed = subprocess.run(
|
||||
argv,
|
||||
cwd=ROOT,
|
||||
check=False,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
timeout=20,
|
||||
)
|
||||
except subprocess.TimeoutExpired as error:
|
||||
raise GateFailure(
|
||||
"INFRA_BLOCKED", f"command timed out: {' '.join(argv)}"
|
||||
) from error
|
||||
accepted = {0} if allowed is None else allowed
|
||||
if completed.returncode not in accepted:
|
||||
detail = completed.stderr.decode("utf-8", errors="replace").strip()
|
||||
raise GateFailure(
|
||||
"RUNNER_FAIL",
|
||||
f"command failed ({completed.returncode}): {' '.join(argv)}: {detail}",
|
||||
)
|
||||
return completed.stdout
|
||||
|
||||
|
||||
def git_text(*args: str) -> str:
|
||||
return run_bytes(["git", "-C", str(ROOT), *args]).decode(
|
||||
"utf-8", errors="strict"
|
||||
).strip()
|
||||
|
||||
|
||||
def source_at(commit: str, relative: str) -> bytes:
|
||||
return run_bytes(["git", "-C", str(ROOT), "show", f"{commit}:{relative}"])
|
||||
|
||||
|
||||
def parse_ls_tree(raw: bytes) -> dict[str, dict[str, Any]]:
|
||||
entries: dict[str, dict[str, Any]] = {}
|
||||
for record in raw.split(b"\0"):
|
||||
if not record:
|
||||
continue
|
||||
metadata, path_bytes = record.split(b"\t", 1)
|
||||
mode, kind, oid, size = metadata.decode("ascii").split()
|
||||
path = path_bytes.decode("utf-8", errors="strict")
|
||||
entries[path] = {
|
||||
"mode": mode,
|
||||
"kind": kind,
|
||||
"oid": oid,
|
||||
"size": int(size),
|
||||
}
|
||||
return entries
|
||||
|
||||
|
||||
def grep_paths(commit: str, expressions: list[str], fixed: bool = False) -> list[str]:
|
||||
argv = ["git", "-C", str(ROOT), "grep", "-I", "-l"]
|
||||
if fixed:
|
||||
argv.append("-F")
|
||||
else:
|
||||
argv.extend(["-i", "-E"])
|
||||
for expression in expressions:
|
||||
argv.extend(["-e", expression])
|
||||
argv.extend(
|
||||
[
|
||||
commit,
|
||||
"--",
|
||||
"repo-pre-15",
|
||||
"planning/pre15",
|
||||
":(exclude)planning/pre15/introduction.md",
|
||||
":(exclude)repo-pre-15/tests/pre15/gates/P15-031.sh",
|
||||
":(exclude)repo-pre-15/tests/pre15/gates/P15-031-input.json",
|
||||
":(exclude)repo-pre-15/docs/pre15-stage0/P15-031.md",
|
||||
]
|
||||
)
|
||||
raw = run_bytes(argv, {0, 1})
|
||||
prefix = f"{commit}:"
|
||||
paths = []
|
||||
for line in raw.decode("utf-8", errors="strict").splitlines():
|
||||
if not line.startswith(prefix):
|
||||
raise GateFailure("RUNNER_FAIL", f"unexpected git grep path: {line}")
|
||||
paths.append(line[len(prefix):])
|
||||
return sorted(paths)
|
||||
|
||||
|
||||
def classify_reference(path: str) -> str:
|
||||
if path.startswith("planning/pre15/evidence/"):
|
||||
return "historical-evidence"
|
||||
if path.startswith("planning/pre15/"):
|
||||
return "planning-proposal"
|
||||
if path.startswith("repo-pre-15/tests/"):
|
||||
return "test-only"
|
||||
if path.startswith("repo-pre-15/src-linux/"):
|
||||
return "linux-reference"
|
||||
if path.startswith("repo-pre-15/docs/") or path.startswith("repo-pre-15/current/"):
|
||||
return "project-report-or-documentation"
|
||||
return "possible-project-consumer"
|
||||
|
||||
|
||||
def require_text(record: dict[str, Any], field: str) -> str:
|
||||
value = record.get(field)
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer declaration lacks {field}")
|
||||
return value.strip()
|
||||
|
||||
|
||||
def validate_bound_path(
|
||||
record: dict[str, Any], path_field: str, hash_field: str,
|
||||
entries: dict[str, dict[str, Any]], commit: str, maximum_bytes: int,
|
||||
) -> tuple[str, bytes, str]:
|
||||
path = require_text(record, path_field)
|
||||
if not path.startswith("repo-pre-15/"):
|
||||
raise GateFailure("RUNNER_FAIL", f"{path_field} is outside DUT: {path}")
|
||||
if path not in entries:
|
||||
raise GateFailure("RUNNER_FAIL", f"{path_field} is not tracked: {path}")
|
||||
if entries[path]["size"] > maximum_bytes:
|
||||
raise GateFailure("INFRA_BLOCKED", f"bound file exceeds size limit: {path}")
|
||||
data = source_at(commit, path)
|
||||
digest = sha256_bytes(data)
|
||||
if digest != require_text(record, hash_field):
|
||||
raise GateFailure("RUNNER_FAIL", f"{hash_field} mismatch: {path}")
|
||||
return path, data, digest
|
||||
|
||||
|
||||
def validate_consumer_manifest(
|
||||
path: str, data: bytes, entries: dict[str, dict[str, Any]], commit: str,
|
||||
event_schema_sha256: str, maximum_bytes: int,
|
||||
) -> dict[str, Any]:
|
||||
try:
|
||||
record = json.loads(data.decode("utf-8", errors="strict"))
|
||||
except (UnicodeDecodeError, json.JSONDecodeError) as error:
|
||||
raise GateFailure("RUNNER_FAIL", f"invalid consumer manifest: {path}") from error
|
||||
if not isinstance(record, dict):
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer manifest is not an object: {path}")
|
||||
contract = SPEC["consumer_contract"]
|
||||
if not path.startswith(contract["manifest_prefix"]):
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer manifest is outside diagnostic prefix: {path}")
|
||||
if record.get("schema") != contract["schema_marker"]:
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer manifest schema mismatch: {path}")
|
||||
missing_fields = sorted(set(contract["required_fields"]) - set(record))
|
||||
if missing_fields:
|
||||
raise GateFailure(
|
||||
"RUNNER_FAIL", f"consumer manifest lacks fields {missing_fields}: {path}"
|
||||
)
|
||||
if require_text(record, "consumer_class") != "diagnostic":
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer is not diagnostic: {path}")
|
||||
if record.get("production_use") is not True or record.get("actual_capture") is not True:
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer lacks production capture evidence: {path}")
|
||||
if not require_text(record, "freebsd_version").startswith("FreeBSD 15"):
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer capture is not FreeBSD 15: {path}")
|
||||
transport = require_text(record, "transport")
|
||||
if transport not in contract["allowed_transports"]:
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer transport is not native: {path}")
|
||||
if require_text(record, "access_mode") != contract["required_access_mode"]:
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer access is not privileged: {path}")
|
||||
for field in (
|
||||
"consumer_id", "owner", "version", "entrypoint", "workflow",
|
||||
"deployment_reference",
|
||||
):
|
||||
require_text(record, field)
|
||||
if record.get("event_schema_sha256") != event_schema_sha256:
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer event schema mismatch: {path}")
|
||||
|
||||
expected_ids = [event["id"] for event in SPEC["event_schema"]["events"]]
|
||||
if record.get("event_ids") != expected_ids:
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer event order/set is incomplete: {path}")
|
||||
counts = record.get("captured_event_counts")
|
||||
if not isinstance(counts, dict) or set(counts) != set(expected_ids):
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer capture counts are incomplete: {path}")
|
||||
if any(type(counts[event_id]) is not int or counts[event_id] < 1 for event_id in expected_ids):
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer did not capture every event: {path}")
|
||||
|
||||
implementation_path, implementation, implementation_hash = validate_bound_path(
|
||||
record, "implementation_path", "implementation_sha256",
|
||||
entries, commit, maximum_bytes,
|
||||
)
|
||||
if any(
|
||||
implementation_path.startswith(prefix)
|
||||
for prefix in contract["prohibited_implementation_prefixes"]
|
||||
):
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer implementation is nonqualifying: {path}")
|
||||
capture_path, capture, capture_hash = validate_bound_path(
|
||||
record, "capture_evidence_path", "capture_evidence_sha256",
|
||||
entries, commit, maximum_bytes,
|
||||
)
|
||||
implementation_text = implementation.decode("utf-8", errors="replace").lower()
|
||||
capture_text = capture.decode("utf-8", errors="replace").lower()
|
||||
missing_implementation_events = [
|
||||
event_id for event_id in expected_ids
|
||||
if event_id not in implementation_text and event_id.replace("_", "-") not in implementation_text
|
||||
]
|
||||
missing_capture_events = [
|
||||
event_id for event_id in expected_ids
|
||||
if event_id not in capture_text and event_id.replace("_", "-") not in capture_text
|
||||
]
|
||||
if missing_implementation_events:
|
||||
raise GateFailure(
|
||||
"RUNNER_FAIL", f"consumer implementation omits {missing_implementation_events}: {path}"
|
||||
)
|
||||
if missing_capture_events:
|
||||
raise GateFailure(
|
||||
"RUNNER_FAIL", f"consumer capture omits {missing_capture_events}: {path}"
|
||||
)
|
||||
if transport == "dtrace-sdt":
|
||||
if "dtrace" not in implementation_text or "erofs" not in implementation_text:
|
||||
raise GateFailure("RUNNER_FAIL", f"consumer does not invoke EROFS DTrace: {path}")
|
||||
else:
|
||||
if "ktrdump" not in implementation_text:
|
||||
raise GateFailure("RUNNER_FAIL", f"KTR consumer does not invoke ktrdump: {path}")
|
||||
require_text(record, "ktr_justification")
|
||||
|
||||
return {
|
||||
"manifest_path": path,
|
||||
"manifest_sha256": sha256_bytes(data),
|
||||
"consumer_id": record["consumer_id"],
|
||||
"owner": record["owner"],
|
||||
"version": record["version"],
|
||||
"transport": transport,
|
||||
"implementation_path": implementation_path,
|
||||
"implementation_sha256": implementation_hash,
|
||||
"capture_evidence_path": capture_path,
|
||||
"capture_evidence_sha256": capture_hash,
|
||||
"captured_event_counts": counts,
|
||||
"production_use": True,
|
||||
"actual_capture": True,
|
||||
}
|
||||
|
||||
|
||||
def all_conditions_pass(statuses: list[str]) -> bool:
|
||||
return len(statuses) == 5 and all(status == "PASS" for status in statuses)
|
||||
|
||||
|
||||
def false_go_controls() -> dict[str, Any]:
|
||||
vectors = []
|
||||
all_pass = ["PASS"] * 5
|
||||
if not all_conditions_pass(all_pass):
|
||||
raise GateFailure("RUNNER_FAIL", "all-PASS control did not authorize GO")
|
||||
vectors.append({"name": "all-pass", "go": True})
|
||||
for index in range(5):
|
||||
statuses = all_pass.copy()
|
||||
statuses[index] = "NOT_RUN"
|
||||
if all_conditions_pass(statuses):
|
||||
raise GateFailure("RUNNER_FAIL", f"false-GO control {index} was accepted")
|
||||
vectors.append({"name": f"condition-{index + 1}-not-run", "go": False})
|
||||
statuses = all_pass.copy()
|
||||
statuses[0] = "STOP"
|
||||
if all_conditions_pass(statuses):
|
||||
raise GateFailure("RUNNER_FAIL", "consumer STOP control was accepted")
|
||||
vectors.append({"name": "consumer-stop", "go": False})
|
||||
return {"status": "PASS", "control_count": len(vectors), "vectors": vectors}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if SPEC.get("schema") != 1 or SPEC.get("candidate") != "P15-031":
|
||||
raise GateFailure("RUNNER_FAIL", "invalid P15-031 input identity")
|
||||
if SPEC.get("gate") != "G08" or SPEC.get("batch") != "B38":
|
||||
raise GateFailure("RUNNER_FAIL", "invalid G08/B38 input identity")
|
||||
if len(SPEC.get("go_requirements", [])) != 5:
|
||||
raise GateFailure("RUNNER_FAIL", "G08 P15-031 must have exactly five GO requirements")
|
||||
|
||||
resolved = git_text("rev-parse", f"{REQUESTED_BASE}^{{commit}}")
|
||||
if resolved != SPEC["required_base"]:
|
||||
raise GateFailure(
|
||||
"INFRA_BLOCKED", f"P15-031 must replay {SPEC['required_base']}, got {resolved}"
|
||||
)
|
||||
for path, expected_oid in SPEC["scope_tree_oids"].items():
|
||||
actual_oid = git_text("rev-parse", f"{resolved}:{path}")
|
||||
if actual_oid != expected_oid:
|
||||
raise GateFailure("INFRA_BLOCKED", f"frozen scope tree changed: {path}")
|
||||
|
||||
source_hashes = {
|
||||
path: sha256_bytes(source_at(resolved, path))
|
||||
for path in SPEC["source_sha256"]
|
||||
}
|
||||
if source_hashes != SPEC["source_sha256"]:
|
||||
raise GateFailure("INFRA_BLOCKED", "frozen G08/B38 source identity changed")
|
||||
write_json(OUTPUT / "source-sha256.json", source_hashes)
|
||||
addendum_paths = (
|
||||
"repo-pre-15/tests/pre15/gates/P15-031.sh",
|
||||
"repo-pre-15/tests/pre15/gates/P15-031-input.json",
|
||||
"repo-pre-15/docs/pre15-stage0/P15-031.md",
|
||||
)
|
||||
addendum_hashes = {
|
||||
path: sha256_bytes((ROOT / path).read_bytes()) for path in addendum_paths
|
||||
}
|
||||
write_json(OUTPUT / "gate-addendum-sha256.json", addendum_hashes)
|
||||
|
||||
entries = parse_ls_tree(
|
||||
run_bytes(
|
||||
[
|
||||
"git", "-C", str(ROOT), "ls-tree", "-r", "--long", "-z",
|
||||
resolved, "--", "repo-pre-15", "planning/pre15",
|
||||
]
|
||||
)
|
||||
)
|
||||
prohibited = set(SPEC["prohibited_paths"])
|
||||
scoped_entries = {path: value for path, value in entries.items() if path not in prohibited}
|
||||
if len(scoped_entries) != SPEC["consumer_inventory"]["expected_scoped_path_count"]:
|
||||
raise GateFailure("INFRA_BLOCKED", "frozen consumer scope path count changed")
|
||||
|
||||
reference_paths = grep_paths(
|
||||
resolved, SPEC["consumer_inventory"]["reference_patterns"]
|
||||
)
|
||||
maximum_bytes = SPEC["consumer_inventory"]["maximum_candidate_bytes"]
|
||||
references = []
|
||||
reference_counts = {
|
||||
"planning-proposal": 0,
|
||||
"historical-evidence": 0,
|
||||
"test-only": 0,
|
||||
"linux-reference": 0,
|
||||
"project-report-or-documentation": 0,
|
||||
"possible-project-consumer": 0,
|
||||
}
|
||||
reasons = {
|
||||
"planning-proposal": "proposal or audit text is not an existing consumer",
|
||||
"historical-evidence": "historical gate output is not a deployed consumer",
|
||||
"test-only": "a test, gate, or test record is explicitly nonqualifying",
|
||||
"linux-reference": "Linux tracepoints are not a FreeBSD diagnostic consumer",
|
||||
"project-report-or-documentation": "documentation is not an implemented consumer",
|
||||
"possible-project-consumer": "requires a validated production capture manifest",
|
||||
}
|
||||
for path in reference_paths:
|
||||
if path in prohibited:
|
||||
raise GateFailure("RUNNER_FAIL", "prohibited path entered consumer scan")
|
||||
entry = scoped_entries[path]
|
||||
if entry["size"] > maximum_bytes:
|
||||
raise GateFailure("INFRA_BLOCKED", f"consumer candidate exceeds bound: {path}")
|
||||
data = source_at(resolved, path)
|
||||
classification = classify_reference(path)
|
||||
reference_counts[classification] += 1
|
||||
references.append(
|
||||
{
|
||||
"path": path,
|
||||
"blob_oid": entry["oid"],
|
||||
"bytes": entry["size"],
|
||||
"sha256": sha256_bytes(data),
|
||||
"classification": classification,
|
||||
"qualifies": False,
|
||||
"reason": reasons[classification],
|
||||
}
|
||||
)
|
||||
expected_counts = SPEC["consumer_inventory"]["expected_reference_counts"]
|
||||
if len(reference_paths) != expected_counts["total"]:
|
||||
raise GateFailure("INFRA_BLOCKED", "frozen native-tracing reference count changed")
|
||||
for classification, count in reference_counts.items():
|
||||
if count != expected_counts[classification]:
|
||||
raise GateFailure(
|
||||
"INFRA_BLOCKED", f"frozen reference classification changed: {classification}"
|
||||
)
|
||||
|
||||
event_schema_sha256 = canonical_sha256(SPEC["event_schema"])
|
||||
marker = SPEC["consumer_contract"]["schema_marker"]
|
||||
manifest_paths = grep_paths(resolved, [marker], fixed=True)
|
||||
consumers = []
|
||||
manifest_hashes = {}
|
||||
for path in manifest_paths:
|
||||
entry = scoped_entries[path]
|
||||
if entry["size"] > maximum_bytes:
|
||||
raise GateFailure("INFRA_BLOCKED", f"consumer manifest exceeds bound: {path}")
|
||||
data = source_at(resolved, path)
|
||||
manifest_hashes[path] = sha256_bytes(data)
|
||||
consumers.append(
|
||||
validate_consumer_manifest(
|
||||
path, data, scoped_entries, resolved, event_schema_sha256, maximum_bytes
|
||||
)
|
||||
)
|
||||
if manifest_hashes != SPEC["approved_consumer_manifest_sha256"]:
|
||||
raise GateFailure("INFRA_BLOCKED", "declared consumer inventory changed")
|
||||
|
||||
inventory = {
|
||||
"schema": 1,
|
||||
"base": resolved,
|
||||
"scope": SPEC["consumer_inventory"]["scope"],
|
||||
"scoped_path_count": len(scoped_entries),
|
||||
"prohibited_path_read": False,
|
||||
"reference_patterns": SPEC["consumer_inventory"]["reference_patterns"],
|
||||
"reference_path_count": len(reference_paths),
|
||||
"reference_classification_counts": reference_counts,
|
||||
"references": references,
|
||||
"consumer_manifest_marker": marker,
|
||||
"consumer_manifest_count": len(consumers),
|
||||
"qualified_consumer_count": len(consumers),
|
||||
"qualified_consumers": consumers,
|
||||
"event_schema_sha256": event_schema_sha256,
|
||||
"required_event_ids": [event["id"] for event in SPEC["event_schema"]["events"]],
|
||||
"nonqualifying_examples": SPEC["consumer_contract"]["nonqualifying_examples"],
|
||||
}
|
||||
write_json(OUTPUT / "consumer-inventory.json", inventory)
|
||||
write_json(
|
||||
OUTPUT / "event-schema.json",
|
||||
{
|
||||
"schema": 1,
|
||||
"event_schema_sha256": event_schema_sha256,
|
||||
"event_schema": SPEC["event_schema"],
|
||||
},
|
||||
)
|
||||
controls = false_go_controls()
|
||||
write_json(OUTPUT / "false-go-controls.json", controls)
|
||||
|
||||
consumer_present = bool(consumers)
|
||||
conditions = [
|
||||
{
|
||||
"id": "concrete-existing-native-diagnostic-consumer",
|
||||
"status": "PASS" if consumer_present else "STOP",
|
||||
"observation": (
|
||||
f"{len(consumers)} qualified production capture manifest(s)"
|
||||
if consumer_present
|
||||
else "zero qualified consumers; all 33 references are planning, history, tests, or documentation"
|
||||
),
|
||||
},
|
||||
{
|
||||
"id": "no-pointer-credential-or-unauthenticated-metadata-leakage",
|
||||
"status": "NOT_RUN",
|
||||
"reason": "existing-consumer prerequisite is absent",
|
||||
},
|
||||
{
|
||||
"id": "owned-temp-freebsd15-prototype-fields-counts-failure-unload",
|
||||
"status": "NOT_RUN",
|
||||
"reason": "existing-consumer prerequisite is absent; no prototype was generated",
|
||||
},
|
||||
{
|
||||
"id": "disabled-zstdio0-zstdio1-no-new-undefined-symbols",
|
||||
"status": "NOT_RUN",
|
||||
"reason": "existing-consumer prerequisite is absent; no builds were run",
|
||||
},
|
||||
{
|
||||
"id": "ten-run-hot-path-median-regression-at-most-three-percent",
|
||||
"status": "NOT_RUN",
|
||||
"reason": "existing-consumer prerequisite is absent; no benchmark was run",
|
||||
},
|
||||
]
|
||||
if consumer_present:
|
||||
raise GateFailure(
|
||||
"RUNNER_FAIL",
|
||||
"a consumer is now approved; add and run the native privacy/prototype/build/benchmark stages before GO",
|
||||
)
|
||||
if all_conditions_pass([condition["status"] for condition in conditions]):
|
||||
raise GateFailure("RUNNER_FAIL", "false-GO guard accepted incomplete conditions")
|
||||
|
||||
result = {
|
||||
"schema": 1,
|
||||
"gate": "G08",
|
||||
"candidate": "P15-031",
|
||||
"batch": "B38",
|
||||
"status": "STOP",
|
||||
"reason": "no concrete existing project diagnostic consumer captures the predeclared native trace schema",
|
||||
"requested_base": REQUESTED_BASE,
|
||||
"resolved_base": resolved,
|
||||
"start_utc": START_UTC,
|
||||
"end_utc": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
||||
"deadline_seconds": DEADLINE,
|
||||
"expected_exit_code": 22,
|
||||
"conditions": conditions,
|
||||
"false_go_guard": controls,
|
||||
"consumer_inventory": "consumer-inventory.json",
|
||||
"event_schema": "event-schema.json",
|
||||
"gate_metrics": {
|
||||
"scoped_paths": len(scoped_entries),
|
||||
"native_tracing_reference_paths": len(reference_paths),
|
||||
"qualified_consumers": len(consumers),
|
||||
"predeclared_events": len(SPEC["event_schema"]["events"]),
|
||||
"prototype_runs": 0,
|
||||
"builds": 0,
|
||||
"benchmark_runs": 0,
|
||||
"qemu_runs": 0,
|
||||
},
|
||||
"b38": "STOP-NO-SOURCE",
|
||||
"prototype": "NOT_RUN",
|
||||
"builds": "NOT_RUN",
|
||||
"microbenchmark": "NOT_RUN",
|
||||
"qemu": "NOT_RUN",
|
||||
"tc178": "NOT_RUN",
|
||||
"full_feature_suite": "NOT_RUN",
|
||||
"smoke_suite": "NOT_RUN",
|
||||
"production_source_changed": False,
|
||||
"protected_pid_addressed": False,
|
||||
"protected_port_addressed": False,
|
||||
"base_image_addressed_or_hashed": False,
|
||||
}
|
||||
write_json(OUTPUT / "result.json", result)
|
||||
print(json.dumps(result, ensure_ascii=True, sort_keys=True))
|
||||
return 22
|
||||
|
||||
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except GateFailure as error:
|
||||
failure = {
|
||||
"schema": 1,
|
||||
"gate": "G08",
|
||||
"candidate": "P15-031",
|
||||
"batch": "B38",
|
||||
"status": error.status,
|
||||
"reason": error.reason,
|
||||
"requested_base": REQUESTED_BASE,
|
||||
"start_utc": START_UTC,
|
||||
"end_utc": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
||||
"deadline_seconds": DEADLINE,
|
||||
"b38": "NOT_RUN",
|
||||
"prototype": "NOT_RUN",
|
||||
"builds": "NOT_RUN",
|
||||
"microbenchmark": "NOT_RUN",
|
||||
"qemu": "NOT_RUN",
|
||||
"production_source_changed": False,
|
||||
}
|
||||
write_json(OUTPUT / "result.json", failure)
|
||||
print(json.dumps(failure, ensure_ascii=True, sort_keys=True))
|
||||
raise SystemExit({"RUNNER_FAIL": 20, "INFRA_BLOCKED": 21}.get(error.status, 20))
|
||||
PY
|
||||
gate_rc=$?
|
||||
set -e
|
||||
|
||||
cleanup_status=PASS
|
||||
if ! rm -rf -- "$work"; then
|
||||
cleanup_status=FAIL
|
||||
gate_rc=20
|
||||
fi
|
||||
trap - EXIT HUP INT TERM
|
||||
printf 'owned temporary path removed: %s\ncleanup=%s\n' \
|
||||
"$work" "$cleanup_status" >"$output/cleanup.log"
|
||||
|
||||
python3 -B - "$output" "$gate_rc" "$cleanup_status" <<'PY'
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
output = Path(sys.argv[1])
|
||||
exit_code = int(sys.argv[2])
|
||||
cleanup = sys.argv[3]
|
||||
if cleanup != "PASS":
|
||||
status = "RUNNER_FAIL"
|
||||
origin = "runner"
|
||||
elif exit_code == 22:
|
||||
status = "STOP"
|
||||
origin = "gate"
|
||||
elif exit_code in (124, 137):
|
||||
status = "INFRA_BLOCKED"
|
||||
origin = "infrastructure"
|
||||
else:
|
||||
status = "RUNNER_FAIL" if exit_code == 20 else "INFRA_BLOCKED"
|
||||
origin = "runner" if exit_code == 20 else "infrastructure"
|
||||
(output / "attempt.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema": 1,
|
||||
"exit_code": exit_code,
|
||||
"status": status,
|
||||
"failure_origin": origin,
|
||||
"cleanup": cleanup,
|
||||
"target_marker": "reached" if status == "STOP" else "not_reached",
|
||||
},
|
||||
ensure_ascii=True,
|
||||
indent=2,
|
||||
sort_keys=True,
|
||||
) + "\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
lines = []
|
||||
for path in sorted(output.iterdir(), key=lambda item: item.name):
|
||||
if path.is_file() and path.name != "SHA256SUMS":
|
||||
digest = hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
lines.append(f"{digest} {path.name}")
|
||||
(output / "SHA256SUMS").write_text("\n".join(lines) + "\n", encoding="ascii")
|
||||
PY
|
||||
|
||||
printf 'P15-031 gate exit=%s cleanup=%s output=%s\n' \
|
||||
"$gate_rc" "$cleanup_status" "$output"
|
||||
exit "$gate_rc"
|
||||
Reference in New Issue
Block a user