1# Copyright 2022 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Ensure all modules have OWNERS files.""" 15 16import logging 17from pathlib import Path 18from typing import Callable 19 20from pw_presubmit.presubmit_context import ( 21 PresubmitContext, 22 PresubmitFailure, 23) 24from pw_presubmit import presubmit 25 26_LOG: logging.Logger = logging.getLogger(__name__) 27 28 29def upstream_pigweed_applicability( 30 ctx: PresubmitContext, 31 path: Path, 32) -> Path | None: 33 """Return a parent of path required to have an OWNERS file, or None.""" 34 parts: tuple[str, ...] = path.relative_to(ctx.root).parts 35 36 if len(parts) >= 2 and parts[0].startswith('pw_'): 37 return ctx.root / parts[0] 38 if len(parts) >= 3 and parts[0] in ('targets', 'third_party'): 39 return ctx.root / parts[0] / parts[1] 40 41 return None 42 43 44ApplicabilityFunc = Callable[[PresubmitContext, Path], Path | None] 45 46 47def presubmit_check( 48 applicability: ApplicabilityFunc = upstream_pigweed_applicability, 49) -> presubmit.Check: 50 """Create a presubmit check for the presence of OWNERS files.""" 51 52 @presubmit.check(name='module_owners') 53 def check(ctx: PresubmitContext) -> None: 54 """Presubmit check that ensures all modules have OWNERS files.""" 55 56 modules_to_check = set() 57 58 for path in ctx.paths: 59 result = applicability(ctx, path) 60 if result: 61 modules_to_check.add(result) 62 63 errors = 0 64 for module in sorted(modules_to_check): 65 _LOG.debug('Checking module %s', module) 66 owners_path = module / 'OWNERS' 67 if not owners_path.is_file(): 68 _LOG.error('%s is missing an OWNERS file', module) 69 errors += 1 70 continue 71 72 with owners_path.open() as ins: 73 contents = [x.strip() for x in ins.read().strip().splitlines()] 74 wo_comments = [x for x in contents if not x.startswith('#')] 75 owners = [x for x in wo_comments if 'per-file' not in x] 76 if len(owners) < 1: 77 _LOG.error('%s is too short: add owners', owners_path) 78 79 if errors: 80 raise PresubmitFailure 81 82 return check 83