96 lines
2.4 KiB
Python
Executable File
96 lines
2.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Created by: Claude
|
|
# Date: 2026-01-01
|
|
# Purpose: Validate Mesh traceability headers in repository files
|
|
# Refs: tooling_precommit_vscode_snippets.md
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# File extensions we validate
|
|
VALID_EXTS = {
|
|
".rs",
|
|
".py",
|
|
".ts",
|
|
".tsx",
|
|
".js",
|
|
".jsx",
|
|
".yml",
|
|
".yaml",
|
|
".toml",
|
|
".md",
|
|
".css",
|
|
".html",
|
|
".htm",
|
|
}
|
|
|
|
# For markdown we allow HTML comment headers
|
|
HEADER_PATTERNS = [
|
|
re.compile(r"^\s*//\s*Created by:\s*.+$", re.IGNORECASE),
|
|
re.compile(r"^\s*#\s*Created by:\s*.+$", re.IGNORECASE),
|
|
re.compile(r"^\s*/\*\s*$", re.IGNORECASE),
|
|
re.compile(r"^\s*<!--\s*$", re.IGNORECASE),
|
|
]
|
|
|
|
REQUIRED_FIELDS = [
|
|
re.compile(r"Created by:\s*.+", re.IGNORECASE),
|
|
re.compile(r"Date:\s*\d{4}-\d{2}-\d{2}", re.IGNORECASE),
|
|
re.compile(r"Purpose:\s*.+", re.IGNORECASE),
|
|
]
|
|
|
|
|
|
def is_text_file(path: Path) -> bool:
|
|
"""Check if file is text by attempting UTF-8 decode."""
|
|
try:
|
|
path.read_text(encoding="utf-8")
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def has_required_header(text: str) -> bool:
|
|
"""Check if text contains required traceability header fields."""
|
|
# Check within first 40 lines
|
|
head = "\n".join(text.splitlines()[:40])
|
|
if not any(p.search(head) for p in HEADER_PATTERNS):
|
|
return False
|
|
return all(p.search(head) for p in REQUIRED_FIELDS)
|
|
|
|
|
|
def main(argv: list[str]) -> int:
|
|
"""Main entry point."""
|
|
files = [Path(a) for a in argv[1:] if a and not a.startswith("-")]
|
|
if not files:
|
|
return 0
|
|
|
|
bad: list[str] = []
|
|
|
|
for f in files:
|
|
if not f.exists() or f.is_dir():
|
|
continue
|
|
if f.suffix.lower() not in VALID_EXTS:
|
|
continue
|
|
if not is_text_file(f):
|
|
continue
|
|
|
|
txt = f.read_text(encoding="utf-8", errors="replace")
|
|
if not has_required_header(txt):
|
|
bad.append(str(f))
|
|
|
|
if bad:
|
|
sys.stderr.write("\nMesh header check failed. Missing/invalid traceability header in:\n")
|
|
for b in bad:
|
|
sys.stderr.write(f"- {b}\n")
|
|
sys.stderr.write("\nExpected header fields near top of file: Created by, Date (YYYY-MM-DD), Purpose.\n")
|
|
sys.stderr.write("See: tooling_precommit_vscode_snippets.md\n\n")
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main(sys.argv))
|