#!/usr/bin/env python3 from pathlib import Path import sys import argparse from collections import defaultdict from esphome.helpers import write_file_if_changed from esphome.config import get_component, get_platform from esphome.core import CORE parser = argparse.ArgumentParser() parser.add_argument('--check', help="Check if the CODEOWNERS file is up to date.", action='store_true') args = parser.parse_args() # The root directory of the repo root = Path(__file__).parent.parent components_dir = root / 'esphome' / 'components' BASE = """ # This file is generated by script/build_codeowners.py # People marked here will be automatically requested for a review # when the code that they own is touched. # # Every time an issue is created with a label corresponding to an integration, # the integration's code owner is automatically notified. # Core Code setup.py @esphome/core esphome/*.py @esphome/core esphome/core/* @esphome/core # Integrations """.strip() parts = [BASE] # Fake some diretory so that get_component works CORE.config_path = str(root) codeowners = defaultdict(list) for path in components_dir.iterdir(): if not path.is_dir(): continue if not (path / '__init__.py').is_file(): continue name = path.name comp = get_component(name) if comp is None: print(f'Cannot find component {name}. Make sure current path is pip installed ESPHome') sys.exit(1) codeowners[f'esphome/components/{name}/*'].extend(comp.codeowners) for platform_path in path.iterdir(): platform_name = platform_path.stem platform = get_platform(platform_name, name) if platform is None: continue if platform_path.is_dir(): # Sub foldered platforms get their own line if not (platform_path / '__init__.py').is_file(): continue codeowners[f'esphome/components/{name}/{platform_name}/*'].extend(platform.codeowners) continue # Non-subfoldered platforms add to codeowners at component level if not platform_path.is_file() or platform_path.name == '__init__.py': continue codeowners[f'esphome/components/{name}/*'].extend(platform.codeowners) for path, owners in sorted(codeowners.items()): owners = sorted(set(owners)) if not owners: continue for owner in owners: if not owner.startswith('@'): print(f"Codeowner {owner} for integration {path} must start with an '@' symbol!") sys.exit(1) parts.append(f"{path} {' '.join(owners)}") # End newline parts.append('') content = '\n'.join(parts) codeowners_file = root / 'CODEOWNERS' if args.check: if codeowners_file.read_text() != content: print("CODEOWNERS file is not up to date.") print("Please run `script/build_codeowners.py`") sys.exit(1) print("CODEOWNERS file is up to date") else: write_file_if_changed(codeowners_file, content) print("Wrote CODEOWNERS")