mirror of
https://github.com/esphome/esphome.git
synced 2024-12-21 16:27:44 +01:00
Add CODEOWNERS mechanism (#1199)
This commit is contained in:
parent
5887fe8302
commit
4996967c79
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -143,6 +143,8 @@ jobs:
|
||||
run: script/ci-custom.py
|
||||
- name: Lint Python
|
||||
run: script/lint-python
|
||||
- name: Lint CODEOWNERS
|
||||
run: script/build_codeowners.py --check
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
2
.github/workflows/release-dev.yml
vendored
2
.github/workflows/release-dev.yml
vendored
@ -98,6 +98,8 @@ jobs:
|
||||
run: script/ci-custom.py
|
||||
- name: Lint Python
|
||||
run: script/lint-python
|
||||
- name: Lint CODEOWNERS
|
||||
run: script/build_codeowners.py --check
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -97,6 +97,8 @@ jobs:
|
||||
run: script/ci-custom.py
|
||||
- name: Lint Python
|
||||
run: script/lint-python
|
||||
- name: Lint CODEOWNERS
|
||||
run: script/build_codeowners.py --check
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
54
CODEOWNERS
Normal file
54
CODEOWNERS
Normal file
@ -0,0 +1,54 @@
|
||||
# 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
|
||||
esphome/components/adc/* @esphome/core
|
||||
esphome/components/api/* @OttoWinter
|
||||
esphome/components/async_tcp/* @OttoWinter
|
||||
esphome/components/bang_bang/* @OttoWinter
|
||||
esphome/components/binary_sensor/* @esphome/core
|
||||
esphome/components/captive_portal/* @OttoWinter
|
||||
esphome/components/climate/* @esphome/core
|
||||
esphome/components/cover/* @esphome/core
|
||||
esphome/components/debug/* @OttoWinter
|
||||
esphome/components/dht/* @OttoWinter
|
||||
esphome/components/exposure_notifications/* @OttoWinter
|
||||
esphome/components/fastled_base/* @OttoWinter
|
||||
esphome/components/globals/* @esphome/core
|
||||
esphome/components/gpio/* @esphome/core
|
||||
esphome/components/homeassistant/* @OttoWinter
|
||||
esphome/components/i2c/* @esphome/core
|
||||
esphome/components/integration/* @OttoWinter
|
||||
esphome/components/interval/* @esphome/core
|
||||
esphome/components/json/* @OttoWinter
|
||||
esphome/components/ledc/* @OttoWinter
|
||||
esphome/components/light/* @esphome/core
|
||||
esphome/components/logger/* @esphome/core
|
||||
esphome/components/network/* @esphome/core
|
||||
esphome/components/ota/* @esphome/core
|
||||
esphome/components/output/* @esphome/core
|
||||
esphome/components/pid/* @OttoWinter
|
||||
esphome/components/pn532/* @OttoWinter
|
||||
esphome/components/power_supply/* @esphome/core
|
||||
esphome/components/restart/* @esphome/core
|
||||
esphome/components/script/* @esphome/core
|
||||
esphome/components/sensor/* @esphome/core
|
||||
esphome/components/shutdown/* @esphome/core
|
||||
esphome/components/spi/* @esphome/core
|
||||
esphome/components/substitutions/* @esphome/core
|
||||
esphome/components/sun/* @OttoWinter
|
||||
esphome/components/switch/* @esphome/core
|
||||
esphome/components/time/* @OttoWinter
|
||||
esphome/components/uart/* @esphome/core
|
||||
esphome/components/ultrasonic/* @OttoWinter
|
||||
esphome/components/version/* @esphome/core
|
||||
esphome/components/web_server_base/* @OttoWinter
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@esphome/core']
|
@ -8,6 +8,7 @@ from esphome.core import coroutine_with_priority
|
||||
|
||||
DEPENDENCIES = ['network']
|
||||
AUTO_LOAD = ['async_tcp']
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
|
||||
api_ns = cg.esphome_ns.namespace('api')
|
||||
APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller)
|
||||
|
@ -2,6 +2,8 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
|
||||
|
||||
@coroutine_with_priority(200.0)
|
||||
def to_code(config):
|
||||
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@OttoWinter']
|
@ -11,6 +11,7 @@ from esphome.const import CONF_DEVICE_CLASS, CONF_FILTERS, \
|
||||
from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
from esphome.util import Registry
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
DEVICE_CLASSES = [
|
||||
'', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas',
|
||||
'heat', 'light', 'lock', 'moisture', 'motion', 'moving', 'occupancy',
|
||||
|
@ -7,6 +7,7 @@ from esphome.core import coroutine_with_priority
|
||||
|
||||
AUTO_LOAD = ['web_server_base']
|
||||
DEPENDENCIES = ['wifi']
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
|
||||
captive_portal_ns = cg.esphome_ns.namespace('captive_portal')
|
||||
CaptivePortal = captive_portal_ns.class_('CaptivePortal', cg.Component)
|
||||
|
@ -10,6 +10,7 @@ from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
climate_ns = cg.esphome_ns.namespace('climate')
|
||||
|
||||
Climate = climate_ns.class_('Climate', cg.Nameable)
|
||||
|
@ -9,6 +9,7 @@ from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
DEVICE_CLASSES = [
|
||||
'', 'awning', 'blind', 'curtain', 'damper', 'door', 'garage',
|
||||
'gate', 'shade', 'shutter', 'window'
|
||||
|
@ -2,6 +2,7 @@ import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
DEPENDENCIES = ['logger']
|
||||
|
||||
debug_ns = cg.esphome_ns.namespace('debug')
|
||||
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@OttoWinter']
|
@ -4,6 +4,7 @@ import esphome.config_validation as cv
|
||||
from esphome.components import esp32_ble_tracker
|
||||
from esphome.const import CONF_TRIGGER_ID
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
exposure_notifications_ns = cg.esphome_ns.namespace('exposure_notifications')
|
||||
|
@ -4,6 +4,7 @@ from esphome.components import light
|
||||
from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_RGB_ORDER, CONF_MAX_REFRESH_RATE
|
||||
from esphome.core import coroutine
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
fastled_base_ns = cg.esphome_ns.namespace('fastled_base')
|
||||
FastLEDLightOutput = fastled_base_ns.class_('FastLEDLightOutput', light.AddressableLight)
|
||||
|
||||
|
@ -5,6 +5,7 @@ from esphome import codegen as cg
|
||||
from esphome.const import CONF_ID, CONF_INITIAL_VALUE, CONF_RESTORE_VALUE, CONF_TYPE, CONF_VALUE
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
globals_ns = cg.esphome_ns.namespace('globals')
|
||||
GlobalsComponent = globals_ns.class_('GlobalsComponent', cg.Component)
|
||||
GlobalVarSetAction = globals_ns.class_('GlobalVarSetAction', automation.Action)
|
||||
|
@ -1,3 +1,4 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
gpio_ns = cg.esphome_ns.namespace('gpio')
|
||||
|
@ -1,3 +1,4 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
homeassistant_ns = cg.esphome_ns.namespace('homeassistant')
|
||||
|
@ -5,6 +5,7 @@ from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_SCAN, CONF_SCL, CONF_SDA
|
||||
CONF_I2C_ID
|
||||
from esphome.core import coroutine, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
i2c_ns = cg.esphome_ns.namespace('i2c')
|
||||
I2CComponent = i2c_ns.class_('I2CComponent', cg.Component)
|
||||
I2CDevice = i2c_ns.class_('I2CDevice')
|
||||
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@OttoWinter']
|
@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.const import CONF_ID, CONF_INTERVAL
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
interval_ns = cg.esphome_ns.namespace('interval')
|
||||
IntervalTrigger = interval_ns.class_('IntervalTrigger', automation.Trigger.template(),
|
||||
cg.PollingComponent)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
json_ns = cg.esphome_ns.namespace('json')
|
||||
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@OttoWinter']
|
@ -14,6 +14,7 @@ from .types import ( # noqa
|
||||
LightState, AddressableLightState, light_ns, LightOutput, AddressableLight, \
|
||||
LightTurnOnTrigger, LightTurnOffTrigger)
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
LightRestoreMode = light_ns.enum('LightRestoreMode')
|
||||
|
@ -8,6 +8,7 @@ from esphome.const import CONF_ARGS, CONF_BAUD_RATE, CONF_FORMAT, CONF_HARDWARE_
|
||||
CONF_LEVEL, CONF_LOGS, CONF_ON_MESSAGE, CONF_TAG, CONF_TRIGGER_ID, CONF_TX_BUFFER_SIZE
|
||||
from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
logger_ns = cg.esphome_ns.namespace('logger')
|
||||
LOG_LEVELS = {
|
||||
'NONE': cg.global_ns.ESPHOME_LOG_LEVEL_NONE,
|
||||
|
@ -1 +1,2 @@
|
||||
# Dummy package to allow components to depend on network
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
|
@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_PASSWORD, CONF_PORT, CONF_SAFE_MODE
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
DEPENDENCIES = ['network']
|
||||
|
||||
ota_ns = cg.esphome_ns.namespace('ota')
|
||||
|
@ -7,6 +7,8 @@ from esphome.const import CONF_ID, CONF_INVERTED, CONF_LEVEL, CONF_MAX_POWER, \
|
||||
CONF_MIN_POWER, CONF_POWER_SUPPLY
|
||||
from esphome.core import CORE, coroutine
|
||||
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
BINARY_OUTPUT_SCHEMA = cv.Schema({
|
||||
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@OttoWinter']
|
@ -4,6 +4,7 @@ from esphome import automation
|
||||
from esphome.components import spi
|
||||
from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
DEPENDENCIES = ['spi']
|
||||
AUTO_LOAD = ['binary_sensor']
|
||||
MULTI_CONF = True
|
||||
|
@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.const import CONF_ENABLE_TIME, CONF_ID, CONF_KEEP_ON_TIME, CONF_PIN
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
power_supply_ns = cg.esphome_ns.namespace('power_supply')
|
||||
PowerSupply = power_supply_ns.class_('PowerSupply', cg.Component)
|
||||
MULTI_CONF = True
|
||||
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@esphome/core']
|
@ -4,6 +4,7 @@ from esphome import automation
|
||||
from esphome.automation import maybe_simple_id
|
||||
from esphome.const import CONF_ID, CONF_MODE
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
script_ns = cg.esphome_ns.namespace('script')
|
||||
Script = script_ns.class_('Script', automation.Trigger.template())
|
||||
ScriptExecuteAction = script_ns.class_('ScriptExecuteAction', automation.Action)
|
||||
|
@ -12,6 +12,7 @@ from esphome.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_B
|
||||
from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
from esphome.util import Registry
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@esphome/core']
|
@ -5,6 +5,7 @@ from esphome.const import CONF_CLK_PIN, CONF_ID, CONF_MISO_PIN, CONF_MOSI_PIN, C
|
||||
CONF_CS_PIN
|
||||
from esphome.core import coroutine, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
spi_ns = cg.esphome_ns.namespace('spi')
|
||||
SPIComponent = spi_ns.class_('SPIComponent', cg.Component)
|
||||
SPIDevice = spi_ns.class_('SPIDevice')
|
||||
|
@ -5,6 +5,7 @@ import esphome.config_validation as cv
|
||||
from esphome import core
|
||||
from esphome.const import CONF_SUBSTITUTIONS
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
VALID_SUBSTITUTIONS_CHARACTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
|
||||
|
@ -4,6 +4,7 @@ from esphome import automation
|
||||
from esphome.components import time
|
||||
from esphome.const import CONF_TIME_ID, CONF_ID, CONF_TRIGGER_ID
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
sun_ns = cg.esphome_ns.namespace('sun')
|
||||
|
||||
Sun = sun_ns.class_('Sun')
|
||||
|
@ -7,6 +7,7 @@ from esphome.const import CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_INVERTED, CONF
|
||||
CONF_ON_TURN_ON, CONF_TRIGGER_ID, CONF_MQTT_ID, CONF_NAME
|
||||
from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
switch_ns = cg.esphome_ns.namespace('switch_')
|
||||
|
@ -17,6 +17,7 @@ from esphome.core import coroutine, coroutine_with_priority
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
time_ns = cg.esphome_ns.namespace('time')
|
||||
|
@ -5,6 +5,7 @@ from esphome.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN, CON
|
||||
CONF_DATA, CONF_RX_BUFFER_SIZE
|
||||
from esphome.core import CORE, coroutine
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
uart_ns = cg.esphome_ns.namespace('uart')
|
||||
UARTComponent = uart_ns.class_('UARTComponent', cg.Component)
|
||||
UARTDevice = uart_ns.class_('UARTDevice')
|
||||
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@OttoWinter']
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@esphome/core']
|
@ -3,6 +3,7 @@ import esphome.codegen as cg
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.core import coroutine_with_priority, CORE
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
DEPENDENCIES = ['network']
|
||||
AUTO_LOAD = ['async_tcp']
|
||||
|
||||
|
@ -66,6 +66,10 @@ class ComponentManifest:
|
||||
def auto_load(self):
|
||||
return getattr(self.module, 'AUTO_LOAD', [])
|
||||
|
||||
@property
|
||||
def codeowners(self) -> List[str]:
|
||||
return getattr(self.module, 'CODEOWNERS', [])
|
||||
|
||||
def _get_flags_set(self, name, config):
|
||||
if not hasattr(self.module, name):
|
||||
return set()
|
||||
|
@ -2,6 +2,9 @@ import codecs
|
||||
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
import tempfile
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -168,15 +171,21 @@ def read_file(path):
|
||||
raise EsphomeError(f"Error reading file {path}: {err}")
|
||||
|
||||
|
||||
def _write_file(path, text):
|
||||
import tempfile
|
||||
directory = os.path.dirname(path)
|
||||
mkdir_p(directory)
|
||||
def _write_file(path: Union[Path, str], text: Union[str, bytes]):
|
||||
"""Atomically writes `text` to the given path.
|
||||
|
||||
tmp_path = None
|
||||
Automatically creates all parent directories.
|
||||
"""
|
||||
if not isinstance(path, Path):
|
||||
path = Path(path)
|
||||
data = text
|
||||
if isinstance(text, str):
|
||||
data = text.encode()
|
||||
|
||||
directory = path.parent
|
||||
directory.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
tmp_path = None
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(mode="wb", dir=directory, delete=False) as f_handle:
|
||||
tmp_path = f_handle.name
|
||||
@ -193,7 +202,7 @@ def _write_file(path, text):
|
||||
_LOGGER.error("Write file cleanup failed: %s", err)
|
||||
|
||||
|
||||
def write_file(path, text):
|
||||
def write_file(path: Union[Path, str], text: str):
|
||||
try:
|
||||
_write_file(path, text)
|
||||
except OSError:
|
||||
@ -201,9 +210,12 @@ def write_file(path, text):
|
||||
raise EsphomeError(f"Could not write file at {path}")
|
||||
|
||||
|
||||
def write_file_if_changed(path, text):
|
||||
def write_file_if_changed(path: Union[Path, str], text: str):
|
||||
if not isinstance(path, Path):
|
||||
path = Path(path)
|
||||
|
||||
src_content = None
|
||||
if os.path.isfile(path):
|
||||
if path.is_file():
|
||||
src_content = read_file(path)
|
||||
if src_content != text:
|
||||
write_file(path, text)
|
||||
|
68
script/build_codeowners.py
Executable file
68
script/build_codeowners.py
Executable file
@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
from esphome.helpers import write_file_if_changed
|
||||
from esphome.config import get_component
|
||||
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)
|
||||
|
||||
for path in sorted(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.codeowners:
|
||||
for owner in comp.codeowners:
|
||||
if not owner.startswith('@'):
|
||||
print(f"Codeowner {owner} for integration {name} must start with an '@' symbol!")
|
||||
sys.exit(1)
|
||||
|
||||
parts.append(f"esphome/components/{name}/* {' '.join(comp.codeowners)}")
|
||||
|
||||
# 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")
|
Loading…
Reference in New Issue
Block a user