#!/usr/bin/env python3 """Fail if a client-specific term appears inside a product (community_*) module. Deployment modules (matching *_deployment) are exempt, since client identity is expected to live there. Run from the repo root: python scripts/check_brand_leak.py """ import re import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent ADDONS_DIR = REPO_ROOT / "addons" BANNED_WORDS = [ "tncsc", "tamil nadu", "tamil nadu cultural society", ] BANNED_PATTERNS = [ re.compile(r"\b" + re.escape(word).replace(r"\ ", r"\s+") + r"\b", re.IGNORECASE) for word in BANNED_WORDS ] # Any email/domain that identifies the specific client deployment. BANNED_DOMAIN_PATTERN = re.compile(r"\btncsc\.(ca|org|com)\b", re.IGNORECASE) SCAN_EXTENSIONS = {".py", ".xml", ".csv", ".js", ".rst", ".md", ".html", ".txt"} # Never scan the plan itself, or this checker's own word list. EXCLUDE_FILENAMES = {"check_brand_leak.py"} def is_deployment_module(module_dir: Path) -> bool: return module_dir.name.endswith("_deployment") def is_product_module(module_dir: Path) -> bool: return module_dir.is_dir() and not module_dir.name.startswith(".") def iter_product_files(): if not ADDONS_DIR.exists(): return for module_dir in sorted(ADDONS_DIR.iterdir()): if not is_product_module(module_dir) or is_deployment_module(module_dir): continue for path in module_dir.rglob("*"): if not path.is_file(): continue if path.name in EXCLUDE_FILENAMES: continue if path.suffix.lower() not in SCAN_EXTENSIONS: continue yield path def scan_file(path: Path): """Return a list of (line_no, line_text, matched_term) violations.""" violations = [] try: text = path.read_text(encoding="utf-8", errors="ignore") except OSError: return violations for line_no, line in enumerate(text.splitlines(), start=1): for pattern in BANNED_PATTERNS: if pattern.search(line): violations.append((line_no, line.strip(), pattern.pattern)) if BANNED_DOMAIN_PATTERN.search(line): violations.append((line_no, line.strip(), BANNED_DOMAIN_PATTERN.pattern)) return violations def main() -> int: if not ADDONS_DIR.exists(): print(f"No addons/ directory found at {ADDONS_DIR}; nothing to check.") return 0 any_violation = False for path in iter_product_files(): for line_no, line_text, term in scan_file(path): any_violation = True rel_path = path.relative_to(REPO_ROOT) print(f"BRAND LEAK: {rel_path}:{line_no}: matched /{term}/ -> {line_text}") if any_violation: print( "\nOne or more community_* (product) modules reference client-specific " "identity. Move this content into the appropriate *_deployment module." ) return 1 print("Brand-leak check passed: no client-specific terms found in product modules.") return 0 if __name__ == "__main__": sys.exit(main())