Skip to content

Instantly share code, notes, and snippets.

@abock
Last active January 10, 2023 05:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abock/30fc4e8b1fd1e879e113597ff536aa35 to your computer and use it in GitHub Desktop.
Save abock/30fc4e8b1fd1e879e113597ff536aa35 to your computer and use it in GitHub Desktop.
#!/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