update
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
#!/bin/sh
|
||||
|
||||
pre15_write_argv_json()
|
||||
{
|
||||
pre15_argv_path=$1
|
||||
shift
|
||||
python3 - "$pre15_argv_path" "$@" <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
path = Path(sys.argv[1])
|
||||
with path.open("x", encoding="ascii") as output:
|
||||
json.dump(sys.argv[2:], output, ensure_ascii=True, separators=(",", ":"))
|
||||
output.write("\n")
|
||||
PY
|
||||
}
|
||||
|
||||
pre15_write_manifest()
|
||||
{
|
||||
python3 - "$PRE15_MANIFEST_PATH" <<'PY'
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
def digest(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def relative(path: Path, root: Path) -> str:
|
||||
return path.relative_to(root).as_posix()
|
||||
|
||||
|
||||
manifest_path = Path(sys.argv[1])
|
||||
run_dir = manifest_path.parent
|
||||
argv_path = Path(os.environ["PRE15_ARGV_PATH"])
|
||||
fixture_path = Path(os.environ["PRE15_FIXTURE_HASHES"])
|
||||
module_path = Path(os.environ["PRE15_MODULE_HASH"])
|
||||
stdout_path = Path(os.environ["PRE15_STDOUT_PATH"])
|
||||
stderr_path = Path(os.environ["PRE15_STDERR_PATH"])
|
||||
ownership_path = Path(os.environ["PRE15_OWNERSHIP_FILE"])
|
||||
cleanup_log = Path(os.environ["PRE15_CLEANUP_LOG"])
|
||||
identity_path = Path(os.environ["PRE15_IDENTITY_PATH"])
|
||||
|
||||
fixtures = []
|
||||
fixture_details = []
|
||||
if fixture_path.exists():
|
||||
for raw_line in fixture_path.read_text(encoding="utf-8").splitlines():
|
||||
if not raw_line:
|
||||
continue
|
||||
checksum, label, path = raw_line.split("\t", 2)
|
||||
fixtures.append(checksum)
|
||||
fixture_details.append(
|
||||
{"sha256": checksum, "label": label, "path": path}
|
||||
)
|
||||
|
||||
module_sha256 = None
|
||||
module_details = None
|
||||
if module_path.exists() and module_path.stat().st_size:
|
||||
checksum, path = module_path.read_text(encoding="utf-8").rstrip("\n").split(
|
||||
"\t", 1
|
||||
)
|
||||
module_sha256 = checksum
|
||||
module_details = {"sha256": checksum, "path": path}
|
||||
|
||||
exit_code_text = os.environ["PRE15_EXIT_CODE"]
|
||||
exit_code = None if exit_code_text == "null" else int(exit_code_text)
|
||||
identity = json.loads(identity_path.read_text(encoding="ascii"))
|
||||
manifest = {
|
||||
"schema_version": 1,
|
||||
"run_id": os.environ["PRE15_RUN_ID"],
|
||||
"mode": os.environ["PRE15_MODE"],
|
||||
"case_id": os.environ["PRE15_CASE_ID"],
|
||||
"dut_head": os.environ["PRE15_DUT_HEAD"],
|
||||
"dut_tree": os.environ["PRE15_DUT_TREE"],
|
||||
"worktree_diff_sha256": os.environ["PRE15_WORKTREE_DIFF_SHA256"],
|
||||
"freebsd_source": os.environ["PRE15_FREEBSD_SOURCE"],
|
||||
"linux_source": os.environ["PRE15_LINUX_SOURCE"],
|
||||
"module_sha256": module_sha256,
|
||||
"fixture_sha256": fixtures,
|
||||
"command_argv": json.loads(argv_path.read_text(encoding="ascii")),
|
||||
"start_utc": os.environ["PRE15_START_UTC"],
|
||||
"end_utc": os.environ["PRE15_END_UTC"],
|
||||
"deadline_seconds": int(os.environ["PRE15_DEADLINE"]),
|
||||
"exit_code": exit_code,
|
||||
"timed_out": os.environ["PRE15_TIMED_OUT"] == "1",
|
||||
"target_marker": os.environ["PRE15_TARGET_STATE"],
|
||||
"status": os.environ["PRE15_STATUS"],
|
||||
"reason": os.environ["PRE15_REASON"],
|
||||
"failure_origin": os.environ["PRE15_FAILURE_ORIGIN"],
|
||||
"cleanup": os.environ["PRE15_CLEANUP_STATUS"],
|
||||
"stdout": relative(stdout_path, run_dir),
|
||||
"stderr": relative(stderr_path, run_dir),
|
||||
"raw_sha256": {
|
||||
"stdout": digest(stdout_path),
|
||||
"stderr": digest(stderr_path),
|
||||
"ownership": digest(ownership_path),
|
||||
"cleanup": digest(cleanup_log),
|
||||
},
|
||||
"ownership_manifest": relative(ownership_path, run_dir),
|
||||
"cleanup_log": relative(cleanup_log, run_dir),
|
||||
"identity": identity,
|
||||
"fixtures": fixture_details,
|
||||
"module": module_details,
|
||||
}
|
||||
with manifest_path.open("x", encoding="ascii") as output:
|
||||
json.dump(manifest, output, ensure_ascii=True, indent=2, sort_keys=True)
|
||||
output.write("\n")
|
||||
PY
|
||||
}
|
||||
|
||||
pre15_validate_manifest()
|
||||
{
|
||||
pre15_manifest_to_validate=$1
|
||||
pre15_schema_to_validate=$2
|
||||
python3 - "$pre15_manifest_to_validate" "$pre15_schema_to_validate" <<'PY'
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
manifest_path = Path(sys.argv[1])
|
||||
schema_path = Path(sys.argv[2])
|
||||
manifest = json.loads(manifest_path.read_text(encoding="ascii"))
|
||||
schema = json.loads(schema_path.read_text(encoding="ascii"))
|
||||
|
||||
missing = sorted(set(schema["required"]) - set(manifest))
|
||||
if missing:
|
||||
raise SystemExit(f"manifest missing required keys: {', '.join(missing)}")
|
||||
if manifest["status"] not in schema["properties"]["status"]["enum"]:
|
||||
raise SystemExit(f"invalid status: {manifest['status']}")
|
||||
if manifest["cleanup"] not in schema["properties"]["cleanup"]["enum"]:
|
||||
raise SystemExit(f"invalid cleanup: {manifest['cleanup']}")
|
||||
if manifest["target_marker"] not in schema["properties"]["target_marker"]["enum"]:
|
||||
raise SystemExit(f"invalid target marker: {manifest['target_marker']}")
|
||||
if not re.fullmatch(r"[0-9a-f]{40}", manifest["dut_head"]):
|
||||
raise SystemExit("dut_head is not a 40-hex commit")
|
||||
for key in ("dut_tree", "worktree_diff_sha256"):
|
||||
if not re.fullmatch(r"[0-9a-f]{40,64}", manifest[key]):
|
||||
raise SystemExit(f"{key} is not a lowercase hex identity")
|
||||
if manifest["module_sha256"] is not None and not re.fullmatch(
|
||||
r"[0-9a-f]{64}", manifest["module_sha256"]
|
||||
):
|
||||
raise SystemExit("module_sha256 is neither null nor SHA256")
|
||||
if not isinstance(manifest["command_argv"], list) or not manifest["command_argv"]:
|
||||
raise SystemExit("command_argv must be a nonempty array")
|
||||
if not all(isinstance(item, str) for item in manifest["command_argv"]):
|
||||
raise SystemExit("command_argv contains a non-string item")
|
||||
if not isinstance(manifest["fixture_sha256"], list) or not all(
|
||||
re.fullmatch(r"[0-9a-f]{64}", item) for item in manifest["fixture_sha256"]
|
||||
):
|
||||
raise SystemExit("fixture_sha256 contains an invalid digest")
|
||||
if not isinstance(manifest["deadline_seconds"], int) or manifest["deadline_seconds"] <= 0:
|
||||
raise SystemExit("deadline_seconds must be positive")
|
||||
if manifest["exit_code"] is not None and not isinstance(manifest["exit_code"], int):
|
||||
raise SystemExit("exit_code must be an integer or null")
|
||||
for key in ("start_utc", "end_utc"):
|
||||
datetime.fromisoformat(manifest[key].replace("Z", "+00:00"))
|
||||
if manifest["status"] == "PASS":
|
||||
if manifest["exit_code"] != 0 or manifest["target_marker"] != "reached":
|
||||
raise SystemExit("PASS requires exit_code=0 and a reached target")
|
||||
if manifest["cleanup"] != "PASS":
|
||||
raise SystemExit("PASS requires successful cleanup")
|
||||
if manifest["status"] == "DUT_FAIL" and manifest["target_marker"] != "reached":
|
||||
raise SystemExit("DUT_FAIL requires a reached target")
|
||||
if manifest["cleanup"] == "FAIL" and manifest["status"] != "RUNNER_FAIL":
|
||||
raise SystemExit("cleanup failure must classify as RUNNER_FAIL")
|
||||
|
||||
run_dir = manifest_path.parent
|
||||
for key in ("stdout", "stderr", "ownership_manifest", "cleanup_log"):
|
||||
relative = Path(manifest[key])
|
||||
if relative.is_absolute() or ".." in relative.parts:
|
||||
raise SystemExit(f"{key} escapes the run directory")
|
||||
if not (run_dir / relative).is_file():
|
||||
raise SystemExit(f"{key} does not exist: {relative}")
|
||||
hashed_paths = {
|
||||
"stdout": manifest["stdout"],
|
||||
"stderr": manifest["stderr"],
|
||||
"ownership": manifest["ownership_manifest"],
|
||||
"cleanup": manifest["cleanup_log"],
|
||||
}
|
||||
for key, relative_path in hashed_paths.items():
|
||||
actual = hashlib.sha256((run_dir / relative_path).read_bytes()).hexdigest()
|
||||
if actual != manifest["raw_sha256"][key]:
|
||||
raise SystemExit(f"{key} hash mismatch")
|
||||
PY
|
||||
}
|
||||
Reference in New Issue
Block a user