132 lines
3.5 KiB
Python
Executable File
132 lines
3.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""Generate a list of GitHub issues that need attention."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import multiprocessing
|
|
import sys
|
|
from typing import Any
|
|
|
|
import requests
|
|
from tqdm import tqdm
|
|
|
|
OWNER = "rerun-io"
|
|
REPO = "rerun"
|
|
OFFICIAL_RERUN_DEVS = [
|
|
"abey79",
|
|
"emilk",
|
|
"jleibs",
|
|
"jondo2010",
|
|
"jprochazk",
|
|
"karimo87",
|
|
"martenbjork",
|
|
"nikolausWest",
|
|
"roym899",
|
|
"teh-cmc",
|
|
"Wumpf",
|
|
"zehiko",
|
|
]
|
|
|
|
|
|
def get_github_token() -> str:
|
|
import os
|
|
|
|
token = os.environ.get("GH_ACCESS_TOKEN", "")
|
|
if token != "":
|
|
return token
|
|
|
|
home_dir = os.path.expanduser("~")
|
|
token_file = os.path.join(home_dir, ".githubtoken")
|
|
|
|
try:
|
|
with open(token_file, encoding="utf8") as f:
|
|
token = f.read().strip()
|
|
return token
|
|
except Exception:
|
|
pass
|
|
|
|
print("ERROR: expected a GitHub token in the environment variable GH_ACCESS_TOKEN or in ~/.githubtoken")
|
|
sys.exit(1)
|
|
|
|
|
|
def fetch_issue(issue_json: dict[str, Any]) -> dict[str, Any]:
|
|
url = issue_json["url"]
|
|
gh_access_token = get_github_token()
|
|
headers = {"Authorization": f"Token {gh_access_token}"}
|
|
response = requests.get(url, headers=headers)
|
|
json: dict[str, Any] = response.json()
|
|
if response.status_code != 200:
|
|
print(f"ERROR {url}: {response.status_code} - {json['message']}")
|
|
sys.exit(1)
|
|
return json
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description="Generate a list of GitHub issues that need attention.")
|
|
parser.add_argument("--list-external", action="store_true", help="List all external issues")
|
|
args = parser.parse_args()
|
|
|
|
access_token = get_github_token()
|
|
|
|
headers = {"Authorization": f"Bearer {access_token}"}
|
|
|
|
all_issues = []
|
|
|
|
urls = [f"https://api.github.com/repos/{OWNER}/{REPO}/issues"]
|
|
|
|
while urls:
|
|
url = urls.pop()
|
|
|
|
print(f"Fetching {url}…")
|
|
response = requests.get(url, headers=headers)
|
|
json = response.json()
|
|
if response.status_code != 200:
|
|
print(f"ERROR {url}: {response.status_code} - {json['message']}")
|
|
sys.exit(1)
|
|
|
|
all_issues += list(json)
|
|
|
|
# Check if there is a next page:
|
|
if "Link" in response.headers:
|
|
links = response.headers["Link"].split(", ")
|
|
for link in links:
|
|
if 'rel="next"' in link:
|
|
next_url = link.split(";")[0][1:-1]
|
|
urls += [next_url]
|
|
|
|
pool = multiprocessing.Pool()
|
|
issues_list = list(
|
|
tqdm(
|
|
pool.imap(fetch_issue, all_issues),
|
|
total=len(all_issues),
|
|
desc="Fetching issue details",
|
|
),
|
|
)
|
|
|
|
issues_list.sort(key=lambda issue: issue["number"])
|
|
|
|
# Print the response content
|
|
for issue in issues_list:
|
|
author = issue["user"]["login"]
|
|
html_url = issue["html_url"]
|
|
comments = issue["comments"]
|
|
state = issue["state"]
|
|
labels = [label["name"] for label in issue["labels"]]
|
|
|
|
if args.list_external and state == "open" and author not in OFFICIAL_RERUN_DEVS:
|
|
print(f"{html_url} by {author}")
|
|
continue
|
|
|
|
if "👀 needs triage" in labels:
|
|
print(f"{html_url} by {author} needs triage")
|
|
elif len(labels) == 0:
|
|
print(f"{html_url} by {author} has no labels")
|
|
elif comments == 0 and state == "open" and author not in OFFICIAL_RERUN_DEVS:
|
|
print(f"{html_url} by {author} has {comments} comments")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|