| from dataclasses import dataclass |
|
|
| import requests |
|
|
|
|
| @dataclass |
| class User: |
| name: str |
| organizations: list[str] |
|
|
|
|
| @dataclass |
| class Commit: |
| message: str |
| user: User |
| additions: int |
| deletions: int |
|
|
|
|
| def call_with_query(query, token): |
| url = "https://api.github.com/graphql" |
| r = requests.post(url, json={"query": query}, headers={"Authorization": f"Bearer {token}"}) |
| return r.json() |
|
|
|
|
| def get_tag_commit_date(token, repository, tag): |
| owner, name = repository.split("/") |
| query = f""" |
| query GetTagCommit {{ |
| repository(owner: "{owner}", name: "{name}"){{ |
| object(expression: "{tag}") {{ |
| ... on Commit {{ |
| oid |
| message |
| committedDate |
| author {{ |
| user {{ |
| login |
| }} |
| }} |
| }} |
| }} |
| }} |
| }} |
| """ |
|
|
| response = call_with_query(query, token) |
|
|
| try: |
| repository = response["data"]["repository"]["object"] |
|
|
| if repository is None: |
| if "errors" in response: |
| raise ValueError(response["errors"][0]["message"]) |
| raise ValueError("Invalid tag. Does this tag exist?") |
|
|
| committed_date = repository["committedDate"] |
| except (KeyError, TypeError): |
| raise ValueError("Invalid token. Does your token have the valid permissions?") |
|
|
| return committed_date |
|
|
|
|
| def get_commits(token, repository, branch, since): |
| owner, name = repository.split("/") |
|
|
| def get_page_result(next_page=""): |
| query = f""" |
| query GetCommits {{ |
| repository(owner: "{owner}", name: "{name}"){{ |
| nameWithOwner |
| object(expression: "{branch}") {{ |
| ... on Commit {{ |
| oid |
| history(first: 100, since: "{since}"{next_page}) {{ |
| nodes {{ |
| message |
| deletions |
| additions |
| author {{ |
| user {{ |
| login |
| organizations(first: 100) {{ |
| nodes {{ |
| name |
| }} |
| }} |
| }} |
| }} |
| }} |
| pageInfo {{ |
| endCursor |
| hasNextPage |
| }} |
| }} |
| }} |
| }} |
| }} |
| }} |
| """ |
| result = call_with_query(query, token) |
|
|
| if "data" not in result: |
| raise ValueError(result["errors"][0]["message"]) |
|
|
| if result["data"]["repository"]["object"] is None: |
| raise ValueError("Either the tag or the branch were incorrect.") |
|
|
| nodes = result["data"]["repository"]["object"]["history"]["nodes"] |
| cursor = result["data"]["repository"]["object"]["history"]["pageInfo"]["endCursor"] |
| return nodes, cursor |
|
|
| nodes, cursor = get_page_result() |
|
|
| while cursor is not None: |
| _nodes, cursor = get_page_result(f' after:"{cursor}"') |
| nodes.extend(_nodes) |
|
|
| commits = [] |
| for node in nodes: |
| if node["author"]["user"] is None: |
| commits.append( |
| Commit( |
| message=node["message"].split("\n")[0], |
| user=User(name="<NOT FOUND>", organizations=[]), |
| additions=node.get("additions"), |
| deletions=node.get("deletions"), |
| ) |
| ) |
| else: |
| commits.append( |
| Commit( |
| message=node["message"].split("\n")[0], |
| user=User( |
| name=node["author"]["user"]["login"], |
| organizations=[n["name"] for n in node["author"]["user"]["organizations"]["nodes"]], |
| ), |
| additions=node.get("additions"), |
| deletions=node.get("deletions"), |
| ) |
| ) |
|
|
| return commits |
|
|