#!/usr/bin/env python3 """Check that the hardcoded CODEOWNERS list in pr-triage.yml matches the global owners line in .github/CODEOWNERS.""" import re import sys from pathlib import Path def main(): root = Path(__file__).parent.parent codeowners_file = root / ".github" / "CODEOWNERS" workflow_file = root / ".github" / "workflows" / "pr-triage.yml" if not codeowners_file.exists() or not workflow_file.exists(): return 0 try: codeowners_text = codeowners_file.read_text(encoding="utf-8") workflow_text = workflow_file.read_text(encoding="utf-8") except OSError as e: print(f"ERROR: Could not read file: {e}") return 1 global_owners = None for line in codeowners_text.splitlines(): stripped = line.strip() if not stripped or stripped.startswith("#"): continue if stripped.startswith("* "): global_owners = re.findall(r"@([A-Za-z0-9][A-Za-z0-9-]*)", stripped) break if not global_owners: print( "ERROR: Could not find a '* @owner...' global owners line " "in .github/CODEOWNERS" ) return 1 match = re.search( r"const\s+CODEOWNERS\s*=\s*\[([^\]]*)\]", workflow_text, ) if match is None: print( "ERROR: Could not find 'const CODEOWNERS = [...]' " "in .github/workflows/pr-triage.yml" ) return 1 js_owners = re.findall( r"['\"]([A-Za-z0-9][A-Za-z0-9-]*)['\"]", match.group(1) ) co_set = {u.lower() for u in global_owners} js_set = {u.lower() for u in js_owners} if co_set != js_set: print("ERROR: CODEOWNERS list mismatch between files.") print(f" .github/CODEOWNERS global owners: {sorted(global_owners)}") print(f" pr-triage.yml CODEOWNERS const: {sorted(js_owners)}") only_in_co = co_set - js_set only_in_js = js_set - co_set if only_in_co: print(f" Only in CODEOWNERS: {sorted(only_in_co)}") if only_in_js: print(f" Only in pr-triage.yml: {sorted(only_in_js)}") print( "Update both lists so they share the same maintainers " "(comments in each file note this requirement)." ) return 1 return 0 if __name__ == "__main__": sys.exit(main())