xref: /aosp_15_r20/system/chre/tools/todo_checker.py (revision 84e339476a462649f82315436d70fd732297a399)
1*84e33947SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*84e33947SAndroid Build Coastguard Worker
3*84e33947SAndroid Build Coastguard Worker#
4*84e33947SAndroid Build Coastguard Worker# Copyright 2021, The Android Open Source Project
5*84e33947SAndroid Build Coastguard Worker#
6*84e33947SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
7*84e33947SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
8*84e33947SAndroid Build Coastguard Worker# You may obtain a copy of the License at
9*84e33947SAndroid Build Coastguard Worker#
10*84e33947SAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
11*84e33947SAndroid Build Coastguard Worker#
12*84e33947SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
13*84e33947SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
14*84e33947SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15*84e33947SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
16*84e33947SAndroid Build Coastguard Worker# limitations under the License.
17*84e33947SAndroid Build Coastguard Worker#
18*84e33947SAndroid Build Coastguard Worker
19*84e33947SAndroid Build Coastguard Worker"""Repohook script to run checks on TODOs in CHRE.
20*84e33947SAndroid Build Coastguard Worker
21*84e33947SAndroid Build Coastguard WorkerThis script runs the following checks on TODOs in a commit:
22*84e33947SAndroid Build Coastguard Worker
23*84e33947SAndroid Build Coastguard Worker1: Prints a warning if a TODO references the bug ID in the commit message.
24*84e33947SAndroid Build Coastguard WorkerThis is mainly intended to minimize TODOs in the CHRE codebase, and to be
25*84e33947SAndroid Build Coastguard Workeran active reminder to remove a TODO once a commit addresses the debt
26*84e33947SAndroid Build Coastguard Workermentioned.
27*84e33947SAndroid Build Coastguard Worker
28*84e33947SAndroid Build Coastguard Worker2: Fails the repo upload if the current commit adds a TODO, but fails to
29*84e33947SAndroid Build Coastguard Workerassociate it with a bug-ID in the (usual) expected format of
30*84e33947SAndroid Build Coastguard Worker'TODO(b/13371337).
31*84e33947SAndroid Build Coastguard Worker
32*84e33947SAndroid Build Coastguard WorkerA bug ID field in the commit message is REQUIRED for this script to work.
33*84e33947SAndroid Build Coastguard WorkerThis can be ensured by adding a 'commit_msg_bug_field = true' hook to the
34*84e33947SAndroid Build Coastguard Workerproject's PREUPLOAD.cfg file. It is also recommended to add the
35*84e33947SAndroid Build Coastguard Worker'ignore_merged_commits' option to avoid unexpected script behavior.
36*84e33947SAndroid Build Coastguard Worker
37*84e33947SAndroid Build Coastguard WorkerThis script will work with any number of commits in the current repo
38*84e33947SAndroid Build Coastguard Workercheckout.
39*84e33947SAndroid Build Coastguard Worker"""
40*84e33947SAndroid Build Coastguard Worker
41*84e33947SAndroid Build Coastguard Workerimport os
42*84e33947SAndroid Build Coastguard Workerimport re
43*84e33947SAndroid Build Coastguard Workerimport subprocess
44*84e33947SAndroid Build Coastguard Workerimport sys
45*84e33947SAndroid Build Coastguard Worker
46*84e33947SAndroid Build Coastguard WorkerCOMMIT_HASH = os.environ['PREUPLOAD_COMMIT']
47*84e33947SAndroid Build Coastguard Worker
48*84e33947SAndroid Build Coastguard Worker# According to the repohooks documentation, only the warning and success IDs
49*84e33947SAndroid Build Coastguard Worker# are mentioned - we use a random non-zero value (that's high enough to
50*84e33947SAndroid Build Coastguard Worker# avoid confusion with errno values) as our error code.
51*84e33947SAndroid Build Coastguard WorkerREPO_ERROR_RETURN_CODE = 1337
52*84e33947SAndroid Build Coastguard WorkerREPO_WARNING_RETURN_CODE = 77
53*84e33947SAndroid Build Coastguard WorkerREPO_SUCCESS_RETURN_CODE = 0
54*84e33947SAndroid Build Coastguard Worker
55*84e33947SAndroid Build Coastguard Workerdef check_for_unassociated_todos() -> int:
56*84e33947SAndroid Build Coastguard Worker  """Check if a TODO has a bug ID associated with it.
57*84e33947SAndroid Build Coastguard Worker
58*84e33947SAndroid Build Coastguard Worker  Check if a TODO has a bug ID, in the usual 'TODO(b/13371337): {desc}'
59*84e33947SAndroid Build Coastguard Worker  format. Also prints the line where said TODO was found.
60*84e33947SAndroid Build Coastguard Worker
61*84e33947SAndroid Build Coastguard Worker  Returns:
62*84e33947SAndroid Build Coastguard Worker    An error code if a TODO has no bugs associated with it.
63*84e33947SAndroid Build Coastguard Worker  """
64*84e33947SAndroid Build Coastguard Worker  rc = REPO_SUCCESS_RETURN_CODE
65*84e33947SAndroid Build Coastguard Worker  commit_contents_cmd = 'git diff ' + COMMIT_HASH + '~ ' + COMMIT_HASH
66*84e33947SAndroid Build Coastguard Worker  diff_result_lines = subprocess.check_output(commit_contents_cmd,
67*84e33947SAndroid Build Coastguard Worker                                              shell=True,
68*84e33947SAndroid Build Coastguard Worker                                              encoding='UTF-8') \
69*84e33947SAndroid Build Coastguard Worker                                              .split('\n')
70*84e33947SAndroid Build Coastguard Worker  regex = r'TODO\(b\/([0-9]+)'
71*84e33947SAndroid Build Coastguard Worker
72*84e33947SAndroid Build Coastguard Worker  for line in diff_result_lines:
73*84e33947SAndroid Build Coastguard Worker    if line.startswith('+') and not line.startswith('+++') and \
74*84e33947SAndroid Build Coastguard Worker        'TODO' in line and not re.findall(regex, line):
75*84e33947SAndroid Build Coastguard Worker      print('Found a TODO in the following line in the commit without an \
76*84e33947SAndroid Build Coastguard Worker            associated bug-ID!')
77*84e33947SAndroid Build Coastguard Worker      print(line)
78*84e33947SAndroid Build Coastguard Worker      print('Please include a bug ID in the format TODO(b/13371337)')
79*84e33947SAndroid Build Coastguard Worker      rc = REPO_ERROR_RETURN_CODE
80*84e33947SAndroid Build Coastguard Worker
81*84e33947SAndroid Build Coastguard Worker  return rc
82*84e33947SAndroid Build Coastguard Worker
83*84e33947SAndroid Build Coastguard Workerdef grep_for_todos(bug_id : str) -> int:
84*84e33947SAndroid Build Coastguard Worker  """Searches for TODOs associated with the BUG ID referenced in the commit.
85*84e33947SAndroid Build Coastguard Worker
86*84e33947SAndroid Build Coastguard Worker  Args:
87*84e33947SAndroid Build Coastguard Worker    bug_id: Bug ID referenced in the commit.
88*84e33947SAndroid Build Coastguard Worker
89*84e33947SAndroid Build Coastguard Worker  Returns:
90*84e33947SAndroid Build Coastguard Worker    A warning code if current bug ID references any TODOs.
91*84e33947SAndroid Build Coastguard Worker  """
92*84e33947SAndroid Build Coastguard Worker  grep_result = None
93*84e33947SAndroid Build Coastguard Worker  rc = REPO_SUCCESS_RETURN_CODE
94*84e33947SAndroid Build Coastguard Worker  git_repo_path_cmd = 'git rev-parse --show-toplevel'
95*84e33947SAndroid Build Coastguard Worker  repo_path = ' ' + subprocess.check_output(git_repo_path_cmd, shell=True,
96*84e33947SAndroid Build Coastguard Worker                                            encoding='UTF-8')
97*84e33947SAndroid Build Coastguard Worker
98*84e33947SAndroid Build Coastguard Worker  grep_base_cmd = 'grep -nri '
99*84e33947SAndroid Build Coastguard Worker  grep_file_filters = '--include \*.h --include \*.cc --include \*.cpp --include \*.c '
100*84e33947SAndroid Build Coastguard Worker  grep_shell_cmd = grep_base_cmd + grep_file_filters + bug_id + repo_path
101*84e33947SAndroid Build Coastguard Worker  try:
102*84e33947SAndroid Build Coastguard Worker    grep_result = subprocess.check_output(grep_shell_cmd, shell=True,
103*84e33947SAndroid Build Coastguard Worker                                          encoding='UTF-8')
104*84e33947SAndroid Build Coastguard Worker  except subprocess.CalledProcessError as e:
105*84e33947SAndroid Build Coastguard Worker    if e.returncode != 1:
106*84e33947SAndroid Build Coastguard Worker      # A return code of 1 means that grep returned a 'NOT_FOUND', which is
107*84e33947SAndroid Build Coastguard Worker      # our ideal scenario! A return code of > 1 means something went very
108*84e33947SAndroid Build Coastguard Worker      # wrong with grep. We still return a success here, since there's
109*84e33947SAndroid Build Coastguard Worker      # nothing much else we can do (and this tool is intended to be mostly
110*84e33947SAndroid Build Coastguard Worker      # informational).
111*84e33947SAndroid Build Coastguard Worker      print('ERROR: grep failed with err code {}'.format(e.returncode),
112*84e33947SAndroid Build Coastguard Worker            file=sys.stderr)
113*84e33947SAndroid Build Coastguard Worker      print('The grep command that was run was:\n{}'.format(grep_shell_cmd),
114*84e33947SAndroid Build Coastguard Worker            file=sys.stderr)
115*84e33947SAndroid Build Coastguard Worker
116*84e33947SAndroid Build Coastguard Worker  if grep_result is not None:
117*84e33947SAndroid Build Coastguard Worker    print('Matching TODOs found for the Bug-ID in the commit message..')
118*84e33947SAndroid Build Coastguard Worker    print('Hash of the current commit being checked: {}'
119*84e33947SAndroid Build Coastguard Worker          .format(COMMIT_HASH))
120*84e33947SAndroid Build Coastguard Worker    grep_result = grep_result.replace(repo_path + '/', '')
121*84e33947SAndroid Build Coastguard Worker    print(grep_result)
122*84e33947SAndroid Build Coastguard Worker    rc = REPO_WARNING_RETURN_CODE
123*84e33947SAndroid Build Coastguard Worker
124*84e33947SAndroid Build Coastguard Worker  return rc
125*84e33947SAndroid Build Coastguard Worker
126*84e33947SAndroid Build Coastguard Workerdef get_bug_id_for_current_commit() -> str:
127*84e33947SAndroid Build Coastguard Worker  """Get the Bug ID for the current commit
128*84e33947SAndroid Build Coastguard Worker
129*84e33947SAndroid Build Coastguard Worker  Returns:
130*84e33947SAndroid Build Coastguard Worker    The bug ID for the current commit.
131*84e33947SAndroid Build Coastguard Worker  """
132*84e33947SAndroid Build Coastguard Worker  git_current_commit_msg_cmd = 'git log --format=%B -n 1 '
133*84e33947SAndroid Build Coastguard Worker  commit_msg_lines_cmd = git_current_commit_msg_cmd + COMMIT_HASH
134*84e33947SAndroid Build Coastguard Worker  commit_msg_lines_list = subprocess.check_output(commit_msg_lines_cmd,
135*84e33947SAndroid Build Coastguard Worker                                                  shell=True,
136*84e33947SAndroid Build Coastguard Worker                                                  encoding='UTF-8') \
137*84e33947SAndroid Build Coastguard Worker                                                  .split('\n')
138*84e33947SAndroid Build Coastguard Worker  try:
139*84e33947SAndroid Build Coastguard Worker    bug_id_line = \
140*84e33947SAndroid Build Coastguard Worker      [line for line in commit_msg_lines_list if \
141*84e33947SAndroid Build Coastguard Worker        any(word in line.lower() for word in ['bug:', 'fixes:'])][0]
142*84e33947SAndroid Build Coastguard Worker  except IndexError:
143*84e33947SAndroid Build Coastguard Worker    print('Please include a Bug or Fixes field in the commit message')
144*84e33947SAndroid Build Coastguard Worker    sys.exit(-1);
145*84e33947SAndroid Build Coastguard Worker  return bug_id_line.split(':')[1].strip()
146*84e33947SAndroid Build Coastguard Worker
147*84e33947SAndroid Build Coastguard Workerdef is_file_in_diff(filename : str) -> bool:
148*84e33947SAndroid Build Coastguard Worker  """Check if a given filename is part of the commit.
149*84e33947SAndroid Build Coastguard Worker
150*84e33947SAndroid Build Coastguard Worker  Args:
151*84e33947SAndroid Build Coastguard Worker    filename: filename to check in the git diff.
152*84e33947SAndroid Build Coastguard Worker
153*84e33947SAndroid Build Coastguard Worker  Returns:
154*84e33947SAndroid Build Coastguard Worker    True if the file is part of the commit.
155*84e33947SAndroid Build Coastguard Worker  """
156*84e33947SAndroid Build Coastguard Worker  commit_contents_cmd = 'git diff ' + COMMIT_HASH + '~ ' + COMMIT_HASH
157*84e33947SAndroid Build Coastguard Worker  diff_result = subprocess.check_output(commit_contents_cmd, shell=True,
158*84e33947SAndroid Build Coastguard Worker                                        encoding='UTF-8')
159*84e33947SAndroid Build Coastguard Worker  return filename in diff_result
160*84e33947SAndroid Build Coastguard Worker
161*84e33947SAndroid Build Coastguard Workerdef main():
162*84e33947SAndroid Build Coastguard Worker  # This script has a bunch of TODOs peppered around, though not with the
163*84e33947SAndroid Build Coastguard Worker  # same intention as the checks that are being performed. Skip the checks
164*84e33947SAndroid Build Coastguard Worker  # if we're committing changes to this script! One caveat is that we
165*84e33947SAndroid Build Coastguard Worker  # should avoid pushing in changes to other code if we're committing
166*84e33947SAndroid Build Coastguard Worker  # changes to this script.
167*84e33947SAndroid Build Coastguard Worker  rc = REPO_SUCCESS_RETURN_CODE
168*84e33947SAndroid Build Coastguard Worker  if not is_file_in_diff(os.path.basename(__file__)):
169*84e33947SAndroid Build Coastguard Worker    bug_id = get_bug_id_for_current_commit()
170*84e33947SAndroid Build Coastguard Worker    grep_rc = grep_for_todos(bug_id)
171*84e33947SAndroid Build Coastguard Worker    check_rc = check_for_unassociated_todos()
172*84e33947SAndroid Build Coastguard Worker    rc = max(grep_rc, check_rc)
173*84e33947SAndroid Build Coastguard Worker  sys.exit(rc)
174*84e33947SAndroid Build Coastguard Worker
175*84e33947SAndroid Build Coastguard Workerif __name__ == '__main__':
176*84e33947SAndroid Build Coastguard Worker  main()
177