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