#!/usr/bin/env python3 # Future improvements: # - link bugs and code references # - handle multiple bug references per line (currently only fetches one) # - fetch more than the first ~900 entries from Buganizer # - check if CLI tool is available + authenticated import pathlib, re, subprocess outfile = "closed_bugs.csv" print("Searching code for bug references") androidx_root = pathlib.Path(__file__).parent.parent.resolve() grep_cmd = subprocess.run( ["egrep", # -I excludes binary files # -i case insensitive search # -n include line numbers # -r recursive "-Iinr", # regex for buganizer format ("b/123456789") "b\/[[:digit:]]{8,9}", # Files and directories to include and exclude "--exclude-dir=.idea", "--include=*.gradle", "--include=*.java", "--include=*.kt", "--include=*.xml", # Search all of the AndroidX repo checkout f"{androidx_root}" ], capture_output=True, text=True ) raw_output_lines = grep_cmd.stdout.split("\n") print("Cleaning up search results") bug_dict = {} # mapping of bug id to list of filename + line number for line in raw_output_lines: regex_result = re.search('b\/[0-9]{8,9}', line) if regex_result is not None: bug_id = regex_result.group(0).removeprefix("b/") file = line.split(":")[0].removeprefix(str(androidx_root)) linenum = line.split(":")[1] if bug_id in bug_dict: matching_files = bug_dict[bug_id] else: matching_files = set() matching_files.add(f"{file}:{linenum}") bug_dict[bug_id] = matching_files print(f"Found {len(bug_dict)} bugs") # Create bug id query string. # The CLI tool fails if there are too many bugs (>900?); only use the first 900. bug_ids = list(bug_dict.keys()) bug_ids.sort() joined_ids = "|".join(bug_ids[0:899]) # Query buganizer to determine which of the given bugs are closed. # Store the issue, reporter, and assignee of the matching [closed] bugs. print("Querying Buganizer to find how many of these bugs are resolved") bugged_cmd = subprocess.run( ["bugged", "search", f"id:({joined_ids})", "status:closed", "--columns=issue,reporter,assignee"], capture_output=True, text=True # capture output as String instead of byte sequence ) closed_bug_list = bugged_cmd.stdout.split("\n") # Remove header and trailing rows of Buganizer query result closed_bug_list.pop(0) closed_bug_list.pop() print(f"{len(closed_bug_list)} have been resolved") # Combine buganizer results with file search results and write to CSV csv_str = "bug_id,reporter,assignee,files\n" for line in closed_bug_list: elements = re.split(" +", line) bug_id = elements[0] reporter = elements[1] assignee = elements[2] matching_files = bug_dict[bug_id] line_str = f"b/{bug_id},{reporter},{assignee}," # The list of matching file(s) are enclosed in double quotes to preserve \n in the csv line_str += ("\"" + "\n".join(matching_files) + "\"") csv_str += line_str + "\n" print(csv_str, file=open(outfile, 'w')) print(f"Wrote results to {outfile}")