Initial LinuxCNC WebAssembly workspace snapshot
This commit is contained in:
68
scripts/validate_download_index.py
Executable file
68
scripts/validate_download_index.py
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate external artifact provenance before it enters a build context."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
SHA256 = re.compile(r"^[0-9a-f]{64}$")
|
||||
KINDS = {"official", "china-mirror", "local-cache"}
|
||||
|
||||
|
||||
def validate(path: Path) -> list[str]:
|
||||
errors: list[str] = []
|
||||
seen: set[tuple[str, str]] = set()
|
||||
with path.open("r", encoding="utf-8") as handle:
|
||||
for line_number, line in enumerate(handle, 1):
|
||||
if not line.strip():
|
||||
continue
|
||||
try:
|
||||
row = json.loads(line)
|
||||
except json.JSONDecodeError as exc:
|
||||
errors.append(f"{path}:{line_number}: invalid JSON: {exc.msg}")
|
||||
continue
|
||||
if row.get("record_type") != "artifact":
|
||||
continue
|
||||
required = {"name", "version", "source_url", "sha256", "bytes", "license", "mirror_kind"}
|
||||
missing = sorted(required - row.keys())
|
||||
if missing:
|
||||
errors.append(f"{path}:{line_number}: missing {missing}")
|
||||
continue
|
||||
key = (str(row["name"]), str(row["version"]))
|
||||
if key in seen:
|
||||
errors.append(f"{path}:{line_number}: duplicate artifact {key[0]}@{key[1]}")
|
||||
seen.add(key)
|
||||
if not SHA256.fullmatch(str(row["sha256"])):
|
||||
errors.append(f"{path}:{line_number}: sha256 must be 64 lowercase hex characters")
|
||||
if not isinstance(row["bytes"], int) or row["bytes"] <= 0:
|
||||
errors.append(f"{path}:{line_number}: bytes must be a positive integer")
|
||||
if row["mirror_kind"] not in KINDS:
|
||||
errors.append(f"{path}:{line_number}: invalid mirror_kind")
|
||||
parsed = urlparse(str(row["source_url"]))
|
||||
if row["mirror_kind"] != "local-cache" and parsed.scheme not in {"http", "https"}:
|
||||
errors.append(f"{path}:{line_number}: source_url must be HTTP(S) for downloaded artifacts")
|
||||
if row["mirror_kind"] == "china-mirror" and not row.get("origin_url"):
|
||||
errors.append(f"{path}:{line_number}: china-mirror requires origin_url")
|
||||
return errors
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--index", type=Path, default=Path("vendor/downloads/index.jsonl"))
|
||||
args = parser.parse_args()
|
||||
errors = validate(args.index)
|
||||
if errors:
|
||||
for error in errors:
|
||||
print(f"ERROR: {error}")
|
||||
return 1
|
||||
print(f"download_index=valid path={args.index}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user