222 lines
6.5 KiB
Python
222 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
# ruff: noqa: E402, F401
|
|
|
|
import argparse
|
|
import csv
|
|
import os
|
|
import pickle
|
|
import sys
|
|
from collections.abc import Callable
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
|
|
sys.path.append(str(REPO_ROOT / "ci" / "scripts" / "jenkins"))
|
|
|
|
from cmd_utils import tags_from_title
|
|
from git_utils import GitHubRepo, git
|
|
|
|
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
|
|
|
|
|
|
PRS_QUERY = """
|
|
query ($owner: String!, $name: String!, $after: String, $pageSize: Int!) {
|
|
repository(owner: $owner, name: $name) {
|
|
defaultBranchRef {
|
|
name
|
|
target {
|
|
... on Commit {
|
|
oid
|
|
history(after: $after, first: $pageSize) {
|
|
pageInfo {
|
|
hasNextPage
|
|
endCursor
|
|
}
|
|
nodes {
|
|
oid
|
|
committedDate
|
|
associatedPullRequests(first: 1) {
|
|
nodes {
|
|
number
|
|
additions
|
|
changedFiles
|
|
deletions
|
|
author {
|
|
login
|
|
}
|
|
title
|
|
body
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
|
|
def append_and_save(items, file):
|
|
if not file.exists():
|
|
data = []
|
|
else:
|
|
with open(file, "rb") as f:
|
|
data = pickle.load(f)
|
|
|
|
data += items
|
|
with open(file, "wb") as f:
|
|
pickle.dump(data, f)
|
|
|
|
|
|
def fetch_pr_data(args, cache):
|
|
github = GitHubRepo(user=user, repo=repo, token=GITHUB_TOKEN)
|
|
|
|
if args.from_commit is None or args.to_commit is None:
|
|
print("--from-commit and --to-commit must be specified if --skip-query is not used")
|
|
exit(1)
|
|
|
|
i = 0
|
|
page_size = 80
|
|
cursor = f"{args.from_commit} {i}"
|
|
|
|
while True:
|
|
try:
|
|
r = github.graphql(
|
|
query=PRS_QUERY,
|
|
variables={
|
|
"owner": user,
|
|
"name": repo,
|
|
"after": cursor,
|
|
"pageSize": page_size,
|
|
},
|
|
)
|
|
except RuntimeError as e:
|
|
print(f"{e}\nPlease check enviroment variable GITHUB_TOKEN whether is valid.")
|
|
exit(1)
|
|
data = r["data"]["repository"]["defaultBranchRef"]["target"]["history"]
|
|
if not data["pageInfo"]["hasNextPage"]:
|
|
break
|
|
cursor = data["pageInfo"]["endCursor"]
|
|
results = data["nodes"]
|
|
|
|
to_add = []
|
|
stop = False
|
|
for r in results:
|
|
if r["oid"] == args.to_commit:
|
|
print(f"Found {r['oid']}, stopping")
|
|
stop = True
|
|
break
|
|
else:
|
|
to_add.append(r)
|
|
|
|
oids = [r["oid"] for r in to_add]
|
|
print(oids)
|
|
append_and_save(to_add, cache)
|
|
if stop:
|
|
break
|
|
print(i)
|
|
i += page_size
|
|
|
|
|
|
def write_csv(
|
|
filename: str, data: list[dict[str, Any]], threshold_filter: Callable[[dict[str, Any]], bool]
|
|
) -> None:
|
|
with open(filename, "w", newline="") as csvfile:
|
|
writer = csv.writer(csvfile, quotechar='"')
|
|
writer.writerow(
|
|
(
|
|
"category",
|
|
"subject",
|
|
"date",
|
|
"url",
|
|
"author",
|
|
"pr_title_tags",
|
|
"pr_title",
|
|
"additions",
|
|
"deletions",
|
|
"additions+deletions>threshold",
|
|
"changed_files",
|
|
)
|
|
)
|
|
for item in data:
|
|
nodes = item["associatedPullRequests"]["nodes"]
|
|
if len(nodes) == 0:
|
|
continue
|
|
pr = nodes[0]
|
|
tags = tags_from_title(pr["title"])
|
|
actual_tags = []
|
|
for t in tags:
|
|
items = [x.strip() for x in t.split(",")]
|
|
actual_tags += items
|
|
tags = actual_tags
|
|
tags = [t.lower() for t in tags]
|
|
category = tags[0] if len(tags) > 0 else ""
|
|
author = pr["author"] if pr["author"] else "ghost"
|
|
author = author.get("login", "") if isinstance(author, dict) else author
|
|
writer.writerow(
|
|
(
|
|
category,
|
|
"n/a",
|
|
item["committedDate"],
|
|
f"https://github.com/apache/tvm/pull/{pr['number']}",
|
|
author,
|
|
"/".join(tags),
|
|
pr["title"].replace(",", " "),
|
|
pr["additions"],
|
|
pr["deletions"],
|
|
1 if threshold_filter(pr) else 0,
|
|
pr["changedFiles"],
|
|
)
|
|
)
|
|
print(f"{filename} generated!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
help = "List out commits with attached PRs since a certain commit"
|
|
parser = argparse.ArgumentParser(description=help)
|
|
parser.add_argument("--from-commit", help="commit to start checking PRs from")
|
|
parser.add_argument("--to-commit", help="commit to stop checking PRs from")
|
|
parser.add_argument(
|
|
"--threshold", default=0, help="sum of additions + deletions to consider large, such as 150"
|
|
)
|
|
parser.add_argument(
|
|
"--skip-query", action="store_true", help="don't query GitHub and instead use cache file"
|
|
)
|
|
args = parser.parse_args()
|
|
user = "apache"
|
|
repo = "tvm"
|
|
threshold = int(args.threshold)
|
|
|
|
cache = Path("out.pkl")
|
|
if not args.skip_query:
|
|
fetch_pr_data(args, cache)
|
|
|
|
with open(cache, "rb") as f:
|
|
data = pickle.load(f)
|
|
|
|
print(f"Found {len(data)} PRs")
|
|
|
|
write_csv(
|
|
filename="out_pr_gathered.csv",
|
|
data=data,
|
|
threshold_filter=lambda pr: pr["additions"] + pr["deletions"] > threshold,
|
|
)
|