xref: /aosp_15_r20/tools/repohooks/rh/hooks.py (revision d68f33bc6fb0cc2476107c2af0573a2f5a63dfc1)
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