Anand Krishna, Christian Bourjau, Chun-Wei Chen, G. Ramalingam, Gary Miguel, Gaétan Lepage, Ilya Lavrenov, Ivan Novikov, Jack·Boos·Yu, Jakub Bachurski, Jicheng Tang, Joaquin Anton, Justin Chu, Nat Kershaw (MSFT), Philip Lassen, Prasanth Pulavarthi, Przemyslaw Wysocki, SpaceIm, Stephen Neuendorffer, Takeshi Watanabe, Teodora Sechkova, Thiago Crepaldi, Xavier Dupré, Yi Zhang, Yuan Yao, andife, daquexian, kylesayrs, liqun Fu, longlee0622, sqhao, williamberman, yanbc
-
-
Save abock/30fc4e8b1fd1e879e113597ff536aa35 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# SPDX-License-Identifier: Apache-2.0 | |
import argparse | |
import subprocess | |
from datetime import datetime | |
from os import getenv | |
from typing import Dict, List, Optional | |
import dateutil.parser | |
import requests | |
github_endpoint = "https://api.github.com/graphql" | |
github_token = getenv("GITHUB_TOKEN") | |
class Contributor: | |
def __init__( | |
self, name: str, github_alias: Optional[str], github_url: Optional[str] | |
): | |
self.name = name | |
self.github_alias = github_alias | |
self.github_url = github_url | |
def __eq__(self, __o: object) -> bool: | |
return ( | |
isinstance(__o, Contributor) | |
and __o.name == self.name | |
and __o.github_alias == self.github_alias | |
and __o.github_url == self.github_url | |
) | |
def __hash__(self) -> int: | |
return hash((self.name, self.github_alias, self.github_url)) | |
class Contribution: | |
def __init__( | |
self, date: datetime, commit: str, message: str, additions: int, deletions: int | |
): | |
self.date = date | |
self.commit = commit | |
self.message = message | |
self.additions = additions | |
self.deletions = deletions | |
Contributions = Dict[Contributor, List[Contribution]] | |
def query_contributions( | |
owner_name: str, | |
repo_name: str, | |
branch: str, | |
since: datetime, | |
cursor: Optional[str] = None, | |
): | |
all_contributions: Contributions = {} | |
while True: | |
response = requests.post( | |
url=github_endpoint, | |
headers={"Authorization": f"bearer {github_token}"}, | |
json={ | |
"query": """ | |
query query_contributions( | |
$owner_name:String! | |
$repo_name:String! | |
$branch:String!, | |
$since:GitTimestamp!, | |
$cursor:String) { | |
repository(owner:$owner_name name:$repo_name) { | |
ref(qualifiedName: $branch) { | |
target { | |
...on Commit { | |
history(since:$since after:$cursor) { | |
pageInfo { | |
endCursor | |
} | |
nodes { | |
oid | |
message | |
additions | |
deletions | |
author { | |
user { | |
login | |
url | |
} | |
name | |
date | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
""", | |
"variables": { | |
"owner_name": owner_name, | |
"repo_name": repo_name, | |
"branch": branch, | |
"since": since.isoformat(), | |
"cursor": cursor, | |
}, | |
}, | |
) | |
response.raise_for_status() | |
history = response.json()["data"]["repository"]["ref"]["target"]["history"] | |
for commit_node in history["nodes"]: | |
author = commit_node["author"] | |
user = author["user"] | |
contributor = Contributor( | |
author["name"], | |
user.get("login") if user else None, | |
user.get("url") if user else None, | |
) | |
all_contributions.setdefault(contributor, []).append( | |
Contribution( | |
dateutil.parser.isoparse(author["date"]), | |
commit_node["oid"], | |
commit_node["message"], | |
commit_node["additions"], | |
commit_node["deletions"], | |
) | |
) | |
cursor = history["pageInfo"]["endCursor"] | |
if not cursor: | |
return all_contributions | |
def get_markdown_list(contributions: Contributions): | |
for contributor in sorted(contributions.keys(), key=lambda c: c.name): | |
if contributor.github_alias: | |
yield f'[{contributor.name}]({contributor.github_url} "{contributor.github_alias}")' | |
else: | |
yield contributor.name | |
def git_command(*args: str) -> str: | |
return ( | |
subprocess.run(["git"] + list(args), capture_output=True) | |
.stdout.decode("utf-8") | |
.strip() | |
) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description="Generate Contributor List") | |
parser.add_argument( | |
"--github-repo-owner", type=str, help="GitHub repository owner", required=True | |
) | |
parser.add_argument( | |
"--github-repo-name", type=str, help="GitHub repository name", required=True | |
) | |
parser.add_argument( | |
"--git-branch", type=str, help="Git branch to query", required=True | |
) | |
parser.add_argument( | |
"--since-latest-tag", | |
action="store_true", | |
help="Query all contributors since the latest tag", | |
) | |
parser.add_argument( | |
"--since-ref", type=str, help="Query all contributors since a ref/tag" | |
) | |
parser.add_argument( | |
"--since-date", type=str, help="Query all contributors since a date" | |
) | |
args = parser.parse_args() | |
if not args.since_date and not args.since_ref: | |
args.since_latest_tag = True | |
if args.since_date: | |
args.since_date = dateutil.parser.parse(args.since_date) | |
if args.since_latest_tag: | |
output = git_command("tag", "--sort=-creatordate").splitlines() | |
if len(output) > 0: | |
args.since_ref = output[0] | |
if args.since_ref: | |
output = git_command("show", "-s", "--format='%ci'", args.since_ref) | |
args.since_date = dateutil.parser.parse(output) | |
contributions = query_contributions( | |
owner_name=args.github_repo_owner, | |
repo_name=args.github_repo_name, | |
branch=args.git_branch, | |
since=args.since_date, | |
) | |
print(", ".join(get_markdown_list(contributions))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment