Files
esphome/script/build_codeowners.py
T
Clyde StubbsandGitHub 5e3e2f82c9
CI / Create common environment (push) Has been cancelled
CI / Check pylint (push) Has been cancelled
CI / Run script/ci-custom (push) Has been cancelled
CI / Check import esphome.__main__ time (push) Has been cancelled
CI / Test downstream esphome/device-builder (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.12) (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.14) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.12) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.13) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.14) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.12) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.14) (push) Has been cancelled
CI / Determine which jobs to run (push) Has been cancelled
CI / Run integration tests (${{ matrix.bucket.name }}) (push) Has been cancelled
CI / Run C++ unit tests (push) Has been cancelled
CI / Run CodSpeed benchmarks (push) Has been cancelled
CI / Run script/clang-tidy for ESP8266 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino (push) Has been cancelled
CI / Run script/clang-tidy for ZEPHYR (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF 1/3 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF 2/3 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF 3/3 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 C6 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 P4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 S3 (push) Has been cancelled
CI / Test components batch (${{ matrix.batch.components }}) (push) Has been cancelled
CI / Test esp32 components with PlatformIO (push) Has been cancelled
CI / Seed pre-commit cache (push) Has been cancelled
CI / pre-commit.ci lite (push) Has been cancelled
CI / Build target branch for memory impact (push) Has been cancelled
CI / Build PR branch for memory impact (push) Has been cancelled
CI / Comment memory impact (push) Has been cancelled
CI / CI Status (push) Has been cancelled
CI - GitHub Scripts / Test auto-label-pr scripts (push) Has been cancelled
[script] Fix duplicate import in build_codeowners.py (#17543)
2026-07-13 10:16:53 -04:00

118 lines
3.7 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
from collections import defaultdict
from pathlib import Path
import sys
from esphome.config import get_component, get_platform
from esphome.const import KEY_CORE, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM
from esphome.core import CORE
from esphome.helpers import write_file_if_changed
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
pyproject.toml @esphome/core
esphome/*.py @esphome/core
esphome/core/* @esphome/core
.github/** @esphome/core
# Integrations
""".strip()
parts = [BASE]
# Fake some directory so that get_component works
CORE.config_path = root
CORE.data[KEY_CORE] = {KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: None}
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():
if platform_path.name == "__init__.py":
# `import pkg.__init__` is valid but distinct from `import pkg`: it re-executes
# the component's __init__.py as a second, separate module. That's harmless for
# components whose top-level code is idempotent, but not guaranteed in general
# (e.g. code that registers into a global registry with a duplicate check), so
# never treat __init__.py itself as a platform candidate.
continue
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), key=str.casefold)
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")