content-explorer/scripts/check_ui_boundary.py

62 lines
2 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""Reject oncology, cancer, drug, and target imports in the generic UI."""
from __future__ import annotations
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
UI = ROOT / "ui"
SOURCE_SUFFIXES = {".ts", ".tsx", ".css", ".mjs"}
IMPORT_STATEMENT = re.compile(
r"""(?:^|\n)\s*import\b[\s\S]*?\bfrom\s+['"][^'"]+['"]"""
r"""|(?:^|\n)\s*import\s+['"][^'"]+['"]"""
r"""|import\s*\(\s*['"][^'"]+['"]\s*\)"""
r"""|require\s*\(\s*['"][^'"]+['"]\s*\)"""
r"""|@import\s+['"][^'"]+['"]""",
)
# The token may start an identifier such as CancerIcon or targetThumb.
FORBIDDEN = re.compile(r"\b(?:oncology|cancers?|drugs?|targets?)", re.IGNORECASE)
def import_problems(text: str, label: str) -> list[str]:
problems: list[str] = []
for match in IMPORT_STATEMENT.finditer(text):
statement = " ".join(match.group(0).split())
if FORBIDDEN.search(statement):
problems.append(f"{label}: {statement}")
return problems
def tree_problems(root: Path = UI) -> list[str]:
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(import_problems(path.read_text(encoding="utf-8"), str(path.relative_to(ROOT))))
notice = root / "NOTICE"
if not notice.is_file():
problems.append("ui/NOTICE is missing")
else:
text = notice.read_text(encoding="utf-8")
if "Copyright (c) 2026 OnCo" not in text or "MIT License" not in text:
problems.append("ui/NOTICE is missing the OnCo MIT copyright and permission notice")
return problems
def main() -> int:
problems = tree_problems()
if problems:
print("\n".join(problems))
return 1
print("ui boundary: no oncology, cancer, drug, or target imports; NOTICE retains the OnCo MIT text")
return 0
if __name__ == "__main__":
sys.exit(main())