| import sys |
| import urllib.request |
| import urllib.parse |
| import json |
| import subprocess |
| import os.path |
| |
| |
| from typing import Set |
| |
| OWNER_REPO = "" |
| ACCESS_TOKEN = "" |
| USER_AGENT = "" |
| |
| |
| def get(entity: str, query: dict = None) -> dict: |
| if query is None: |
| query = {} |
| path = entity |
| if query: |
| querystring = urllib.parse.urlencode(query) |
| path += f"?{querystring}" |
| print(f"Getting {path}") |
| req = urllib.request.Request( |
| f"https://api.github.com/{path}", |
| None, |
| { |
| "User-Agent": USER_AGENT, |
| "Authorization": f"token {ACCESS_TOKEN}", |
| "Accept": "application/vnd.github.v3+json", |
| }, |
| ) |
| result = urllib.request.urlopen(req) |
| # It's ok not to check for error codes here. We'll throw either way |
| return json.loads(result.read()) |
| |
| |
| def paginated_get(entity: str, query: dict = None) -> [dict]: |
| if query is None: |
| query = {} |
| result = [] |
| results_per_page = 50 |
| query["page"] = 1 |
| query["per_page"] = results_per_page |
| while True: |
| current_page_results = get(entity, query) |
| result.extend(current_page_results) |
| if len(current_page_results) == results_per_page: |
| query["page"] += 1 |
| else: |
| break |
| return result |
| |
| |
| def list_open_prs(stale_label: str = None) -> [dict]: |
| prs = paginated_get(f"repos/{OWNER_REPO}/pulls", {"state": "open"}) |
| if stale_label is not None: |
| return [pr for pr in prs if not any(label["name"] == stale_label for label in pr["labels"])] |
| return prs |
| |
| |
| def list_pr_files(pr: dict) -> [dict]: |
| return paginated_get(f'repos/{OWNER_REPO}/pulls/{pr["number"]}/files') |
| |
| |
| def list_modified_paths_in_pr(pr: dict) -> Set[str]: |
| pr_paths = list_pr_files(pr) |
| filtered = {x["filename"] for x in pr_paths if x["status"] == "modified"} |
| return filtered |
| |
| |
| def list_files_under_vc() -> Set[str]: |
| output = subprocess.check_output( |
| ["git", "ls-tree", "-r", "main", "--name-only"] |
| ).decode("utf-8") |
| paths = {x for x in output.splitlines()} |
| return paths |
| |
| |
| def make_file_formateable(path: str): |
| try: |
| with open(path, "r+") as f: |
| content = ["/**\n", " * @prettier\n", " */\n"] |
| file_contents = f.readlines() |
| if file_contents[0:3] != content: |
| content.extend(file_contents) |
| f.seek(0) |
| f.writelines(content) |
| f.truncate() |
| except: |
| print(f"Error making {path} formatable") |
| |
| |
| def main(): |
| # In case you want to save the result to avoid extra API calls |
| use_file = False |
| if not use_file or not os.path.isfile("./paths.json"): |
| current_prs = list_open_prs(stale_label="likely-stale") |
| modified_paths = set() |
| for PR in current_prs: |
| modified_paths.update(list_modified_paths_in_pr(PR)) |
| current_git_files = list_files_under_vc() |
| untouched_paths = list(current_git_files - modified_paths) |
| if use_file: |
| with open("./paths.json", "w") as f: |
| json.dump(untouched_paths, f) |
| else: |
| with open("./paths.json", "r") as f: |
| untouched_paths = json.load(f) |
| js_files = {x for x in untouched_paths if x.endswith(".js")} |
| for path in js_files: |
| make_file_formateable(path) |
| |
| |
| if __name__ == "__main__": |
| if len(sys.argv) != 4: |
| print(f"Usage: {os.path.basename(__file__)} OWNER/REPO ACCESS_TOKEN USER_AGENT") |
| else: |
| OWNER_REPO = sys.argv[1] |
| ACCESS_TOKEN = sys.argv[2] |
| USER_AGENT = sys.argv[3] |
| main() |