2024-03-28 06:11:33 +00:00
import re
2024-03-28 05:14:30 +00:00
from argparse import ArgumentParser , RawTextHelpFormatter
from typing import Any
import requests
from attr import dataclass
2024-03-28 06:11:33 +00:00
from tqdm import tqdm
2024-03-28 05:14:30 +00:00
def get_author ( commit : dict [ str , Any ] ) - > str :
""" Gets the author of a commit.
If the author is not present , the committer is used instead and an asterisk appended to the name . """
return commit [ " author " ] [ " login " ] if commit [ " author " ] else f " { commit [ ' commit ' ] [ ' author ' ] [ ' name ' ] } * "
@dataclass
class CommitInfo :
sha : str
url : str
author : str
is_username : bool
message : str
data : dict [ str , Any ]
def __str__ ( self ) - > str :
return f " { self . sha } : { self . author } { ' * ' if not self . is_username else ' ' } - { self . message } ( { self . url } ) "
@classmethod
def from_data ( cls , commit : dict [ str , Any ] ) - > " CommitInfo " :
return CommitInfo (
sha = commit [ " sha " ] ,
url = commit [ " url " ] ,
author = commit [ " author " ] [ " login " ] if commit [ " author " ] else commit [ " commit " ] [ " author " ] [ " name " ] ,
is_username = bool ( commit [ " author " ] ) ,
message = commit [ " commit " ] [ " message " ] . split ( " \n " ) [ 0 ] ,
data = commit ,
)
def fetch_commits_between_tags (
org_name : str , repo_name : str , from_ref : str , to_ref : str , token : str
) - > list [ CommitInfo ] :
""" Fetches all commits between two tags in a GitHub repository. """
2024-03-28 06:11:33 +00:00
commit_info : list [ CommitInfo ] = [ ]
2024-03-28 05:14:30 +00:00
headers = { " Authorization " : f " token { token } " } if token else None
2024-03-28 06:11:33 +00:00
# Get the total number of pages w/ an intial request - a bit hacky but it works...
response = requests . get (
f " https://api.github.com/repos/ { org_name } / { repo_name } /compare/ { from_ref } ... { to_ref } ?page=1&per_page=100 " ,
headers = headers ,
)
last_page_match = re . search ( r ' page=( \ d+)&per_page= \ d+>; rel= " last " ' , response . headers [ " Link " ] )
last_page = int ( last_page_match . group ( 1 ) ) if last_page_match else 1
pbar = tqdm ( range ( 1 , last_page + 1 ) , desc = " Fetching commits " , unit = " page " , leave = False )
for page in pbar :
compare_url = f " https://api.github.com/repos/ { org_name } / { repo_name } /compare/ { from_ref } ... { to_ref } ?page= { page } &per_page=100 "
response = requests . get ( compare_url , headers = headers )
commits = response . json ( ) [ " commits " ]
commit_info . extend ( [ CommitInfo . from_data ( c ) for c in commits ] )
return commit_info
2024-03-28 05:14:30 +00:00
def main ( ) :
description = """ Fetch external contributions between two tags in the InvokeAI GitHub repository. Useful for generating a list of contributors to include in release notes.
When the GitHub username for a commit is not available , the committer name is used instead and an asterisk appended to the name .
Example output ( note the second commit has an asterisk appended to the name ) :
171 f2aa20ddfefa23c5edbeb2849c4bd601fe104 : rohinish404 - fix ( ui ) : image not getting selected ( https : / / api . github . com / repos / invoke - ai / InvokeAI / commits / 171 f2aa20ddfefa23c5edbeb2849c4bd601fe104 )
0 bb0e226dcec8a17e843444ad27c29b4821dad7c : Mark E . Shoulson * - Flip default ordering of workflow library ; #5477 (https://api.github.com/repos/invoke-ai/InvokeAI/commits/0bb0e226dcec8a17e843444ad27c29b4821dad7c)
"""
parser = ArgumentParser ( description = description , formatter_class = RawTextHelpFormatter )
parser . add_argument ( " --token " , dest = " token " , type = str , default = None , help = " The GitHub token to use " )
parser . add_argument ( " --from " , dest = " from_ref " , type = str , help = " The start reference (commit, tag, etc) " )
parser . add_argument ( " --to " , dest = " to_ref " , type = str , help = " The end reference (commit, tag, etc) " )
args = parser . parse_args ( )
org_name = " invoke-ai "
repo_name = " InvokeAI "
# List of members of the organization, including usernames and known display names,
# any of which may be used in the commit data. Used to filter out commits.
org_members = [
" blessedcoolant " ,
" brandonrising " ,
" chainchompa " ,
" ebr " ,
" Eugene Brodsky " ,
" hipsterusername " ,
" Kent Keirsey " ,
" lstein " ,
" Lincoln Stein " ,
" maryhipp " ,
" Mary Hipp Rogers " ,
" Mary Hipp " ,
" psychedelicious " ,
" RyanJDick " ,
" Ryan Dick " ,
]
all_commits = fetch_commits_between_tags (
org_name = org_name ,
repo_name = repo_name ,
from_ref = args . from_ref ,
to_ref = args . to_ref ,
token = args . token ,
)
filtered_commits = filter ( lambda x : x . author not in org_members , all_commits )
for commit in filtered_commits :
print ( commit )
if __name__ == " __main__ " :
main ( )