Skip to content

Instantly share code, notes, and snippets.

@L1ghtmann
Created February 14, 2023 02:31
Show Gist options
  • Select an option

  • Save L1ghtmann/bae4b379e176b0af65a6e57cb41793b0 to your computer and use it in GitHub Desktop.

Select an option

Save L1ghtmann/bae4b379e176b0af65a6e57cb41793b0 to your computer and use it in GitHub Desktop.
Get a list of contributors to a public GitHub org sorted by total diff
#!/usr/bin/env python3
#
# org-contributors.py
# Created by Lightmann
# February 2023
#
import subprocess, tempfile, argparse, sys, os, re
def help_prompt():
"""Usage: org-contributors.py [options]
Options:
[-i|--ignore] Comma-separated list of repos to ignore in the organization
[-o|--org] Name of GitHub organization to query
[-h|--help] Display this page"""
print(help_prompt.__doc__)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--ignore", action="store")
parser.add_argument("-o", "--org", action="store", required=True)
parser.print_help = help_prompt
opt = parser.parse_args()
curl = subprocess.getoutput(f"curl -s \"https://api.github.com/users/{opt.org}/repos?per_page=100\" | grep -o 'git@[^\"]*'")
if curl == "":
print(f"Error: the org specified -- {opt.org} -- does not appear to exist. Please check the name for typos.")
exit(1)
repos = curl.split("\n")
cdir = sys.path[0]
tmp = tempfile.TemporaryDirectory()
os.chdir(tmp.name)
authors = {}
for repo in repos:
# Switch repo url to https from ssh
url = repo.replace("git@github.com:", "https://github.com/")
# Skip specified repos
if opt.ignore:
filters = [opt.ignore]
if "," in opt.ignore:
filters = opt.ignore.split(",")
# https://stackoverflow.com/q/653509
# Check if filter strings are present
class skip(Exception): pass
try:
for i in filters:
if i in repo:
print(f"[=] Skipping {url}")
raise skip
except skip:
continue
print(f"[x] Starting work on {url}....")
# Clone repo
os.system(f"git clone --quiet --no-checkout {url}")
# Grab repo name (i.e., directory name)
# Split url at "/", grab repo, and remove extension
directory = (url.split("/")[-1]).replace(".git", "")
# Get contribution info
log = subprocess.getoutput(f"git -C {directory} --no-pager log --stat")
commits = log.split("commit ")
print("[+] Got the commit history. Quantifying diff....")
for commit in commits:
lines = commit.split("\n")
# Grab relevant bits
author = ""
diff = ""
for line in lines:
if "Author: " in line:
author = line
elif "changed, " in line:
diff = line
# Santiy check
if not author or not diff:
continue
# Simplify bits
author = author.replace("Author: ", "") # Remove prefix
author = re.sub("<[^<>]*>", "", author) # Remove email
author = author.strip() # Remove trailing space
changes = diff.split(", ")
# Do some math
for change in changes:
if "-" in change or "+" in change:
num = int(re.sub("\D", "", change))
if num != "":
if not author in authors:
authors[author] = num
else:
authors[author] += num
print("[!] The results are in. Sorting....")
# https://stackoverflow.com/a/2258273
# Sort dict key-val pairs according to vals
# Puts into tuples in a list to maintain order
sortedA = sorted(authors.items(), key=lambda x: x[1], reverse=True)
print(f"[-] Ordered list of contributors to {opt.org} repositories:")
print("-------------------------------------------------------")
for pair in sortedA:
# https://stackoverflow.com/a/25281291
# Format each diff num nicely
diff = (format(pair[1], ",d"))
print(f"◉ {pair[0]}: {diff} edits")
print("-------------------------------------------------------")
os.chdir(cdir)
tmp.cleanup()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment