1*d68f33bcSAndroid Build Coastguard Worker# Copyright 2016 The Android Open Source Project 2*d68f33bcSAndroid Build Coastguard Worker# 3*d68f33bcSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*d68f33bcSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*d68f33bcSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*d68f33bcSAndroid Build Coastguard Worker# 7*d68f33bcSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*d68f33bcSAndroid Build Coastguard Worker# 9*d68f33bcSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*d68f33bcSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*d68f33bcSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*d68f33bcSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*d68f33bcSAndroid Build Coastguard Worker# limitations under the License. 14*d68f33bcSAndroid Build Coastguard Worker 15*d68f33bcSAndroid Build Coastguard Worker"""Functions that implement the actual checks.""" 16*d68f33bcSAndroid Build Coastguard Worker 17*d68f33bcSAndroid Build Coastguard Workerimport fnmatch 18*d68f33bcSAndroid Build Coastguard Workerimport json 19*d68f33bcSAndroid Build Coastguard Workerimport os 20*d68f33bcSAndroid Build Coastguard Workerimport platform 21*d68f33bcSAndroid Build Coastguard Workerimport re 22*d68f33bcSAndroid Build Coastguard Workerimport sys 23*d68f33bcSAndroid Build Coastguard Workerfrom typing import Callable, NamedTuple 24*d68f33bcSAndroid Build Coastguard Worker 25*d68f33bcSAndroid Build Coastguard Worker_path = os.path.realpath(__file__ + '/../..') 26*d68f33bcSAndroid Build Coastguard Workerif sys.path[0] != _path: 27*d68f33bcSAndroid Build Coastguard Worker sys.path.insert(0, _path) 28*d68f33bcSAndroid Build Coastguard Workerdel _path 29*d68f33bcSAndroid Build Coastguard Worker 30*d68f33bcSAndroid Build Coastguard Worker# pylint: disable=wrong-import-position 31*d68f33bcSAndroid Build Coastguard Workerimport rh.git 32*d68f33bcSAndroid Build Coastguard Workerimport rh.results 33*d68f33bcSAndroid Build Coastguard Workerimport rh.utils 34*d68f33bcSAndroid Build Coastguard Worker 35*d68f33bcSAndroid Build Coastguard Worker 36*d68f33bcSAndroid Build Coastguard Workerclass Placeholders(object): 37*d68f33bcSAndroid Build Coastguard Worker """Holder class for replacing ${vars} in arg lists. 38*d68f33bcSAndroid Build Coastguard Worker 39*d68f33bcSAndroid Build Coastguard Worker To add a new variable to replace in config files, just add it as a @property 40*d68f33bcSAndroid Build Coastguard Worker to this class using the form. So to add support for BIRD: 41*d68f33bcSAndroid Build Coastguard Worker @property 42*d68f33bcSAndroid Build Coastguard Worker def var_BIRD(self): 43*d68f33bcSAndroid Build Coastguard Worker return <whatever this is> 44*d68f33bcSAndroid Build Coastguard Worker 45*d68f33bcSAndroid Build Coastguard Worker You can return either a string or an iterable (e.g. a list or tuple). 46*d68f33bcSAndroid Build Coastguard Worker """ 47*d68f33bcSAndroid Build Coastguard Worker 48*d68f33bcSAndroid Build Coastguard Worker def __init__(self, diff=()): 49*d68f33bcSAndroid Build Coastguard Worker """Initialize. 50*d68f33bcSAndroid Build Coastguard Worker 51*d68f33bcSAndroid Build Coastguard Worker Args: 52*d68f33bcSAndroid Build Coastguard Worker diff: The list of files that changed. 53*d68f33bcSAndroid Build Coastguard Worker """ 54*d68f33bcSAndroid Build Coastguard Worker self.diff = diff 55*d68f33bcSAndroid Build Coastguard Worker 56*d68f33bcSAndroid Build Coastguard Worker def expand_vars(self, args): 57*d68f33bcSAndroid Build Coastguard Worker """Perform place holder expansion on all of |args|. 58*d68f33bcSAndroid Build Coastguard Worker 59*d68f33bcSAndroid Build Coastguard Worker Args: 60*d68f33bcSAndroid Build Coastguard Worker args: The args to perform expansion on. 61*d68f33bcSAndroid Build Coastguard Worker 62*d68f33bcSAndroid Build Coastguard Worker Returns: 63*d68f33bcSAndroid Build Coastguard Worker The updated |args| list. 64*d68f33bcSAndroid Build Coastguard Worker """ 65*d68f33bcSAndroid Build Coastguard Worker all_vars = set(self.vars()) 66*d68f33bcSAndroid Build Coastguard Worker replacements = dict((var, self.get(var)) for var in all_vars) 67*d68f33bcSAndroid Build Coastguard Worker 68*d68f33bcSAndroid Build Coastguard Worker ret = [] 69*d68f33bcSAndroid Build Coastguard Worker for arg in args: 70*d68f33bcSAndroid Build Coastguard Worker if arg.endswith('${PREUPLOAD_FILES_PREFIXED}'): 71*d68f33bcSAndroid Build Coastguard Worker if arg == '${PREUPLOAD_FILES_PREFIXED}': 72*d68f33bcSAndroid Build Coastguard Worker assert len(ret) > 1, ('PREUPLOAD_FILES_PREFIXED cannot be ' 73*d68f33bcSAndroid Build Coastguard Worker 'the 1st or 2nd argument') 74*d68f33bcSAndroid Build Coastguard Worker prev_arg = ret[-1] 75*d68f33bcSAndroid Build Coastguard Worker ret = ret[0:-1] 76*d68f33bcSAndroid Build Coastguard Worker for file in self.get('PREUPLOAD_FILES'): 77*d68f33bcSAndroid Build Coastguard Worker ret.append(prev_arg) 78*d68f33bcSAndroid Build Coastguard Worker ret.append(file) 79*d68f33bcSAndroid Build Coastguard Worker else: 80*d68f33bcSAndroid Build Coastguard Worker prefix = arg[0:-len('${PREUPLOAD_FILES_PREFIXED}')] 81*d68f33bcSAndroid Build Coastguard Worker ret.extend( 82*d68f33bcSAndroid Build Coastguard Worker prefix + file for file in self.get('PREUPLOAD_FILES')) 83*d68f33bcSAndroid Build Coastguard Worker else: 84*d68f33bcSAndroid Build Coastguard Worker # First scan for exact matches 85*d68f33bcSAndroid Build Coastguard Worker for key, val in replacements.items(): 86*d68f33bcSAndroid Build Coastguard Worker var = '${' + key + '}' 87*d68f33bcSAndroid Build Coastguard Worker if arg == var: 88*d68f33bcSAndroid Build Coastguard Worker if isinstance(val, str): 89*d68f33bcSAndroid Build Coastguard Worker ret.append(val) 90*d68f33bcSAndroid Build Coastguard Worker else: 91*d68f33bcSAndroid Build Coastguard Worker ret.extend(val) 92*d68f33bcSAndroid Build Coastguard Worker # We break on first hit to avoid double expansion. 93*d68f33bcSAndroid Build Coastguard Worker break 94*d68f33bcSAndroid Build Coastguard Worker else: 95*d68f33bcSAndroid Build Coastguard Worker # If no exact matches, do an inline replacement. 96*d68f33bcSAndroid Build Coastguard Worker def replace(m): 97*d68f33bcSAndroid Build Coastguard Worker val = self.get(m.group(1)) 98*d68f33bcSAndroid Build Coastguard Worker if isinstance(val, str): 99*d68f33bcSAndroid Build Coastguard Worker return val 100*d68f33bcSAndroid Build Coastguard Worker return ' '.join(val) 101*d68f33bcSAndroid Build Coastguard Worker ret.append(re.sub(r'\$\{(' + '|'.join(all_vars) + r')\}', 102*d68f33bcSAndroid Build Coastguard Worker replace, arg)) 103*d68f33bcSAndroid Build Coastguard Worker return ret 104*d68f33bcSAndroid Build Coastguard Worker 105*d68f33bcSAndroid Build Coastguard Worker @classmethod 106*d68f33bcSAndroid Build Coastguard Worker def vars(cls): 107*d68f33bcSAndroid Build Coastguard Worker """Yield all replacement variable names.""" 108*d68f33bcSAndroid Build Coastguard Worker for key in dir(cls): 109*d68f33bcSAndroid Build Coastguard Worker if key.startswith('var_'): 110*d68f33bcSAndroid Build Coastguard Worker yield key[4:] 111*d68f33bcSAndroid Build Coastguard Worker 112*d68f33bcSAndroid Build Coastguard Worker def get(self, var): 113*d68f33bcSAndroid Build Coastguard Worker """Helper function to get the replacement |var| value.""" 114*d68f33bcSAndroid Build Coastguard Worker return getattr(self, f'var_{var}') 115*d68f33bcSAndroid Build Coastguard Worker 116*d68f33bcSAndroid Build Coastguard Worker @property 117*d68f33bcSAndroid Build Coastguard Worker def var_PREUPLOAD_COMMIT_MESSAGE(self): 118*d68f33bcSAndroid Build Coastguard Worker """The git commit message.""" 119*d68f33bcSAndroid Build Coastguard Worker return os.environ.get('PREUPLOAD_COMMIT_MESSAGE', '') 120*d68f33bcSAndroid Build Coastguard Worker 121*d68f33bcSAndroid Build Coastguard Worker @property 122*d68f33bcSAndroid Build Coastguard Worker def var_PREUPLOAD_COMMIT(self): 123*d68f33bcSAndroid Build Coastguard Worker """The git commit sha1.""" 124*d68f33bcSAndroid Build Coastguard Worker return os.environ.get('PREUPLOAD_COMMIT', '') 125*d68f33bcSAndroid Build Coastguard Worker 126*d68f33bcSAndroid Build Coastguard Worker @property 127*d68f33bcSAndroid Build Coastguard Worker def var_PREUPLOAD_FILES(self): 128*d68f33bcSAndroid Build Coastguard Worker """List of files modified in this git commit.""" 129*d68f33bcSAndroid Build Coastguard Worker return [x.file for x in self.diff if x.status != 'D'] 130*d68f33bcSAndroid Build Coastguard Worker 131*d68f33bcSAndroid Build Coastguard Worker @property 132*d68f33bcSAndroid Build Coastguard Worker def var_REPO_PATH(self): 133*d68f33bcSAndroid Build Coastguard Worker """The path to the project relative to the root""" 134*d68f33bcSAndroid Build Coastguard Worker return os.environ.get('REPO_PATH', '') 135*d68f33bcSAndroid Build Coastguard Worker 136*d68f33bcSAndroid Build Coastguard Worker @property 137*d68f33bcSAndroid Build Coastguard Worker def var_REPO_PROJECT(self): 138*d68f33bcSAndroid Build Coastguard Worker """The name of the project""" 139*d68f33bcSAndroid Build Coastguard Worker return os.environ.get('REPO_PROJECT', '') 140*d68f33bcSAndroid Build Coastguard Worker 141*d68f33bcSAndroid Build Coastguard Worker @property 142*d68f33bcSAndroid Build Coastguard Worker def var_REPO_ROOT(self): 143*d68f33bcSAndroid Build Coastguard Worker """The root of the repo (sub-manifest) checkout.""" 144*d68f33bcSAndroid Build Coastguard Worker return rh.git.find_repo_root() 145*d68f33bcSAndroid Build Coastguard Worker 146*d68f33bcSAndroid Build Coastguard Worker @property 147*d68f33bcSAndroid Build Coastguard Worker def var_REPO_OUTER_ROOT(self): 148*d68f33bcSAndroid Build Coastguard Worker """The root of the repo (outer) checkout.""" 149*d68f33bcSAndroid Build Coastguard Worker return rh.git.find_repo_root(outer=True) 150*d68f33bcSAndroid Build Coastguard Worker 151*d68f33bcSAndroid Build Coastguard Worker @property 152*d68f33bcSAndroid Build Coastguard Worker def var_BUILD_OS(self): 153*d68f33bcSAndroid Build Coastguard Worker """The build OS (see _get_build_os_name for details).""" 154*d68f33bcSAndroid Build Coastguard Worker return _get_build_os_name() 155*d68f33bcSAndroid Build Coastguard Worker 156*d68f33bcSAndroid Build Coastguard Worker 157*d68f33bcSAndroid Build Coastguard Workerclass ExclusionScope(object): 158*d68f33bcSAndroid Build Coastguard Worker """Exclusion scope for a hook. 159*d68f33bcSAndroid Build Coastguard Worker 160*d68f33bcSAndroid Build Coastguard Worker An exclusion scope can be used to determine if a hook has been disabled for 161*d68f33bcSAndroid Build Coastguard Worker a specific project. 162*d68f33bcSAndroid Build Coastguard Worker """ 163*d68f33bcSAndroid Build Coastguard Worker 164*d68f33bcSAndroid Build Coastguard Worker def __init__(self, scope): 165*d68f33bcSAndroid Build Coastguard Worker """Initialize. 166*d68f33bcSAndroid Build Coastguard Worker 167*d68f33bcSAndroid Build Coastguard Worker Args: 168*d68f33bcSAndroid Build Coastguard Worker scope: A list of shell-style wildcards (fnmatch) or regular 169*d68f33bcSAndroid Build Coastguard Worker expression. Regular expressions must start with the ^ character. 170*d68f33bcSAndroid Build Coastguard Worker """ 171*d68f33bcSAndroid Build Coastguard Worker self._scope = [] 172*d68f33bcSAndroid Build Coastguard Worker for path in scope: 173*d68f33bcSAndroid Build Coastguard Worker if path.startswith('^'): 174*d68f33bcSAndroid Build Coastguard Worker self._scope.append(re.compile(path)) 175*d68f33bcSAndroid Build Coastguard Worker else: 176*d68f33bcSAndroid Build Coastguard Worker self._scope.append(path) 177*d68f33bcSAndroid Build Coastguard Worker 178*d68f33bcSAndroid Build Coastguard Worker def __contains__(self, proj_dir): 179*d68f33bcSAndroid Build Coastguard Worker """Checks if |proj_dir| matches the excluded paths. 180*d68f33bcSAndroid Build Coastguard Worker 181*d68f33bcSAndroid Build Coastguard Worker Args: 182*d68f33bcSAndroid Build Coastguard Worker proj_dir: The relative path of the project. 183*d68f33bcSAndroid Build Coastguard Worker """ 184*d68f33bcSAndroid Build Coastguard Worker for exclusion_path in self._scope: 185*d68f33bcSAndroid Build Coastguard Worker if hasattr(exclusion_path, 'match'): 186*d68f33bcSAndroid Build Coastguard Worker if exclusion_path.match(proj_dir): 187*d68f33bcSAndroid Build Coastguard Worker return True 188*d68f33bcSAndroid Build Coastguard Worker elif fnmatch.fnmatch(proj_dir, exclusion_path): 189*d68f33bcSAndroid Build Coastguard Worker return True 190*d68f33bcSAndroid Build Coastguard Worker return False 191*d68f33bcSAndroid Build Coastguard Worker 192*d68f33bcSAndroid Build Coastguard Worker 193*d68f33bcSAndroid Build Coastguard Workerclass HookOptions(object): 194*d68f33bcSAndroid Build Coastguard Worker """Holder class for hook options.""" 195*d68f33bcSAndroid Build Coastguard Worker 196*d68f33bcSAndroid Build Coastguard Worker def __init__(self, name, args, tool_paths): 197*d68f33bcSAndroid Build Coastguard Worker """Initialize. 198*d68f33bcSAndroid Build Coastguard Worker 199*d68f33bcSAndroid Build Coastguard Worker Args: 200*d68f33bcSAndroid Build Coastguard Worker name: The name of the hook. 201*d68f33bcSAndroid Build Coastguard Worker args: The override commandline arguments for the hook. 202*d68f33bcSAndroid Build Coastguard Worker tool_paths: A dictionary with tool names to paths. 203*d68f33bcSAndroid Build Coastguard Worker """ 204*d68f33bcSAndroid Build Coastguard Worker self.name = name 205*d68f33bcSAndroid Build Coastguard Worker self._args = args 206*d68f33bcSAndroid Build Coastguard Worker self._tool_paths = tool_paths 207*d68f33bcSAndroid Build Coastguard Worker 208*d68f33bcSAndroid Build Coastguard Worker @staticmethod 209*d68f33bcSAndroid Build Coastguard Worker def expand_vars(args, diff=()): 210*d68f33bcSAndroid Build Coastguard Worker """Perform place holder expansion on all of |args|.""" 211*d68f33bcSAndroid Build Coastguard Worker replacer = Placeholders(diff=diff) 212*d68f33bcSAndroid Build Coastguard Worker return replacer.expand_vars(args) 213*d68f33bcSAndroid Build Coastguard Worker 214*d68f33bcSAndroid Build Coastguard Worker def args(self, default_args=(), diff=()): 215*d68f33bcSAndroid Build Coastguard Worker """Gets the hook arguments, after performing place holder expansion. 216*d68f33bcSAndroid Build Coastguard Worker 217*d68f33bcSAndroid Build Coastguard Worker Args: 218*d68f33bcSAndroid Build Coastguard Worker default_args: The list to return if |self._args| is empty. 219*d68f33bcSAndroid Build Coastguard Worker diff: The list of files that changed in the current commit. 220*d68f33bcSAndroid Build Coastguard Worker 221*d68f33bcSAndroid Build Coastguard Worker Returns: 222*d68f33bcSAndroid Build Coastguard Worker A list with arguments. 223*d68f33bcSAndroid Build Coastguard Worker """ 224*d68f33bcSAndroid Build Coastguard Worker args = self._args 225*d68f33bcSAndroid Build Coastguard Worker if not args: 226*d68f33bcSAndroid Build Coastguard Worker args = default_args 227*d68f33bcSAndroid Build Coastguard Worker 228*d68f33bcSAndroid Build Coastguard Worker return self.expand_vars(args, diff=diff) 229*d68f33bcSAndroid Build Coastguard Worker 230*d68f33bcSAndroid Build Coastguard Worker def tool_path(self, tool_name): 231*d68f33bcSAndroid Build Coastguard Worker """Gets the path in which the |tool_name| executable can be found. 232*d68f33bcSAndroid Build Coastguard Worker 233*d68f33bcSAndroid Build Coastguard Worker This function performs expansion for some place holders. If the tool 234*d68f33bcSAndroid Build Coastguard Worker does not exist in the overridden |self._tool_paths| dictionary, the tool 235*d68f33bcSAndroid Build Coastguard Worker name will be returned and will be run from the user's $PATH. 236*d68f33bcSAndroid Build Coastguard Worker 237*d68f33bcSAndroid Build Coastguard Worker Args: 238*d68f33bcSAndroid Build Coastguard Worker tool_name: The name of the executable. 239*d68f33bcSAndroid Build Coastguard Worker 240*d68f33bcSAndroid Build Coastguard Worker Returns: 241*d68f33bcSAndroid Build Coastguard Worker The path of the tool with all optional place holders expanded. 242*d68f33bcSAndroid Build Coastguard Worker """ 243*d68f33bcSAndroid Build Coastguard Worker assert tool_name in TOOL_PATHS 244*d68f33bcSAndroid Build Coastguard Worker if tool_name not in self._tool_paths: 245*d68f33bcSAndroid Build Coastguard Worker return TOOL_PATHS[tool_name] 246*d68f33bcSAndroid Build Coastguard Worker 247*d68f33bcSAndroid Build Coastguard Worker tool_path = os.path.normpath(self._tool_paths[tool_name]) 248*d68f33bcSAndroid Build Coastguard Worker return self.expand_vars([tool_path])[0] 249*d68f33bcSAndroid Build Coastguard Worker 250*d68f33bcSAndroid Build Coastguard Worker 251*d68f33bcSAndroid Build Coastguard Workerclass CallableHook(NamedTuple): 252*d68f33bcSAndroid Build Coastguard Worker """A callable hook.""" 253*d68f33bcSAndroid Build Coastguard Worker name: str 254*d68f33bcSAndroid Build Coastguard Worker hook: Callable 255*d68f33bcSAndroid Build Coastguard Worker scope: ExclusionScope 256*d68f33bcSAndroid Build Coastguard Worker 257*d68f33bcSAndroid Build Coastguard Worker 258*d68f33bcSAndroid Build Coastguard Workerdef _run(cmd, **kwargs): 259*d68f33bcSAndroid Build Coastguard Worker """Helper command for checks that tend to gather output.""" 260*d68f33bcSAndroid Build Coastguard Worker kwargs.setdefault('combine_stdout_stderr', True) 261*d68f33bcSAndroid Build Coastguard Worker kwargs.setdefault('capture_output', True) 262*d68f33bcSAndroid Build Coastguard Worker kwargs.setdefault('check', False) 263*d68f33bcSAndroid Build Coastguard Worker # Make sure hooks run with stdin disconnected to avoid accidentally 264*d68f33bcSAndroid Build Coastguard Worker # interactive tools causing pauses. 265*d68f33bcSAndroid Build Coastguard Worker kwargs.setdefault('input', '') 266*d68f33bcSAndroid Build Coastguard Worker return rh.utils.run(cmd, **kwargs) 267*d68f33bcSAndroid Build Coastguard Worker 268*d68f33bcSAndroid Build Coastguard Worker 269*d68f33bcSAndroid Build Coastguard Workerdef _match_regex_list(subject, expressions): 270*d68f33bcSAndroid Build Coastguard Worker """Try to match a list of regular expressions to a string. 271*d68f33bcSAndroid Build Coastguard Worker 272*d68f33bcSAndroid Build Coastguard Worker Args: 273*d68f33bcSAndroid Build Coastguard Worker subject: The string to match regexes on. 274*d68f33bcSAndroid Build Coastguard Worker expressions: An iterable of regular expressions to check for matches with. 275*d68f33bcSAndroid Build Coastguard Worker 276*d68f33bcSAndroid Build Coastguard Worker Returns: 277*d68f33bcSAndroid Build Coastguard Worker Whether the passed in subject matches any of the passed in regexes. 278*d68f33bcSAndroid Build Coastguard Worker """ 279*d68f33bcSAndroid Build Coastguard Worker for expr in expressions: 280*d68f33bcSAndroid Build Coastguard Worker if re.search(expr, subject): 281*d68f33bcSAndroid Build Coastguard Worker return True 282*d68f33bcSAndroid Build Coastguard Worker return False 283*d68f33bcSAndroid Build Coastguard Worker 284*d68f33bcSAndroid Build Coastguard Worker 285*d68f33bcSAndroid Build Coastguard Workerdef _filter_diff(diff, include_list, exclude_list=()): 286*d68f33bcSAndroid Build Coastguard Worker """Filter out files based on the conditions passed in. 287*d68f33bcSAndroid Build Coastguard Worker 288*d68f33bcSAndroid Build Coastguard Worker Args: 289*d68f33bcSAndroid Build Coastguard Worker diff: list of diff objects to filter. 290*d68f33bcSAndroid Build Coastguard Worker include_list: list of regex that when matched with a file path will cause 291*d68f33bcSAndroid Build Coastguard Worker it to be added to the output list unless the file is also matched with 292*d68f33bcSAndroid Build Coastguard Worker a regex in the exclude_list. 293*d68f33bcSAndroid Build Coastguard Worker exclude_list: list of regex that when matched with a file will prevent it 294*d68f33bcSAndroid Build Coastguard Worker from being added to the output list, even if it is also matched with a 295*d68f33bcSAndroid Build Coastguard Worker regex in the include_list. 296*d68f33bcSAndroid Build Coastguard Worker 297*d68f33bcSAndroid Build Coastguard Worker Returns: 298*d68f33bcSAndroid Build Coastguard Worker A list of filepaths that contain files matched in the include_list and not 299*d68f33bcSAndroid Build Coastguard Worker in the exclude_list. 300*d68f33bcSAndroid Build Coastguard Worker """ 301*d68f33bcSAndroid Build Coastguard Worker filtered = [] 302*d68f33bcSAndroid Build Coastguard Worker for d in diff: 303*d68f33bcSAndroid Build Coastguard Worker if (d.status != 'D' and 304*d68f33bcSAndroid Build Coastguard Worker _match_regex_list(d.file, include_list) and 305*d68f33bcSAndroid Build Coastguard Worker not _match_regex_list(d.file, exclude_list)): 306*d68f33bcSAndroid Build Coastguard Worker # We've got a match! 307*d68f33bcSAndroid Build Coastguard Worker filtered.append(d) 308*d68f33bcSAndroid Build Coastguard Worker return filtered 309*d68f33bcSAndroid Build Coastguard Worker 310*d68f33bcSAndroid Build Coastguard Worker 311*d68f33bcSAndroid Build Coastguard Workerdef _get_build_os_name(): 312*d68f33bcSAndroid Build Coastguard Worker """Gets the build OS name. 313*d68f33bcSAndroid Build Coastguard Worker 314*d68f33bcSAndroid Build Coastguard Worker Returns: 315*d68f33bcSAndroid Build Coastguard Worker A string in a format usable to get prebuilt tool paths. 316*d68f33bcSAndroid Build Coastguard Worker """ 317*d68f33bcSAndroid Build Coastguard Worker system = platform.system() 318*d68f33bcSAndroid Build Coastguard Worker if 'Darwin' in system or 'Macintosh' in system: 319*d68f33bcSAndroid Build Coastguard Worker return 'darwin-x86' 320*d68f33bcSAndroid Build Coastguard Worker 321*d68f33bcSAndroid Build Coastguard Worker # TODO: Add more values if needed. 322*d68f33bcSAndroid Build Coastguard Worker return 'linux-x86' 323*d68f33bcSAndroid Build Coastguard Worker 324*d68f33bcSAndroid Build Coastguard Worker 325*d68f33bcSAndroid Build Coastguard Workerdef _check_cmd(hook_name, project, commit, cmd, fixup_cmd=None, **kwargs): 326*d68f33bcSAndroid Build Coastguard Worker """Runs |cmd| and returns its result as a HookCommandResult.""" 327*d68f33bcSAndroid Build Coastguard Worker return [rh.results.HookCommandResult(hook_name, project, commit, 328*d68f33bcSAndroid Build Coastguard Worker _run(cmd, **kwargs), 329*d68f33bcSAndroid Build Coastguard Worker fixup_cmd=fixup_cmd)] 330*d68f33bcSAndroid Build Coastguard Worker 331*d68f33bcSAndroid Build Coastguard Worker 332*d68f33bcSAndroid Build Coastguard Worker# Where helper programs exist. 333*d68f33bcSAndroid Build Coastguard WorkerTOOLS_DIR = os.path.realpath(__file__ + '/../../tools') 334*d68f33bcSAndroid Build Coastguard Worker 335*d68f33bcSAndroid Build Coastguard Workerdef get_helper_path(tool): 336*d68f33bcSAndroid Build Coastguard Worker """Return the full path to the helper |tool|.""" 337*d68f33bcSAndroid Build Coastguard Worker return os.path.join(TOOLS_DIR, tool) 338*d68f33bcSAndroid Build Coastguard Worker 339*d68f33bcSAndroid Build Coastguard Worker 340*d68f33bcSAndroid Build Coastguard Workerdef check_custom(project, commit, _desc, diff, options=None, **kwargs): 341*d68f33bcSAndroid Build Coastguard Worker """Run a custom hook.""" 342*d68f33bcSAndroid Build Coastguard Worker return _check_cmd(options.name, project, commit, options.args((), diff), 343*d68f33bcSAndroid Build Coastguard Worker **kwargs) 344*d68f33bcSAndroid Build Coastguard Worker 345*d68f33bcSAndroid Build Coastguard Worker 346*d68f33bcSAndroid Build Coastguard Workerdef check_aosp_license(project, commit, _desc, diff, options=None): 347*d68f33bcSAndroid Build Coastguard Worker """Checks that if all new added files has AOSP licenses""" 348*d68f33bcSAndroid Build Coastguard Worker 349*d68f33bcSAndroid Build Coastguard Worker exclude_dir_args = [x for x in options.args() 350*d68f33bcSAndroid Build Coastguard Worker if x.startswith('--exclude-dirs=')] 351*d68f33bcSAndroid Build Coastguard Worker exclude_dirs = [x[len('--exclude-dirs='):].split(',') 352*d68f33bcSAndroid Build Coastguard Worker for x in exclude_dir_args] 353*d68f33bcSAndroid Build Coastguard Worker exclude_list = [fr'^{x}/.*$' for dir_list in exclude_dirs for x in dir_list] 354*d68f33bcSAndroid Build Coastguard Worker 355*d68f33bcSAndroid Build Coastguard Worker # Filter diff based on extension. 356*d68f33bcSAndroid Build Coastguard Worker include_list = [ 357*d68f33bcSAndroid Build Coastguard Worker # Coding languages and scripts. 358*d68f33bcSAndroid Build Coastguard Worker r".*\.c$", 359*d68f33bcSAndroid Build Coastguard Worker r".*\.cc$", 360*d68f33bcSAndroid Build Coastguard Worker r".*\.cpp$", 361*d68f33bcSAndroid Build Coastguard Worker r".*\.h$", 362*d68f33bcSAndroid Build Coastguard Worker r".*\.java$", 363*d68f33bcSAndroid Build Coastguard Worker r".*\.kt$", 364*d68f33bcSAndroid Build Coastguard Worker r".*\.rs$", 365*d68f33bcSAndroid Build Coastguard Worker r".*\.py$", 366*d68f33bcSAndroid Build Coastguard Worker r".*\.sh$", 367*d68f33bcSAndroid Build Coastguard Worker 368*d68f33bcSAndroid Build Coastguard Worker # Build and config files. 369*d68f33bcSAndroid Build Coastguard Worker r".*\.bp$", 370*d68f33bcSAndroid Build Coastguard Worker r".*\.mk$", 371*d68f33bcSAndroid Build Coastguard Worker r".*\.xml$", 372*d68f33bcSAndroid Build Coastguard Worker ] 373*d68f33bcSAndroid Build Coastguard Worker diff = _filter_diff(diff, include_list, exclude_list) 374*d68f33bcSAndroid Build Coastguard Worker 375*d68f33bcSAndroid Build Coastguard Worker # Only check the new-added files. 376*d68f33bcSAndroid Build Coastguard Worker diff = [d for d in diff if d.status == 'A'] 377*d68f33bcSAndroid Build Coastguard Worker 378*d68f33bcSAndroid Build Coastguard Worker if not diff: 379*d68f33bcSAndroid Build Coastguard Worker return None 380*d68f33bcSAndroid Build Coastguard Worker 381*d68f33bcSAndroid Build Coastguard Worker cmd = [get_helper_path('check_aosp_license.py'), '--commit_hash', commit] 382*d68f33bcSAndroid Build Coastguard Worker cmd += HookOptions.expand_vars(('${PREUPLOAD_FILES}',), diff) 383*d68f33bcSAndroid Build Coastguard Worker return _check_cmd('aosp_license', project, commit, cmd) 384*d68f33bcSAndroid Build Coastguard Worker 385*d68f33bcSAndroid Build Coastguard Worker 386*d68f33bcSAndroid Build Coastguard Workerdef check_bpfmt(project, commit, _desc, diff, options=None): 387*d68f33bcSAndroid Build Coastguard Worker """Checks that Blueprint files are formatted with bpfmt.""" 388*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, [r'\.bp$']) 389*d68f33bcSAndroid Build Coastguard Worker if not filtered: 390*d68f33bcSAndroid Build Coastguard Worker return None 391*d68f33bcSAndroid Build Coastguard Worker 392*d68f33bcSAndroid Build Coastguard Worker bpfmt = options.tool_path('bpfmt') 393*d68f33bcSAndroid Build Coastguard Worker bpfmt_options = options.args((), filtered) 394*d68f33bcSAndroid Build Coastguard Worker cmd = [bpfmt, '-d'] + bpfmt_options 395*d68f33bcSAndroid Build Coastguard Worker fixup_cmd = [bpfmt, '-w'] 396*d68f33bcSAndroid Build Coastguard Worker if '-s' in bpfmt_options: 397*d68f33bcSAndroid Build Coastguard Worker fixup_cmd.append('-s') 398*d68f33bcSAndroid Build Coastguard Worker fixup_cmd.append('--') 399*d68f33bcSAndroid Build Coastguard Worker 400*d68f33bcSAndroid Build Coastguard Worker ret = [] 401*d68f33bcSAndroid Build Coastguard Worker for d in filtered: 402*d68f33bcSAndroid Build Coastguard Worker data = rh.git.get_file_content(commit, d.file) 403*d68f33bcSAndroid Build Coastguard Worker result = _run(cmd, input=data) 404*d68f33bcSAndroid Build Coastguard Worker if result.stdout: 405*d68f33bcSAndroid Build Coastguard Worker ret.append(rh.results.HookResult( 406*d68f33bcSAndroid Build Coastguard Worker 'bpfmt', project, commit, 407*d68f33bcSAndroid Build Coastguard Worker error=result.stdout, 408*d68f33bcSAndroid Build Coastguard Worker files=(d.file,), 409*d68f33bcSAndroid Build Coastguard Worker fixup_cmd=fixup_cmd)) 410*d68f33bcSAndroid Build Coastguard Worker return ret 411*d68f33bcSAndroid Build Coastguard Worker 412*d68f33bcSAndroid Build Coastguard Worker 413*d68f33bcSAndroid Build Coastguard Workerdef check_checkpatch(project, commit, _desc, diff, options=None): 414*d68f33bcSAndroid Build Coastguard Worker """Run |diff| through the kernel's checkpatch.pl tool.""" 415*d68f33bcSAndroid Build Coastguard Worker tool = get_helper_path('checkpatch.pl') 416*d68f33bcSAndroid Build Coastguard Worker cmd = ([tool, '-', '--root', project.dir] + 417*d68f33bcSAndroid Build Coastguard Worker options.args(('--ignore=GERRIT_CHANGE_ID',), diff)) 418*d68f33bcSAndroid Build Coastguard Worker return _check_cmd('checkpatch.pl', project, commit, cmd, 419*d68f33bcSAndroid Build Coastguard Worker input=rh.git.get_patch(commit)) 420*d68f33bcSAndroid Build Coastguard Worker 421*d68f33bcSAndroid Build Coastguard Worker 422*d68f33bcSAndroid Build Coastguard Workerdef check_clang_format(project, commit, _desc, diff, options=None): 423*d68f33bcSAndroid Build Coastguard Worker """Run git clang-format on the commit.""" 424*d68f33bcSAndroid Build Coastguard Worker tool = get_helper_path('clang-format.py') 425*d68f33bcSAndroid Build Coastguard Worker clang_format = options.tool_path('clang-format') 426*d68f33bcSAndroid Build Coastguard Worker git_clang_format = options.tool_path('git-clang-format') 427*d68f33bcSAndroid Build Coastguard Worker tool_args = (['--clang-format', clang_format, '--git-clang-format', 428*d68f33bcSAndroid Build Coastguard Worker git_clang_format] + 429*d68f33bcSAndroid Build Coastguard Worker options.args(('--style', 'file', '--commit', commit), diff)) 430*d68f33bcSAndroid Build Coastguard Worker cmd = [tool] + tool_args 431*d68f33bcSAndroid Build Coastguard Worker fixup_cmd = [tool, '--fix'] + tool_args 432*d68f33bcSAndroid Build Coastguard Worker return _check_cmd('clang-format', project, commit, cmd, 433*d68f33bcSAndroid Build Coastguard Worker fixup_cmd=fixup_cmd) 434*d68f33bcSAndroid Build Coastguard Worker 435*d68f33bcSAndroid Build Coastguard Worker 436*d68f33bcSAndroid Build Coastguard Workerdef check_google_java_format(project, commit, _desc, _diff, options=None): 437*d68f33bcSAndroid Build Coastguard Worker """Run google-java-format on the commit.""" 438*d68f33bcSAndroid Build Coastguard Worker include_dir_args = [x for x in options.args() 439*d68f33bcSAndroid Build Coastguard Worker if x.startswith('--include-dirs=')] 440*d68f33bcSAndroid Build Coastguard Worker include_dirs = [x[len('--include-dirs='):].split(',') 441*d68f33bcSAndroid Build Coastguard Worker for x in include_dir_args] 442*d68f33bcSAndroid Build Coastguard Worker patterns = [fr'^{x}/.*\.java$' for dir_list in include_dirs 443*d68f33bcSAndroid Build Coastguard Worker for x in dir_list] 444*d68f33bcSAndroid Build Coastguard Worker if not patterns: 445*d68f33bcSAndroid Build Coastguard Worker patterns = [r'\.java$'] 446*d68f33bcSAndroid Build Coastguard Worker 447*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(_diff, patterns) 448*d68f33bcSAndroid Build Coastguard Worker 449*d68f33bcSAndroid Build Coastguard Worker if not filtered: 450*d68f33bcSAndroid Build Coastguard Worker return None 451*d68f33bcSAndroid Build Coastguard Worker 452*d68f33bcSAndroid Build Coastguard Worker args = [x for x in options.args() if x not in include_dir_args] 453*d68f33bcSAndroid Build Coastguard Worker 454*d68f33bcSAndroid Build Coastguard Worker tool = get_helper_path('google-java-format.py') 455*d68f33bcSAndroid Build Coastguard Worker google_java_format = options.tool_path('google-java-format') 456*d68f33bcSAndroid Build Coastguard Worker google_java_format_diff = options.tool_path('google-java-format-diff') 457*d68f33bcSAndroid Build Coastguard Worker tool_args = ['--google-java-format', google_java_format, 458*d68f33bcSAndroid Build Coastguard Worker '--google-java-format-diff', google_java_format_diff, 459*d68f33bcSAndroid Build Coastguard Worker '--commit', commit] + args 460*d68f33bcSAndroid Build Coastguard Worker cmd = [tool] + tool_args + HookOptions.expand_vars( 461*d68f33bcSAndroid Build Coastguard Worker ('${PREUPLOAD_FILES}',), filtered) 462*d68f33bcSAndroid Build Coastguard Worker fixup_cmd = [tool, '--fix'] + tool_args 463*d68f33bcSAndroid Build Coastguard Worker return [rh.results.HookCommandResult('google-java-format', project, commit, 464*d68f33bcSAndroid Build Coastguard Worker _run(cmd), 465*d68f33bcSAndroid Build Coastguard Worker files=[x.file for x in filtered], 466*d68f33bcSAndroid Build Coastguard Worker fixup_cmd=fixup_cmd)] 467*d68f33bcSAndroid Build Coastguard Worker 468*d68f33bcSAndroid Build Coastguard Worker 469*d68f33bcSAndroid Build Coastguard Workerdef check_ktfmt(project, commit, _desc, diff, options=None): 470*d68f33bcSAndroid Build Coastguard Worker """Checks that kotlin files are formatted with ktfmt.""" 471*d68f33bcSAndroid Build Coastguard Worker 472*d68f33bcSAndroid Build Coastguard Worker include_dir_args = [x for x in options.args() 473*d68f33bcSAndroid Build Coastguard Worker if x.startswith('--include-dirs=')] 474*d68f33bcSAndroid Build Coastguard Worker include_dirs = [x[len('--include-dirs='):].split(',') 475*d68f33bcSAndroid Build Coastguard Worker for x in include_dir_args] 476*d68f33bcSAndroid Build Coastguard Worker patterns = [fr'^{x}/.*\.kt$' for dir_list in include_dirs 477*d68f33bcSAndroid Build Coastguard Worker for x in dir_list] 478*d68f33bcSAndroid Build Coastguard Worker if not patterns: 479*d68f33bcSAndroid Build Coastguard Worker patterns = [r'\.kt$'] 480*d68f33bcSAndroid Build Coastguard Worker 481*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, patterns) 482*d68f33bcSAndroid Build Coastguard Worker 483*d68f33bcSAndroid Build Coastguard Worker if not filtered: 484*d68f33bcSAndroid Build Coastguard Worker return None 485*d68f33bcSAndroid Build Coastguard Worker 486*d68f33bcSAndroid Build Coastguard Worker args = [x for x in options.args() if x not in include_dir_args] 487*d68f33bcSAndroid Build Coastguard Worker 488*d68f33bcSAndroid Build Coastguard Worker ktfmt = options.tool_path('ktfmt') 489*d68f33bcSAndroid Build Coastguard Worker cmd = [ktfmt, '--dry-run'] + args + HookOptions.expand_vars( 490*d68f33bcSAndroid Build Coastguard Worker ('${PREUPLOAD_FILES}',), filtered) 491*d68f33bcSAndroid Build Coastguard Worker result = _run(cmd) 492*d68f33bcSAndroid Build Coastguard Worker if result.stdout: 493*d68f33bcSAndroid Build Coastguard Worker fixup_cmd = [ktfmt] + args 494*d68f33bcSAndroid Build Coastguard Worker return [rh.results.HookResult( 495*d68f33bcSAndroid Build Coastguard Worker 'ktfmt', project, commit, error='Formatting errors detected', 496*d68f33bcSAndroid Build Coastguard Worker files=[x.file for x in filtered], fixup_cmd=fixup_cmd)] 497*d68f33bcSAndroid Build Coastguard Worker return None 498*d68f33bcSAndroid Build Coastguard Worker 499*d68f33bcSAndroid Build Coastguard Worker 500*d68f33bcSAndroid Build Coastguard Workerdef check_commit_msg_bug_field(project, commit, desc, _diff, options=None): 501*d68f33bcSAndroid Build Coastguard Worker """Check the commit message for a 'Bug:' or 'Fix:' line.""" 502*d68f33bcSAndroid Build Coastguard Worker regex = r'^(Bug|Fix): (None|[0-9]+(, [0-9]+)*)$' 503*d68f33bcSAndroid Build Coastguard Worker check_re = re.compile(regex) 504*d68f33bcSAndroid Build Coastguard Worker 505*d68f33bcSAndroid Build Coastguard Worker if options.args(): 506*d68f33bcSAndroid Build Coastguard Worker raise ValueError('commit msg Bug check takes no options') 507*d68f33bcSAndroid Build Coastguard Worker 508*d68f33bcSAndroid Build Coastguard Worker found = [] 509*d68f33bcSAndroid Build Coastguard Worker for line in desc.splitlines(): 510*d68f33bcSAndroid Build Coastguard Worker if check_re.match(line): 511*d68f33bcSAndroid Build Coastguard Worker found.append(line) 512*d68f33bcSAndroid Build Coastguard Worker 513*d68f33bcSAndroid Build Coastguard Worker if not found: 514*d68f33bcSAndroid Build Coastguard Worker error = ( 515*d68f33bcSAndroid Build Coastguard Worker 'Commit message is missing a "Bug:" line. It must match the\n' 516*d68f33bcSAndroid Build Coastguard Worker f'following case-sensitive regex:\n\n {regex}' 517*d68f33bcSAndroid Build Coastguard Worker ) 518*d68f33bcSAndroid Build Coastguard Worker else: 519*d68f33bcSAndroid Build Coastguard Worker return None 520*d68f33bcSAndroid Build Coastguard Worker 521*d68f33bcSAndroid Build Coastguard Worker return [rh.results.HookResult('commit msg: "Bug:" check', 522*d68f33bcSAndroid Build Coastguard Worker project, commit, error=error)] 523*d68f33bcSAndroid Build Coastguard Worker 524*d68f33bcSAndroid Build Coastguard Worker 525*d68f33bcSAndroid Build Coastguard Workerdef check_commit_msg_changeid_field(project, commit, desc, _diff, options=None): 526*d68f33bcSAndroid Build Coastguard Worker """Check the commit message for a 'Change-Id:' line.""" 527*d68f33bcSAndroid Build Coastguard Worker field = 'Change-Id' 528*d68f33bcSAndroid Build Coastguard Worker regex = fr'^{field}: I[a-f0-9]+$' 529*d68f33bcSAndroid Build Coastguard Worker check_re = re.compile(regex) 530*d68f33bcSAndroid Build Coastguard Worker 531*d68f33bcSAndroid Build Coastguard Worker if options.args(): 532*d68f33bcSAndroid Build Coastguard Worker raise ValueError(f'commit msg {field} check takes no options') 533*d68f33bcSAndroid Build Coastguard Worker 534*d68f33bcSAndroid Build Coastguard Worker found = [] 535*d68f33bcSAndroid Build Coastguard Worker for line in desc.splitlines(): 536*d68f33bcSAndroid Build Coastguard Worker if check_re.match(line): 537*d68f33bcSAndroid Build Coastguard Worker found.append(line) 538*d68f33bcSAndroid Build Coastguard Worker 539*d68f33bcSAndroid Build Coastguard Worker if not found: 540*d68f33bcSAndroid Build Coastguard Worker error = ( 541*d68f33bcSAndroid Build Coastguard Worker f'Commit message is missing a "{field}:" line. It must match the\n' 542*d68f33bcSAndroid Build Coastguard Worker f'following case-sensitive regex:\n\n {regex}' 543*d68f33bcSAndroid Build Coastguard Worker ) 544*d68f33bcSAndroid Build Coastguard Worker elif len(found) > 1: 545*d68f33bcSAndroid Build Coastguard Worker error = (f'Commit message has too many "{field}:" lines. There can be ' 546*d68f33bcSAndroid Build Coastguard Worker 'only one.') 547*d68f33bcSAndroid Build Coastguard Worker else: 548*d68f33bcSAndroid Build Coastguard Worker return None 549*d68f33bcSAndroid Build Coastguard Worker 550*d68f33bcSAndroid Build Coastguard Worker return [rh.results.HookResult(f'commit msg: "{field}:" check', 551*d68f33bcSAndroid Build Coastguard Worker project, commit, error=error)] 552*d68f33bcSAndroid Build Coastguard Worker 553*d68f33bcSAndroid Build Coastguard Worker 554*d68f33bcSAndroid Build Coastguard WorkerPREBUILT_APK_MSG = """Commit message is missing required prebuilt APK 555*d68f33bcSAndroid Build Coastguard Workerinformation. To generate the information, use the aapt tool to dump badging 556*d68f33bcSAndroid Build Coastguard Workerinformation of the APKs being uploaded, specify where the APK was built, and 557*d68f33bcSAndroid Build Coastguard Workerspecify whether the APKs are suitable for release: 558*d68f33bcSAndroid Build Coastguard Worker 559*d68f33bcSAndroid Build Coastguard Worker for apk in $(find . -name '*.apk' | sort); do 560*d68f33bcSAndroid Build Coastguard Worker echo "${apk}" 561*d68f33bcSAndroid Build Coastguard Worker ${AAPT} dump badging "${apk}" | 562*d68f33bcSAndroid Build Coastguard Worker grep -iE "(package: |sdkVersion:|targetSdkVersion:)" | 563*d68f33bcSAndroid Build Coastguard Worker sed -e "s/' /'\\n/g" 564*d68f33bcSAndroid Build Coastguard Worker echo 565*d68f33bcSAndroid Build Coastguard Worker done 566*d68f33bcSAndroid Build Coastguard Worker 567*d68f33bcSAndroid Build Coastguard WorkerIt must match the following case-sensitive multiline regex searches: 568*d68f33bcSAndroid Build Coastguard Worker 569*d68f33bcSAndroid Build Coastguard Worker %s 570*d68f33bcSAndroid Build Coastguard Worker 571*d68f33bcSAndroid Build Coastguard WorkerFor more information, see go/platform-prebuilt and go/android-prebuilt. 572*d68f33bcSAndroid Build Coastguard Worker 573*d68f33bcSAndroid Build Coastguard Worker""" 574*d68f33bcSAndroid Build Coastguard Worker 575*d68f33bcSAndroid Build Coastguard Worker 576*d68f33bcSAndroid Build Coastguard Workerdef check_commit_msg_prebuilt_apk_fields(project, commit, desc, diff, 577*d68f33bcSAndroid Build Coastguard Worker options=None): 578*d68f33bcSAndroid Build Coastguard Worker """Check that prebuilt APK commits contain the required lines.""" 579*d68f33bcSAndroid Build Coastguard Worker 580*d68f33bcSAndroid Build Coastguard Worker if options.args(): 581*d68f33bcSAndroid Build Coastguard Worker raise ValueError('prebuilt apk check takes no options') 582*d68f33bcSAndroid Build Coastguard Worker 583*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, [r'\.apk$']) 584*d68f33bcSAndroid Build Coastguard Worker if not filtered: 585*d68f33bcSAndroid Build Coastguard Worker return None 586*d68f33bcSAndroid Build Coastguard Worker 587*d68f33bcSAndroid Build Coastguard Worker regexes = [ 588*d68f33bcSAndroid Build Coastguard Worker r'^package: .*$', 589*d68f33bcSAndroid Build Coastguard Worker r'^sdkVersion:.*$', 590*d68f33bcSAndroid Build Coastguard Worker r'^targetSdkVersion:.*$', 591*d68f33bcSAndroid Build Coastguard Worker r'^Built here:.*$', 592*d68f33bcSAndroid Build Coastguard Worker (r'^This build IS( NOT)? suitable for' 593*d68f33bcSAndroid Build Coastguard Worker r'( preview|( preview or)? public) release' 594*d68f33bcSAndroid Build Coastguard Worker r'( but IS NOT suitable for public release)?\.$') 595*d68f33bcSAndroid Build Coastguard Worker ] 596*d68f33bcSAndroid Build Coastguard Worker 597*d68f33bcSAndroid Build Coastguard Worker missing = [] 598*d68f33bcSAndroid Build Coastguard Worker for regex in regexes: 599*d68f33bcSAndroid Build Coastguard Worker if not re.search(regex, desc, re.MULTILINE): 600*d68f33bcSAndroid Build Coastguard Worker missing.append(regex) 601*d68f33bcSAndroid Build Coastguard Worker 602*d68f33bcSAndroid Build Coastguard Worker if missing: 603*d68f33bcSAndroid Build Coastguard Worker error = PREBUILT_APK_MSG % '\n '.join(missing) 604*d68f33bcSAndroid Build Coastguard Worker else: 605*d68f33bcSAndroid Build Coastguard Worker return None 606*d68f33bcSAndroid Build Coastguard Worker 607*d68f33bcSAndroid Build Coastguard Worker return [rh.results.HookResult('commit msg: "prebuilt apk:" check', 608*d68f33bcSAndroid Build Coastguard Worker project, commit, error=error)] 609*d68f33bcSAndroid Build Coastguard Worker 610*d68f33bcSAndroid Build Coastguard Worker 611*d68f33bcSAndroid Build Coastguard WorkerTEST_MSG = """Commit message is missing a "Test:" line. It must match the 612*d68f33bcSAndroid Build Coastguard Workerfollowing case-sensitive regex: 613*d68f33bcSAndroid Build Coastguard Worker 614*d68f33bcSAndroid Build Coastguard Worker %s 615*d68f33bcSAndroid Build Coastguard Worker 616*d68f33bcSAndroid Build Coastguard WorkerThe Test: stanza is free-form and should describe how you tested your change. 617*d68f33bcSAndroid Build Coastguard WorkerAs a CL author, you'll have a consistent place to describe the testing strategy 618*d68f33bcSAndroid Build Coastguard Workeryou use for your work. As a CL reviewer, you'll be reminded to discuss testing 619*d68f33bcSAndroid Build Coastguard Workeras part of your code review, and you'll more easily replicate testing when you 620*d68f33bcSAndroid Build Coastguard Workerpatch in CLs locally. 621*d68f33bcSAndroid Build Coastguard Worker 622*d68f33bcSAndroid Build Coastguard WorkerSome examples below: 623*d68f33bcSAndroid Build Coastguard Worker 624*d68f33bcSAndroid Build Coastguard WorkerTest: make WITH_TIDY=1 mmma art 625*d68f33bcSAndroid Build Coastguard WorkerTest: make test-art 626*d68f33bcSAndroid Build Coastguard WorkerTest: manual - took a photo 627*d68f33bcSAndroid Build Coastguard WorkerTest: refactoring CL. Existing unit tests still pass. 628*d68f33bcSAndroid Build Coastguard Worker 629*d68f33bcSAndroid Build Coastguard WorkerCheck the git history for more examples. It's a free-form field, so we urge 630*d68f33bcSAndroid Build Coastguard Workeryou to develop conventions that make sense for your project. Note that many 631*d68f33bcSAndroid Build Coastguard Workerprojects use exact test commands, which are perfectly fine. 632*d68f33bcSAndroid Build Coastguard Worker 633*d68f33bcSAndroid Build Coastguard WorkerAdding good automated tests with new code is critical to our goals of keeping 634*d68f33bcSAndroid Build Coastguard Workerthe system stable and constantly improving quality. Please use Test: to 635*d68f33bcSAndroid Build Coastguard Workerhighlight this area of your development. And reviewers, please insist on 636*d68f33bcSAndroid Build Coastguard Workerhigh-quality Test: descriptions. 637*d68f33bcSAndroid Build Coastguard Worker""" 638*d68f33bcSAndroid Build Coastguard Worker 639*d68f33bcSAndroid Build Coastguard Worker 640*d68f33bcSAndroid Build Coastguard Workerdef check_commit_msg_test_field(project, commit, desc, _diff, options=None): 641*d68f33bcSAndroid Build Coastguard Worker """Check the commit message for a 'Test:' line.""" 642*d68f33bcSAndroid Build Coastguard Worker field = 'Test' 643*d68f33bcSAndroid Build Coastguard Worker regex = fr'^{field}: .*$' 644*d68f33bcSAndroid Build Coastguard Worker check_re = re.compile(regex) 645*d68f33bcSAndroid Build Coastguard Worker 646*d68f33bcSAndroid Build Coastguard Worker if options.args(): 647*d68f33bcSAndroid Build Coastguard Worker raise ValueError(f'commit msg {field} check takes no options') 648*d68f33bcSAndroid Build Coastguard Worker 649*d68f33bcSAndroid Build Coastguard Worker found = [] 650*d68f33bcSAndroid Build Coastguard Worker for line in desc.splitlines(): 651*d68f33bcSAndroid Build Coastguard Worker if check_re.match(line): 652*d68f33bcSAndroid Build Coastguard Worker found.append(line) 653*d68f33bcSAndroid Build Coastguard Worker 654*d68f33bcSAndroid Build Coastguard Worker if not found: 655*d68f33bcSAndroid Build Coastguard Worker error = TEST_MSG % (regex) 656*d68f33bcSAndroid Build Coastguard Worker else: 657*d68f33bcSAndroid Build Coastguard Worker return None 658*d68f33bcSAndroid Build Coastguard Worker 659*d68f33bcSAndroid Build Coastguard Worker return [rh.results.HookResult(f'commit msg: "{field}:" check', 660*d68f33bcSAndroid Build Coastguard Worker project, commit, error=error)] 661*d68f33bcSAndroid Build Coastguard Worker 662*d68f33bcSAndroid Build Coastguard Worker 663*d68f33bcSAndroid Build Coastguard WorkerRELNOTE_MISSPELL_MSG = """Commit message contains something that looks 664*d68f33bcSAndroid Build Coastguard Workersimilar to the "Relnote:" tag. It must match the regex: 665*d68f33bcSAndroid Build Coastguard Worker 666*d68f33bcSAndroid Build Coastguard Worker %s 667*d68f33bcSAndroid Build Coastguard Worker 668*d68f33bcSAndroid Build Coastguard WorkerThe Relnote: stanza is free-form and should describe what developers need to 669*d68f33bcSAndroid Build Coastguard Workerknow about your change. 670*d68f33bcSAndroid Build Coastguard Worker 671*d68f33bcSAndroid Build Coastguard WorkerSome examples below: 672*d68f33bcSAndroid Build Coastguard Worker 673*d68f33bcSAndroid Build Coastguard WorkerRelnote: "Added a new API `Class#isBetter` to determine whether or not the 674*d68f33bcSAndroid Build Coastguard Workerclass is better" 675*d68f33bcSAndroid Build Coastguard WorkerRelnote: Fixed an issue where the UI would hang on a double tap. 676*d68f33bcSAndroid Build Coastguard Worker 677*d68f33bcSAndroid Build Coastguard WorkerCheck the git history for more examples. It's a free-form field, so we urge 678*d68f33bcSAndroid Build Coastguard Workeryou to develop conventions that make sense for your project. 679*d68f33bcSAndroid Build Coastguard Worker""" 680*d68f33bcSAndroid Build Coastguard Worker 681*d68f33bcSAndroid Build Coastguard WorkerRELNOTE_MISSING_QUOTES_MSG = """Commit message contains something that looks 682*d68f33bcSAndroid Build Coastguard Workersimilar to the "Relnote:" tag but might be malformatted. For multiline 683*d68f33bcSAndroid Build Coastguard Workerrelease notes, you need to include a starting and closing quote. 684*d68f33bcSAndroid Build Coastguard Worker 685*d68f33bcSAndroid Build Coastguard WorkerMulti-line Relnote example: 686*d68f33bcSAndroid Build Coastguard Worker 687*d68f33bcSAndroid Build Coastguard WorkerRelnote: "Added a new API `Class#getSize` to get the size of the class. 688*d68f33bcSAndroid Build Coastguard Worker This is useful if you need to know the size of the class." 689*d68f33bcSAndroid Build Coastguard Worker 690*d68f33bcSAndroid Build Coastguard WorkerSingle-line Relnote example: 691*d68f33bcSAndroid Build Coastguard Worker 692*d68f33bcSAndroid Build Coastguard WorkerRelnote: Added a new API `Class#containsData` 693*d68f33bcSAndroid Build Coastguard Worker""" 694*d68f33bcSAndroid Build Coastguard Worker 695*d68f33bcSAndroid Build Coastguard WorkerRELNOTE_INVALID_QUOTES_MSG = """Commit message contains something that looks 696*d68f33bcSAndroid Build Coastguard Workersimilar to the "Relnote:" tag but might be malformatted. If you are using 697*d68f33bcSAndroid Build Coastguard Workerquotes that do not mark the start or end of a Relnote, you need to escape them 698*d68f33bcSAndroid Build Coastguard Workerwith a backslash. 699*d68f33bcSAndroid Build Coastguard Worker 700*d68f33bcSAndroid Build Coastguard WorkerNon-starting/non-ending quote Relnote examples: 701*d68f33bcSAndroid Build Coastguard Worker 702*d68f33bcSAndroid Build Coastguard WorkerRelnote: "Fixed an error with `Class#getBar()` where \"foo\" would be returned 703*d68f33bcSAndroid Build Coastguard Workerin edge cases." 704*d68f33bcSAndroid Build Coastguard WorkerRelnote: Added a new API to handle strings like \"foo\" 705*d68f33bcSAndroid Build Coastguard Worker""" 706*d68f33bcSAndroid Build Coastguard Worker 707*d68f33bcSAndroid Build Coastguard Workerdef check_commit_msg_relnote_field_format(project, commit, desc, _diff, 708*d68f33bcSAndroid Build Coastguard Worker options=None): 709*d68f33bcSAndroid Build Coastguard Worker """Check the commit for one correctly formatted 'Relnote:' line. 710*d68f33bcSAndroid Build Coastguard Worker 711*d68f33bcSAndroid Build Coastguard Worker Checks the commit message for two things: 712*d68f33bcSAndroid Build Coastguard Worker (1) Checks for possible misspellings of the 'Relnote:' tag. 713*d68f33bcSAndroid Build Coastguard Worker (2) Ensures that multiline release notes are properly formatted with a 714*d68f33bcSAndroid Build Coastguard Worker starting quote and an endling quote. 715*d68f33bcSAndroid Build Coastguard Worker (3) Checks that release notes that contain non-starting or non-ending 716*d68f33bcSAndroid Build Coastguard Worker quotes are escaped with a backslash. 717*d68f33bcSAndroid Build Coastguard Worker """ 718*d68f33bcSAndroid Build Coastguard Worker field = 'Relnote' 719*d68f33bcSAndroid Build Coastguard Worker regex_relnote = fr'^{field}:.*$' 720*d68f33bcSAndroid Build Coastguard Worker check_re_relnote = re.compile(regex_relnote, re.IGNORECASE) 721*d68f33bcSAndroid Build Coastguard Worker 722*d68f33bcSAndroid Build Coastguard Worker if options.args(): 723*d68f33bcSAndroid Build Coastguard Worker raise ValueError(f'commit msg {field} check takes no options') 724*d68f33bcSAndroid Build Coastguard Worker 725*d68f33bcSAndroid Build Coastguard Worker # Check 1: Check for possible misspellings of the `Relnote:` field. 726*d68f33bcSAndroid Build Coastguard Worker 727*d68f33bcSAndroid Build Coastguard Worker # Regex for misspelled fields. 728*d68f33bcSAndroid Build Coastguard Worker possible_field_misspells = { 729*d68f33bcSAndroid Build Coastguard Worker 'Relnotes', 'ReleaseNote', 730*d68f33bcSAndroid Build Coastguard Worker 'Rel-note', 'Rel note', 731*d68f33bcSAndroid Build Coastguard Worker 'rel-notes', 'releasenotes', 732*d68f33bcSAndroid Build Coastguard Worker 'release-note', 'release-notes', 733*d68f33bcSAndroid Build Coastguard Worker } 734*d68f33bcSAndroid Build Coastguard Worker re_possible_field_misspells = '|'.join(possible_field_misspells) 735*d68f33bcSAndroid Build Coastguard Worker regex_field_misspells = fr'^({re_possible_field_misspells}): .*$' 736*d68f33bcSAndroid Build Coastguard Worker check_re_field_misspells = re.compile(regex_field_misspells, re.IGNORECASE) 737*d68f33bcSAndroid Build Coastguard Worker 738*d68f33bcSAndroid Build Coastguard Worker ret = [] 739*d68f33bcSAndroid Build Coastguard Worker for line in desc.splitlines(): 740*d68f33bcSAndroid Build Coastguard Worker if check_re_field_misspells.match(line): 741*d68f33bcSAndroid Build Coastguard Worker error = RELNOTE_MISSPELL_MSG % (regex_relnote, ) 742*d68f33bcSAndroid Build Coastguard Worker ret.append( 743*d68f33bcSAndroid Build Coastguard Worker rh.results.HookResult( 744*d68f33bcSAndroid Build Coastguard Worker f'commit msg: "{field}:" tag spelling error', 745*d68f33bcSAndroid Build Coastguard Worker project, commit, error=error)) 746*d68f33bcSAndroid Build Coastguard Worker 747*d68f33bcSAndroid Build Coastguard Worker # Check 2: Check that multiline Relnotes are quoted. 748*d68f33bcSAndroid Build Coastguard Worker 749*d68f33bcSAndroid Build Coastguard Worker check_re_empty_string = re.compile(r'^$') 750*d68f33bcSAndroid Build Coastguard Worker 751*d68f33bcSAndroid Build Coastguard Worker # Regex to find other fields that could be used. 752*d68f33bcSAndroid Build Coastguard Worker regex_other_fields = r'^[a-zA-Z0-9-]+:' 753*d68f33bcSAndroid Build Coastguard Worker check_re_other_fields = re.compile(regex_other_fields) 754*d68f33bcSAndroid Build Coastguard Worker 755*d68f33bcSAndroid Build Coastguard Worker desc_lines = desc.splitlines() 756*d68f33bcSAndroid Build Coastguard Worker for i, cur_line in enumerate(desc_lines): 757*d68f33bcSAndroid Build Coastguard Worker # Look for a Relnote tag that is before the last line and 758*d68f33bcSAndroid Build Coastguard Worker # lacking any quotes. 759*d68f33bcSAndroid Build Coastguard Worker if (check_re_relnote.match(cur_line) and 760*d68f33bcSAndroid Build Coastguard Worker i < len(desc_lines) - 1 and 761*d68f33bcSAndroid Build Coastguard Worker '"' not in cur_line): 762*d68f33bcSAndroid Build Coastguard Worker next_line = desc_lines[i + 1] 763*d68f33bcSAndroid Build Coastguard Worker # Check that the next line does not contain any other field 764*d68f33bcSAndroid Build Coastguard Worker # and it's not an empty string. 765*d68f33bcSAndroid Build Coastguard Worker if (not check_re_other_fields.findall(next_line) and 766*d68f33bcSAndroid Build Coastguard Worker not check_re_empty_string.match(next_line)): 767*d68f33bcSAndroid Build Coastguard Worker ret.append( 768*d68f33bcSAndroid Build Coastguard Worker rh.results.HookResult( 769*d68f33bcSAndroid Build Coastguard Worker f'commit msg: "{field}:" tag missing quotes', 770*d68f33bcSAndroid Build Coastguard Worker project, commit, error=RELNOTE_MISSING_QUOTES_MSG)) 771*d68f33bcSAndroid Build Coastguard Worker break 772*d68f33bcSAndroid Build Coastguard Worker 773*d68f33bcSAndroid Build Coastguard Worker # Check 3: Check that multiline Relnotes contain matching quotes. 774*d68f33bcSAndroid Build Coastguard Worker first_quote_found = False 775*d68f33bcSAndroid Build Coastguard Worker second_quote_found = False 776*d68f33bcSAndroid Build Coastguard Worker for cur_line in desc_lines: 777*d68f33bcSAndroid Build Coastguard Worker contains_quote = '"' in cur_line 778*d68f33bcSAndroid Build Coastguard Worker contains_field = check_re_other_fields.findall(cur_line) 779*d68f33bcSAndroid Build Coastguard Worker # If we have found the first quote and another field, break and fail. 780*d68f33bcSAndroid Build Coastguard Worker if first_quote_found and contains_field: 781*d68f33bcSAndroid Build Coastguard Worker break 782*d68f33bcSAndroid Build Coastguard Worker # If we have found the first quote, this line contains a quote, 783*d68f33bcSAndroid Build Coastguard Worker # and this line is not another field, break and succeed. 784*d68f33bcSAndroid Build Coastguard Worker if first_quote_found and contains_quote: 785*d68f33bcSAndroid Build Coastguard Worker second_quote_found = True 786*d68f33bcSAndroid Build Coastguard Worker break 787*d68f33bcSAndroid Build Coastguard Worker # Check that the `Relnote:` tag exists and it contains a starting quote. 788*d68f33bcSAndroid Build Coastguard Worker if check_re_relnote.match(cur_line) and contains_quote: 789*d68f33bcSAndroid Build Coastguard Worker first_quote_found = True 790*d68f33bcSAndroid Build Coastguard Worker # A single-line Relnote containing a start and ending triple quote 791*d68f33bcSAndroid Build Coastguard Worker # is valid. 792*d68f33bcSAndroid Build Coastguard Worker if cur_line.count('"""') == 2: 793*d68f33bcSAndroid Build Coastguard Worker second_quote_found = True 794*d68f33bcSAndroid Build Coastguard Worker break 795*d68f33bcSAndroid Build Coastguard Worker # A single-line Relnote containing a start and ending quote 796*d68f33bcSAndroid Build Coastguard Worker # is valid. 797*d68f33bcSAndroid Build Coastguard Worker if cur_line.count('"') - cur_line.count('\\"') == 2: 798*d68f33bcSAndroid Build Coastguard Worker second_quote_found = True 799*d68f33bcSAndroid Build Coastguard Worker break 800*d68f33bcSAndroid Build Coastguard Worker if first_quote_found != second_quote_found: 801*d68f33bcSAndroid Build Coastguard Worker ret.append( 802*d68f33bcSAndroid Build Coastguard Worker rh.results.HookResult( 803*d68f33bcSAndroid Build Coastguard Worker f'commit msg: "{field}:" tag missing closing quote', 804*d68f33bcSAndroid Build Coastguard Worker project, commit, error=RELNOTE_MISSING_QUOTES_MSG)) 805*d68f33bcSAndroid Build Coastguard Worker 806*d68f33bcSAndroid Build Coastguard Worker # Check 4: Check that non-starting or non-ending quotes are escaped with a 807*d68f33bcSAndroid Build Coastguard Worker # backslash. 808*d68f33bcSAndroid Build Coastguard Worker line_needs_checking = False 809*d68f33bcSAndroid Build Coastguard Worker uses_invalid_quotes = False 810*d68f33bcSAndroid Build Coastguard Worker for cur_line in desc_lines: 811*d68f33bcSAndroid Build Coastguard Worker if check_re_other_fields.findall(cur_line): 812*d68f33bcSAndroid Build Coastguard Worker line_needs_checking = False 813*d68f33bcSAndroid Build Coastguard Worker on_relnote_line = check_re_relnote.match(cur_line) 814*d68f33bcSAndroid Build Coastguard Worker # Determine if we are parsing the base `Relnote:` line. 815*d68f33bcSAndroid Build Coastguard Worker if on_relnote_line and '"' in cur_line: 816*d68f33bcSAndroid Build Coastguard Worker line_needs_checking = True 817*d68f33bcSAndroid Build Coastguard Worker # We don't think anyone will type '"""' and then forget to 818*d68f33bcSAndroid Build Coastguard Worker # escape it, so we're not checking for this. 819*d68f33bcSAndroid Build Coastguard Worker if '"""' in cur_line: 820*d68f33bcSAndroid Build Coastguard Worker break 821*d68f33bcSAndroid Build Coastguard Worker if line_needs_checking: 822*d68f33bcSAndroid Build Coastguard Worker stripped_line = re.sub(fr'^{field}:', '', cur_line, 823*d68f33bcSAndroid Build Coastguard Worker flags=re.IGNORECASE).strip() 824*d68f33bcSAndroid Build Coastguard Worker for i, character in enumerate(stripped_line): 825*d68f33bcSAndroid Build Coastguard Worker if i == 0: 826*d68f33bcSAndroid Build Coastguard Worker # Case 1: Valid quote at the beginning of the 827*d68f33bcSAndroid Build Coastguard Worker # base `Relnote:` line. 828*d68f33bcSAndroid Build Coastguard Worker if on_relnote_line: 829*d68f33bcSAndroid Build Coastguard Worker continue 830*d68f33bcSAndroid Build Coastguard Worker # Case 2: Invalid quote at the beginning of following 831*d68f33bcSAndroid Build Coastguard Worker # lines, where we are not terminating the release note. 832*d68f33bcSAndroid Build Coastguard Worker if character == '"' and stripped_line != '"': 833*d68f33bcSAndroid Build Coastguard Worker uses_invalid_quotes = True 834*d68f33bcSAndroid Build Coastguard Worker break 835*d68f33bcSAndroid Build Coastguard Worker # Case 3: Check all other cases. 836*d68f33bcSAndroid Build Coastguard Worker if (character == '"' 837*d68f33bcSAndroid Build Coastguard Worker and 0 < i < len(stripped_line) - 1 838*d68f33bcSAndroid Build Coastguard Worker and stripped_line[i-1] != '"' 839*d68f33bcSAndroid Build Coastguard Worker and stripped_line[i-1] != "\\"): 840*d68f33bcSAndroid Build Coastguard Worker uses_invalid_quotes = True 841*d68f33bcSAndroid Build Coastguard Worker break 842*d68f33bcSAndroid Build Coastguard Worker 843*d68f33bcSAndroid Build Coastguard Worker if uses_invalid_quotes: 844*d68f33bcSAndroid Build Coastguard Worker ret.append(rh.results.HookResult( 845*d68f33bcSAndroid Build Coastguard Worker f'commit msg: "{field}:" tag using unescaped quotes', 846*d68f33bcSAndroid Build Coastguard Worker project, commit, error=RELNOTE_INVALID_QUOTES_MSG)) 847*d68f33bcSAndroid Build Coastguard Worker return ret 848*d68f33bcSAndroid Build Coastguard Worker 849*d68f33bcSAndroid Build Coastguard Worker 850*d68f33bcSAndroid Build Coastguard WorkerRELNOTE_REQUIRED_CURRENT_TXT_MSG = """\ 851*d68f33bcSAndroid Build Coastguard WorkerCommit contains a change to current.txt or public_plus_experimental_current.txt, 852*d68f33bcSAndroid Build Coastguard Workerbut the commit message does not contain the required `Relnote:` tag. It must 853*d68f33bcSAndroid Build Coastguard Workermatch the regex: 854*d68f33bcSAndroid Build Coastguard Worker 855*d68f33bcSAndroid Build Coastguard Worker %s 856*d68f33bcSAndroid Build Coastguard Worker 857*d68f33bcSAndroid Build Coastguard WorkerThe Relnote: stanza is free-form and should describe what developers need to 858*d68f33bcSAndroid Build Coastguard Workerknow about your change. If you are making infrastructure changes, you 859*d68f33bcSAndroid Build Coastguard Workercan set the Relnote: stanza to be "N/A" for the commit to not be included 860*d68f33bcSAndroid Build Coastguard Workerin release notes. 861*d68f33bcSAndroid Build Coastguard Worker 862*d68f33bcSAndroid Build Coastguard WorkerSome examples: 863*d68f33bcSAndroid Build Coastguard Worker 864*d68f33bcSAndroid Build Coastguard WorkerRelnote: "Added a new API `Class#isBetter` to determine whether or not the 865*d68f33bcSAndroid Build Coastguard Workerclass is better" 866*d68f33bcSAndroid Build Coastguard WorkerRelnote: Fixed an issue where the UI would hang on a double tap. 867*d68f33bcSAndroid Build Coastguard WorkerRelnote: N/A 868*d68f33bcSAndroid Build Coastguard Worker 869*d68f33bcSAndroid Build Coastguard WorkerCheck the git history for more examples. 870*d68f33bcSAndroid Build Coastguard Worker""" 871*d68f33bcSAndroid Build Coastguard Worker 872*d68f33bcSAndroid Build Coastguard Workerdef check_commit_msg_relnote_for_current_txt(project, commit, desc, diff, 873*d68f33bcSAndroid Build Coastguard Worker options=None): 874*d68f33bcSAndroid Build Coastguard Worker """Check changes to current.txt contain the 'Relnote:' stanza.""" 875*d68f33bcSAndroid Build Coastguard Worker field = 'Relnote' 876*d68f33bcSAndroid Build Coastguard Worker regex = fr'^{field}: .+$' 877*d68f33bcSAndroid Build Coastguard Worker check_re = re.compile(regex, re.IGNORECASE) 878*d68f33bcSAndroid Build Coastguard Worker 879*d68f33bcSAndroid Build Coastguard Worker if options.args(): 880*d68f33bcSAndroid Build Coastguard Worker raise ValueError(f'commit msg {field} check takes no options') 881*d68f33bcSAndroid Build Coastguard Worker 882*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff( 883*d68f33bcSAndroid Build Coastguard Worker diff, 884*d68f33bcSAndroid Build Coastguard Worker [r'(^|/)(public_plus_experimental_current|current)\.txt$'] 885*d68f33bcSAndroid Build Coastguard Worker ) 886*d68f33bcSAndroid Build Coastguard Worker # If the commit does not contain a change to *current.txt, then this repo 887*d68f33bcSAndroid Build Coastguard Worker # hook check no longer applies. 888*d68f33bcSAndroid Build Coastguard Worker if not filtered: 889*d68f33bcSAndroid Build Coastguard Worker return None 890*d68f33bcSAndroid Build Coastguard Worker 891*d68f33bcSAndroid Build Coastguard Worker found = [] 892*d68f33bcSAndroid Build Coastguard Worker for line in desc.splitlines(): 893*d68f33bcSAndroid Build Coastguard Worker if check_re.match(line): 894*d68f33bcSAndroid Build Coastguard Worker found.append(line) 895*d68f33bcSAndroid Build Coastguard Worker 896*d68f33bcSAndroid Build Coastguard Worker if not found: 897*d68f33bcSAndroid Build Coastguard Worker error = RELNOTE_REQUIRED_CURRENT_TXT_MSG % (regex) 898*d68f33bcSAndroid Build Coastguard Worker else: 899*d68f33bcSAndroid Build Coastguard Worker return None 900*d68f33bcSAndroid Build Coastguard Worker 901*d68f33bcSAndroid Build Coastguard Worker return [rh.results.HookResult(f'commit msg: "{field}:" check', 902*d68f33bcSAndroid Build Coastguard Worker project, commit, error=error)] 903*d68f33bcSAndroid Build Coastguard Worker 904*d68f33bcSAndroid Build Coastguard Worker 905*d68f33bcSAndroid Build Coastguard Workerdef check_cpplint(project, commit, _desc, diff, options=None): 906*d68f33bcSAndroid Build Coastguard Worker """Run cpplint.""" 907*d68f33bcSAndroid Build Coastguard Worker # This list matches what cpplint expects. We could run on more (like .cxx), 908*d68f33bcSAndroid Build Coastguard Worker # but cpplint would just ignore them. 909*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, [r'\.(cc|h|cpp|cu|cuh)$']) 910*d68f33bcSAndroid Build Coastguard Worker if not filtered: 911*d68f33bcSAndroid Build Coastguard Worker return None 912*d68f33bcSAndroid Build Coastguard Worker 913*d68f33bcSAndroid Build Coastguard Worker cpplint = options.tool_path('cpplint') 914*d68f33bcSAndroid Build Coastguard Worker cmd = [cpplint] + options.args(('${PREUPLOAD_FILES}',), filtered) 915*d68f33bcSAndroid Build Coastguard Worker return _check_cmd('cpplint', project, commit, cmd) 916*d68f33bcSAndroid Build Coastguard Worker 917*d68f33bcSAndroid Build Coastguard Worker 918*d68f33bcSAndroid Build Coastguard Workerdef check_gofmt(project, commit, _desc, diff, options=None): 919*d68f33bcSAndroid Build Coastguard Worker """Checks that Go files are formatted with gofmt.""" 920*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, [r'\.go$']) 921*d68f33bcSAndroid Build Coastguard Worker if not filtered: 922*d68f33bcSAndroid Build Coastguard Worker return None 923*d68f33bcSAndroid Build Coastguard Worker 924*d68f33bcSAndroid Build Coastguard Worker gofmt = options.tool_path('gofmt') 925*d68f33bcSAndroid Build Coastguard Worker cmd = [gofmt, '-l'] + options.args() 926*d68f33bcSAndroid Build Coastguard Worker fixup_cmd = [gofmt, '-w'] + options.args() 927*d68f33bcSAndroid Build Coastguard Worker 928*d68f33bcSAndroid Build Coastguard Worker ret = [] 929*d68f33bcSAndroid Build Coastguard Worker for d in filtered: 930*d68f33bcSAndroid Build Coastguard Worker data = rh.git.get_file_content(commit, d.file) 931*d68f33bcSAndroid Build Coastguard Worker result = _run(cmd, input=data) 932*d68f33bcSAndroid Build Coastguard Worker if result.stdout: 933*d68f33bcSAndroid Build Coastguard Worker ret.append(rh.results.HookResult( 934*d68f33bcSAndroid Build Coastguard Worker 'gofmt', project, commit, error=result.stdout, 935*d68f33bcSAndroid Build Coastguard Worker files=(d.file,), fixup_cmd=fixup_cmd)) 936*d68f33bcSAndroid Build Coastguard Worker return ret 937*d68f33bcSAndroid Build Coastguard Worker 938*d68f33bcSAndroid Build Coastguard Worker 939*d68f33bcSAndroid Build Coastguard Workerdef check_json(project, commit, _desc, diff, options=None): 940*d68f33bcSAndroid Build Coastguard Worker """Verify json files are valid.""" 941*d68f33bcSAndroid Build Coastguard Worker if options.args(): 942*d68f33bcSAndroid Build Coastguard Worker raise ValueError('json check takes no options') 943*d68f33bcSAndroid Build Coastguard Worker 944*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, [r'\.json$']) 945*d68f33bcSAndroid Build Coastguard Worker if not filtered: 946*d68f33bcSAndroid Build Coastguard Worker return None 947*d68f33bcSAndroid Build Coastguard Worker 948*d68f33bcSAndroid Build Coastguard Worker ret = [] 949*d68f33bcSAndroid Build Coastguard Worker for d in filtered: 950*d68f33bcSAndroid Build Coastguard Worker data = rh.git.get_file_content(commit, d.file) 951*d68f33bcSAndroid Build Coastguard Worker try: 952*d68f33bcSAndroid Build Coastguard Worker json.loads(data) 953*d68f33bcSAndroid Build Coastguard Worker except ValueError as e: 954*d68f33bcSAndroid Build Coastguard Worker ret.append(rh.results.HookResult( 955*d68f33bcSAndroid Build Coastguard Worker 'json', project, commit, error=str(e), 956*d68f33bcSAndroid Build Coastguard Worker files=(d.file,))) 957*d68f33bcSAndroid Build Coastguard Worker return ret 958*d68f33bcSAndroid Build Coastguard Worker 959*d68f33bcSAndroid Build Coastguard Worker 960*d68f33bcSAndroid Build Coastguard Workerdef _check_pylint(project, commit, _desc, diff, extra_args=None, options=None): 961*d68f33bcSAndroid Build Coastguard Worker """Run pylint.""" 962*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, [r'\.py$']) 963*d68f33bcSAndroid Build Coastguard Worker if not filtered: 964*d68f33bcSAndroid Build Coastguard Worker return None 965*d68f33bcSAndroid Build Coastguard Worker 966*d68f33bcSAndroid Build Coastguard Worker if extra_args is None: 967*d68f33bcSAndroid Build Coastguard Worker extra_args = [] 968*d68f33bcSAndroid Build Coastguard Worker 969*d68f33bcSAndroid Build Coastguard Worker pylint = options.tool_path('pylint') 970*d68f33bcSAndroid Build Coastguard Worker cmd = [ 971*d68f33bcSAndroid Build Coastguard Worker get_helper_path('pylint.py'), 972*d68f33bcSAndroid Build Coastguard Worker '--executable-path', pylint, 973*d68f33bcSAndroid Build Coastguard Worker ] + extra_args + options.args(('${PREUPLOAD_FILES}',), filtered) 974*d68f33bcSAndroid Build Coastguard Worker return _check_cmd('pylint', project, commit, cmd) 975*d68f33bcSAndroid Build Coastguard Worker 976*d68f33bcSAndroid Build Coastguard Worker 977*d68f33bcSAndroid Build Coastguard Workerdef check_pylint2(project, commit, desc, diff, options=None): 978*d68f33bcSAndroid Build Coastguard Worker """Run pylint through Python 2.""" 979*d68f33bcSAndroid Build Coastguard Worker return _check_pylint(project, commit, desc, diff, options=options) 980*d68f33bcSAndroid Build Coastguard Worker 981*d68f33bcSAndroid Build Coastguard Worker 982*d68f33bcSAndroid Build Coastguard Workerdef check_pylint3(project, commit, desc, diff, options=None): 983*d68f33bcSAndroid Build Coastguard Worker """Run pylint through Python 3.""" 984*d68f33bcSAndroid Build Coastguard Worker return _check_pylint(project, commit, desc, diff, 985*d68f33bcSAndroid Build Coastguard Worker extra_args=['--py3'], 986*d68f33bcSAndroid Build Coastguard Worker options=options) 987*d68f33bcSAndroid Build Coastguard Worker 988*d68f33bcSAndroid Build Coastguard Worker 989*d68f33bcSAndroid Build Coastguard Workerdef check_rustfmt(project, commit, _desc, diff, options=None): 990*d68f33bcSAndroid Build Coastguard Worker """Run "rustfmt --check" on diffed rust files""" 991*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, [r'\.rs$']) 992*d68f33bcSAndroid Build Coastguard Worker if not filtered: 993*d68f33bcSAndroid Build Coastguard Worker return None 994*d68f33bcSAndroid Build Coastguard Worker 995*d68f33bcSAndroid Build Coastguard Worker rustfmt = options.tool_path('rustfmt') 996*d68f33bcSAndroid Build Coastguard Worker cmd = [rustfmt] + options.args((), filtered) 997*d68f33bcSAndroid Build Coastguard Worker ret = [] 998*d68f33bcSAndroid Build Coastguard Worker for d in filtered: 999*d68f33bcSAndroid Build Coastguard Worker data = rh.git.get_file_content(commit, d.file) 1000*d68f33bcSAndroid Build Coastguard Worker result = _run(cmd, input=data) 1001*d68f33bcSAndroid Build Coastguard Worker # If the parsing failed, stdout will contain enough details on the 1002*d68f33bcSAndroid Build Coastguard Worker # location of the error. 1003*d68f33bcSAndroid Build Coastguard Worker if result.returncode: 1004*d68f33bcSAndroid Build Coastguard Worker ret.append(rh.results.HookResult( 1005*d68f33bcSAndroid Build Coastguard Worker 'rustfmt', project, commit, error=result.stdout, 1006*d68f33bcSAndroid Build Coastguard Worker files=(d.file,))) 1007*d68f33bcSAndroid Build Coastguard Worker continue 1008*d68f33bcSAndroid Build Coastguard Worker # TODO(b/164111102): rustfmt stable does not support --check on stdin. 1009*d68f33bcSAndroid Build Coastguard Worker # If no error is reported, compare stdin with stdout. 1010*d68f33bcSAndroid Build Coastguard Worker if data != result.stdout: 1011*d68f33bcSAndroid Build Coastguard Worker ret.append(rh.results.HookResult( 1012*d68f33bcSAndroid Build Coastguard Worker 'rustfmt', project, commit, error='Files not formatted', 1013*d68f33bcSAndroid Build Coastguard Worker files=(d.file,), fixup_cmd=cmd)) 1014*d68f33bcSAndroid Build Coastguard Worker return ret 1015*d68f33bcSAndroid Build Coastguard Worker 1016*d68f33bcSAndroid Build Coastguard Worker 1017*d68f33bcSAndroid Build Coastguard Workerdef check_xmllint(project, commit, _desc, diff, options=None): 1018*d68f33bcSAndroid Build Coastguard Worker """Run xmllint.""" 1019*d68f33bcSAndroid Build Coastguard Worker # XXX: Should we drop most of these and probe for <?xml> tags? 1020*d68f33bcSAndroid Build Coastguard Worker extensions = frozenset(( 1021*d68f33bcSAndroid Build Coastguard Worker 'dbus-xml', # Generated DBUS interface. 1022*d68f33bcSAndroid Build Coastguard Worker 'dia', # File format for Dia. 1023*d68f33bcSAndroid Build Coastguard Worker 'dtd', # Document Type Definition. 1024*d68f33bcSAndroid Build Coastguard Worker 'fml', # Fuzzy markup language. 1025*d68f33bcSAndroid Build Coastguard Worker 'form', # Forms created by IntelliJ GUI Designer. 1026*d68f33bcSAndroid Build Coastguard Worker 'fxml', # JavaFX user interfaces. 1027*d68f33bcSAndroid Build Coastguard Worker 'glade', # Glade user interface design. 1028*d68f33bcSAndroid Build Coastguard Worker 'grd', # GRIT translation files. 1029*d68f33bcSAndroid Build Coastguard Worker 'iml', # Android build modules? 1030*d68f33bcSAndroid Build Coastguard Worker 'kml', # Keyhole Markup Language. 1031*d68f33bcSAndroid Build Coastguard Worker 'mxml', # Macromedia user interface markup language. 1032*d68f33bcSAndroid Build Coastguard Worker 'nib', # OS X Cocoa Interface Builder. 1033*d68f33bcSAndroid Build Coastguard Worker 'plist', # Property list (for OS X). 1034*d68f33bcSAndroid Build Coastguard Worker 'pom', # Project Object Model (for Apache Maven). 1035*d68f33bcSAndroid Build Coastguard Worker 'rng', # RELAX NG schemas. 1036*d68f33bcSAndroid Build Coastguard Worker 'sgml', # Standard Generalized Markup Language. 1037*d68f33bcSAndroid Build Coastguard Worker 'svg', # Scalable Vector Graphics. 1038*d68f33bcSAndroid Build Coastguard Worker 'uml', # Unified Modeling Language. 1039*d68f33bcSAndroid Build Coastguard Worker 'vcproj', # Microsoft Visual Studio project. 1040*d68f33bcSAndroid Build Coastguard Worker 'vcxproj', # Microsoft Visual Studio project. 1041*d68f33bcSAndroid Build Coastguard Worker 'wxs', # WiX Transform File. 1042*d68f33bcSAndroid Build Coastguard Worker 'xhtml', # XML HTML. 1043*d68f33bcSAndroid Build Coastguard Worker 'xib', # OS X Cocoa Interface Builder. 1044*d68f33bcSAndroid Build Coastguard Worker 'xlb', # Android locale bundle. 1045*d68f33bcSAndroid Build Coastguard Worker 'xml', # Extensible Markup Language. 1046*d68f33bcSAndroid Build Coastguard Worker 'xsd', # XML Schema Definition. 1047*d68f33bcSAndroid Build Coastguard Worker 'xsl', # Extensible Stylesheet Language. 1048*d68f33bcSAndroid Build Coastguard Worker )) 1049*d68f33bcSAndroid Build Coastguard Worker 1050*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, [r'\.(' + '|'.join(extensions) + r')$']) 1051*d68f33bcSAndroid Build Coastguard Worker if not filtered: 1052*d68f33bcSAndroid Build Coastguard Worker return None 1053*d68f33bcSAndroid Build Coastguard Worker 1054*d68f33bcSAndroid Build Coastguard Worker # TODO: Figure out how to integrate schema validation. 1055*d68f33bcSAndroid Build Coastguard Worker # XXX: Should we use python's XML libs instead? 1056*d68f33bcSAndroid Build Coastguard Worker cmd = ['xmllint'] + options.args(('${PREUPLOAD_FILES}',), filtered) 1057*d68f33bcSAndroid Build Coastguard Worker 1058*d68f33bcSAndroid Build Coastguard Worker return _check_cmd('xmllint', project, commit, cmd) 1059*d68f33bcSAndroid Build Coastguard Worker 1060*d68f33bcSAndroid Build Coastguard Worker 1061*d68f33bcSAndroid Build Coastguard Workerdef check_android_test_mapping(project, commit, _desc, diff, options=None): 1062*d68f33bcSAndroid Build Coastguard Worker """Verify Android TEST_MAPPING files are valid.""" 1063*d68f33bcSAndroid Build Coastguard Worker if options.args(): 1064*d68f33bcSAndroid Build Coastguard Worker raise ValueError('Android TEST_MAPPING check takes no options') 1065*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, [r'(^|.*/)TEST_MAPPING$']) 1066*d68f33bcSAndroid Build Coastguard Worker if not filtered: 1067*d68f33bcSAndroid Build Coastguard Worker return None 1068*d68f33bcSAndroid Build Coastguard Worker 1069*d68f33bcSAndroid Build Coastguard Worker testmapping_format = options.tool_path('android-test-mapping-format') 1070*d68f33bcSAndroid Build Coastguard Worker testmapping_args = ['--commit', commit] 1071*d68f33bcSAndroid Build Coastguard Worker cmd = [testmapping_format] + options.args( 1072*d68f33bcSAndroid Build Coastguard Worker (project.dir, '${PREUPLOAD_FILES}'), filtered) + testmapping_args 1073*d68f33bcSAndroid Build Coastguard Worker return _check_cmd('android-test-mapping-format', project, commit, cmd) 1074*d68f33bcSAndroid Build Coastguard Worker 1075*d68f33bcSAndroid Build Coastguard Worker 1076*d68f33bcSAndroid Build Coastguard Workerdef check_aidl_format(project, commit, _desc, diff, options=None): 1077*d68f33bcSAndroid Build Coastguard Worker """Checks that AIDL files are formatted with aidl-format.""" 1078*d68f33bcSAndroid Build Coastguard Worker # All *.aidl files except for those under aidl_api directory. 1079*d68f33bcSAndroid Build Coastguard Worker filtered = _filter_diff(diff, [r'\.aidl$'], [r'(^|/)aidl_api/']) 1080*d68f33bcSAndroid Build Coastguard Worker if not filtered: 1081*d68f33bcSAndroid Build Coastguard Worker return None 1082*d68f33bcSAndroid Build Coastguard Worker aidl_format = options.tool_path('aidl-format') 1083*d68f33bcSAndroid Build Coastguard Worker clang_format = options.tool_path('clang-format') 1084*d68f33bcSAndroid Build Coastguard Worker diff_cmd = [aidl_format, '-d', '--clang-format-path', clang_format] + \ 1085*d68f33bcSAndroid Build Coastguard Worker options.args((), filtered) 1086*d68f33bcSAndroid Build Coastguard Worker ret = [] 1087*d68f33bcSAndroid Build Coastguard Worker for d in filtered: 1088*d68f33bcSAndroid Build Coastguard Worker data = rh.git.get_file_content(commit, d.file) 1089*d68f33bcSAndroid Build Coastguard Worker result = _run(diff_cmd, input=data) 1090*d68f33bcSAndroid Build Coastguard Worker if result.stdout: 1091*d68f33bcSAndroid Build Coastguard Worker fixup_cmd = [aidl_format, '-w', '--clang-format-path', clang_format] 1092*d68f33bcSAndroid Build Coastguard Worker ret.append(rh.results.HookResult( 1093*d68f33bcSAndroid Build Coastguard Worker 'aidl-format', project, commit, error=result.stdout, 1094*d68f33bcSAndroid Build Coastguard Worker files=(d.file,), fixup_cmd=fixup_cmd)) 1095*d68f33bcSAndroid Build Coastguard Worker return ret 1096*d68f33bcSAndroid Build Coastguard Worker 1097*d68f33bcSAndroid Build Coastguard Worker 1098*d68f33bcSAndroid Build Coastguard Worker# Hooks that projects can opt into. 1099*d68f33bcSAndroid Build Coastguard Worker# Note: Make sure to keep the top level README.md up to date when adding more! 1100*d68f33bcSAndroid Build Coastguard WorkerBUILTIN_HOOKS = { 1101*d68f33bcSAndroid Build Coastguard Worker 'aidl_format': check_aidl_format, 1102*d68f33bcSAndroid Build Coastguard Worker 'android_test_mapping_format': check_android_test_mapping, 1103*d68f33bcSAndroid Build Coastguard Worker 'aosp_license': check_aosp_license, 1104*d68f33bcSAndroid Build Coastguard Worker 'bpfmt': check_bpfmt, 1105*d68f33bcSAndroid Build Coastguard Worker 'checkpatch': check_checkpatch, 1106*d68f33bcSAndroid Build Coastguard Worker 'clang_format': check_clang_format, 1107*d68f33bcSAndroid Build Coastguard Worker 'commit_msg_bug_field': check_commit_msg_bug_field, 1108*d68f33bcSAndroid Build Coastguard Worker 'commit_msg_changeid_field': check_commit_msg_changeid_field, 1109*d68f33bcSAndroid Build Coastguard Worker 'commit_msg_prebuilt_apk_fields': check_commit_msg_prebuilt_apk_fields, 1110*d68f33bcSAndroid Build Coastguard Worker 'commit_msg_relnote_field_format': check_commit_msg_relnote_field_format, 1111*d68f33bcSAndroid Build Coastguard Worker 'commit_msg_relnote_for_current_txt': 1112*d68f33bcSAndroid Build Coastguard Worker check_commit_msg_relnote_for_current_txt, 1113*d68f33bcSAndroid Build Coastguard Worker 'commit_msg_test_field': check_commit_msg_test_field, 1114*d68f33bcSAndroid Build Coastguard Worker 'cpplint': check_cpplint, 1115*d68f33bcSAndroid Build Coastguard Worker 'gofmt': check_gofmt, 1116*d68f33bcSAndroid Build Coastguard Worker 'google_java_format': check_google_java_format, 1117*d68f33bcSAndroid Build Coastguard Worker 'jsonlint': check_json, 1118*d68f33bcSAndroid Build Coastguard Worker 'ktfmt': check_ktfmt, 1119*d68f33bcSAndroid Build Coastguard Worker 'pylint': check_pylint3, 1120*d68f33bcSAndroid Build Coastguard Worker 'pylint2': check_pylint2, 1121*d68f33bcSAndroid Build Coastguard Worker 'pylint3': check_pylint3, 1122*d68f33bcSAndroid Build Coastguard Worker 'rustfmt': check_rustfmt, 1123*d68f33bcSAndroid Build Coastguard Worker 'xmllint': check_xmllint, 1124*d68f33bcSAndroid Build Coastguard Worker} 1125*d68f33bcSAndroid Build Coastguard Worker 1126*d68f33bcSAndroid Build Coastguard Worker# Additional tools that the hooks can call with their default values. 1127*d68f33bcSAndroid Build Coastguard Worker# Note: Make sure to keep the top level README.md up to date when adding more! 1128*d68f33bcSAndroid Build Coastguard WorkerTOOL_PATHS = { 1129*d68f33bcSAndroid Build Coastguard Worker 'aidl-format': 'aidl-format', 1130*d68f33bcSAndroid Build Coastguard Worker 'android-test-mapping-format': 1131*d68f33bcSAndroid Build Coastguard Worker os.path.join(TOOLS_DIR, 'android_test_mapping_format.py'), 1132*d68f33bcSAndroid Build Coastguard Worker 'bpfmt': 'bpfmt', 1133*d68f33bcSAndroid Build Coastguard Worker 'clang-format': 'clang-format', 1134*d68f33bcSAndroid Build Coastguard Worker 'cpplint': os.path.join(TOOLS_DIR, 'cpplint.py'), 1135*d68f33bcSAndroid Build Coastguard Worker 'git-clang-format': 'git-clang-format', 1136*d68f33bcSAndroid Build Coastguard Worker 'gofmt': 'gofmt', 1137*d68f33bcSAndroid Build Coastguard Worker 'google-java-format': 'google-java-format', 1138*d68f33bcSAndroid Build Coastguard Worker 'google-java-format-diff': 'google-java-format-diff.py', 1139*d68f33bcSAndroid Build Coastguard Worker 'ktfmt': 'ktfmt', 1140*d68f33bcSAndroid Build Coastguard Worker 'pylint': 'pylint', 1141*d68f33bcSAndroid Build Coastguard Worker 'rustfmt': 'rustfmt', 1142*d68f33bcSAndroid Build Coastguard Worker} 1143