| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| """ |
| Uses GitHub API to grab patch data for a PR and calculate changed lines |
| """ |
|
|
| import argparse |
| import json |
| import os |
| import re |
| import requests |
|
|
|
|
| class MissingPatchData(Exception): |
| """Raised when the patch data is missing""" |
|
|
|
|
| def fetch_patch(args): |
| """Grabs the patch data from the GitHub API.""" |
| git_session = requests.Session() |
| headers = { |
| "Accept": "application/vnd.github+json", |
| "X-GitHub-Api-Version": "2022-11-28", |
| } |
| if args.token: |
| headers["Authorization"] = f"Bearer {args.token}" |
|
|
| git_request = git_session.get( |
| f"{args.api_url}/repos/{args.repo}/pulls/{args.pr}/files", headers=headers |
| ) |
| return git_request.json() |
|
|
|
|
| def parse_patch_file(entry): |
| """Parses the individual file changes within a patch""" |
| line_array = [] |
| sublist = [] |
|
|
| patch_array = re.split("\n", entry["patch"]) |
| |
| patch_array = [i for i in patch_array if i] |
|
|
| for item in patch_array: |
| |
| if item.startswith("@@ -"): |
| if sublist: |
| line_array.append(sublist) |
| sublist = [re.sub(r"\s@@(.*)", "", item.split("+")[1])] |
| |
| elif not item.startswith("-") and not item == "\\ No newline at end of file": |
| sublist.append(item) |
| if sublist: |
| line_array.append(sublist) |
| return line_array |
|
|
|
|
| def parse_patch_data(patch_data): |
| """Takes the patch data and returns a dictionary of files and the lines""" |
|
|
| final_dict = {} |
| for entry in patch_data: |
| |
| if entry["status"] == "removed": |
| continue |
|
|
| |
| |
| |
| if entry["additions"] != 0 and "patch" in entry: |
| line_array = parse_patch_file(entry) |
| final_dict[entry["filename"]] = line_array |
| return final_dict |
|
|
|
|
| def get_lines(line_dict): |
| """Takes the dictionary of files and lines and returns a dictionary of files and line numbers""" |
| final_dict = {} |
| for file_name, sublist in line_dict.items(): |
| line_array = [] |
| for array in sublist: |
| line_number = 0 |
| if "," not in array[0]: |
| line_number = int(array[0]) - 1 |
| else: |
| line_number = int(array[0].split(",")[0]) - 1 |
|
|
| start = -1 |
| end = -1 |
| for line in array: |
| if line.startswith("+"): |
| if start < 0: |
| start = line_number |
| end = line_number |
| |
| line_number += 1 |
| line_array.append([start, end]) |
|
|
| |
| if line_array: |
| final_dict[file_name] = line_array |
| return final_dict |
|
|
|
|
| def main(): |
| """main()""" |
| parser = argparse.ArgumentParser( |
| prog="changed_lines.py", |
| description="Identifies the changed files and lines in a GitHub PR.", |
| ) |
| parser.add_argument("--token") |
| parser.add_argument("--api-url", default="https://api.github.com") |
| parser.add_argument("--repo", default="FreeCAD/FreeCAD") |
| parser.add_argument("--ref", required=True) |
| parser.add_argument("--pr", required=True) |
| parser.add_argument("--file-filter", default="") |
| args = parser.parse_args() |
|
|
| data = fetch_patch(args) |
| added_line_data = parse_patch_data(data) |
| added_lines = get_lines(added_line_data) |
|
|
| if args.file_filter: |
| args.file_filter = set(args.file_filter.replace(" ", "").split(",")) |
|
|
| filename_list = [] |
| line_filter = [] |
| for filename, _ in added_lines.items(): |
| if (not args.file_filter) or ( |
| os.path.splitext(filename)[1] in args.file_filter |
| ): |
| filename_list.append(filename) |
| lines_modified = {} |
| lines_modified["name"] = filename |
| lines_modified["lines"] = added_lines[filename] |
| line_filter.append(lines_modified) |
|
|
| print(f"{json.dumps(line_filter)}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|