blob: d6557adaca2d8af5846393c54140fe76e3debb20 [file] [log] [blame] [edit]
#!/usr/bin/env python3
# Copyright (C) 2025 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Generates .github/CODEOWNERS from .github/CODEOWNERS.template and OWNERS.github files."""
import os
import sys
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
HEADER = """# ============================================================================
# THIS FILE IS AUTO-GENERATED - DO NOT EDIT DIRECTLY
#
# This file is automatically generated by tools/gen_github_codeowners
# Changes to this file will be overwritten.
#
# To modify this file, edit OWNERS.github files throughout the repository.
# ============================================================================
"""
def main():
output = HEADER
# Add content from root OWNERS.github
root_owners_path = os.path.join(ROOT_DIR, 'OWNERS.github')
with open(root_owners_path, 'r') as f:
output += f.read().rstrip() + '\n'
# Add separator
output += '\n# ============================================================================\n'
output += '# AUTO-GENERATED ENTRIES FROM OWNERS.github FILES\n'
output += '# ============================================================================\n'
# Find and process all OWNERS.github files
for root, dirs, files in os.walk(ROOT_DIR):
dirs[:] = [d for d in dirs if not d.startswith('.') and d != 'node_modules']
if 'OWNERS.github' not in files:
continue
rel_dir = os.path.relpath(root, ROOT_DIR)
# Skip root OWNERS.github as it's already processed
if rel_dir == '.':
continue
base_path = '/' + rel_dir.replace(os.sep, '/')
if not base_path.endswith('/'):
base_path += '/'
with open(os.path.join(root, 'OWNERS.github'), 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
parts = line.split()
if len(parts) < 2:
continue
pattern = parts[0]
owners = ' '.join(parts[1:])
# Security check: reject patterns with parent directory references
if '../' in pattern or pattern.startswith('..'):
owners_file = os.path.join(root, 'OWNERS.github')
print(f'ERROR: Invalid pattern in {owners_file}: {pattern}')
print('Patterns cannot contain "../" for security reasons.')
sys.exit(1)
# Convert relative pattern to absolute path
if pattern == '/' or pattern == './' or pattern == '.':
path = base_path
elif pattern.startswith('/'):
path = base_path + pattern[1:]
else:
path = base_path + pattern
output += f'{path} {owners}\n'
# Write output
codeowners_path = os.path.join(ROOT_DIR, '.github', 'CODEOWNERS')
with open(codeowners_path, 'w') as f:
f.write(output)
print(f'Generated {codeowners_path}')
if __name__ == '__main__':
sys.exit(main())