Keep OnCo provenance headers on adapted UI files.
CONTENT-WP-0002-T08 requires each adapted file to name the pinned commit, the OnCo copyright, and ui/NOTICE. Assistant: grok Assistant-Session: 01a1346c-fdff-7741-b1af-b7283661554b
This commit is contained in:
parent
9837d43811
commit
940a6a90d0
8 changed files with 178 additions and 3 deletions
90
scripts/check_ui_provenance.py
Normal file
90
scripts/check_ui_provenance.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Require provenance headers on UI files adapted from the pinned OnCo commit."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
UI = ROOT / "ui"
|
||||
PIN_FILE = ROOT / "research" / "baseline" / "ONCO_PIN"
|
||||
SOURCE_SUFFIXES = {".ts", ".tsx", ".css", ".mjs"}
|
||||
ADAPTED = ("adapted from OnCo", "reduced from OnCo")
|
||||
HEADER_MARKS = ("Copyright (c) 2026 OnCo", "MIT License", "ui/NOTICE")
|
||||
NOTICE_MARKS = (
|
||||
"Copyright (c) 2026 OnCo",
|
||||
"MIT License",
|
||||
"Permission is hereby granted",
|
||||
"The above copyright notice and this permission notice shall be included",
|
||||
)
|
||||
|
||||
|
||||
def load_pin(path: Path = PIN_FILE) -> str:
|
||||
for line in path.read_text(encoding="utf-8").splitlines():
|
||||
if line.startswith("sha:"):
|
||||
return line.split(":", 1)[1].strip()
|
||||
raise ValueError(f"{path} has no sha")
|
||||
|
||||
|
||||
def is_adapted(text: str) -> bool:
|
||||
folded = text.casefold()
|
||||
return any(mark.casefold() in folded for mark in ADAPTED)
|
||||
|
||||
|
||||
def header_problems(text: str, label: str, pin: str) -> list[str]:
|
||||
if not is_adapted(text):
|
||||
return []
|
||||
missing = [mark for mark in (*HEADER_MARKS, pin) if mark not in text]
|
||||
if not missing:
|
||||
return []
|
||||
return [f"{label}: provenance header missing {', '.join(missing)}"]
|
||||
|
||||
|
||||
def notice_problems(text: str, pin: str) -> list[str]:
|
||||
missing = [mark for mark in (*NOTICE_MARKS, pin) if mark not in text]
|
||||
if not missing:
|
||||
return []
|
||||
return [f"ui/NOTICE missing {', '.join(missing)}"]
|
||||
|
||||
|
||||
def footer_problems(text: str, pin: str) -> list[str]:
|
||||
missing = [mark for mark in ("MIT License", pin) if mark not in text]
|
||||
if not missing:
|
||||
return []
|
||||
return [f"ui/components/SiteFooter.tsx missing {', '.join(missing)}"]
|
||||
|
||||
|
||||
def tree_problems(root: Path = UI, pin: str | None = None) -> list[str]:
|
||||
pin = load_pin() if pin is None else pin
|
||||
problems: list[str] = []
|
||||
for path in sorted(root.rglob("*")):
|
||||
if not path.is_file() or "node_modules" in path.parts or ".next" in path.parts:
|
||||
continue
|
||||
if path.suffix not in SOURCE_SUFFIXES:
|
||||
continue
|
||||
problems.extend(header_problems(path.read_text(encoding="utf-8"), str(path.relative_to(ROOT)), pin))
|
||||
notice = root / "NOTICE"
|
||||
if not notice.is_file():
|
||||
problems.append("ui/NOTICE is missing")
|
||||
else:
|
||||
problems.extend(notice_problems(notice.read_text(encoding="utf-8"), pin))
|
||||
footer = root / "components" / "SiteFooter.tsx"
|
||||
if not footer.is_file():
|
||||
problems.append("ui/components/SiteFooter.tsx is missing")
|
||||
else:
|
||||
problems.extend(footer_problems(footer.read_text(encoding="utf-8"), pin))
|
||||
return problems
|
||||
|
||||
|
||||
def main() -> int:
|
||||
problems = tree_problems()
|
||||
if problems:
|
||||
print("\n".join(problems))
|
||||
return 1
|
||||
print("ui provenance: adapted files name the OnCo pin, copyright, and NOTICE")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue