Created
February 14, 2023 02:31
-
-
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
This file contains hidden or 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 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