Skip to content

Instantly share code, notes, and snippets.

@L1ghtmann
Last active February 13, 2023 21:40
Show Gist options
  • Select an option

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

Select an option

Save L1ghtmann/4392264f9a0dd2c189d53e2602c8cf0b 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 perl
#
# org-contributors.pl
# Created by Lightmann
# February 2023
#
use strict;
use warnings;
use FindBin;
use File::Temp;
use Getopt::Long;
use sigtrap qw(die normal-signals error-signals);
my $script = $FindBin::Script;
my $usage = <<"EOF";
Usage: $script [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
EOF
my ($opt_ignore, $opt_org, $opt_help);
GetOptions(
"ignore|i=s" => \$opt_ignore,
"org|o=s" => \$opt_org,
"help|h" => \$opt_help,
);
if ($opt_help) {
print $usage;
exit 0;
}
die "Usage: $script --org <org name>\nRun $script --help for more details\n" if (!defined $opt_org);
my $curl = `curl -s "https://api.github.com/users/$opt_org/repos?per_page=100" | grep -o 'git@[^"]*'`;
die "Error: the org specified -- $opt_org -- does not appear to exist. Please check the name for typos.\n" if ($curl eq '');
my @repos = split('\n', $curl);
my $dir = FindBin::again();
my $tmp = File::Temp->newdir;
chdir $tmp;
my %authors;
foreach my $repo (@repos){
# Switch repo url to https from ssh
(my $url = $repo) =~ s/git\@github.com:/https:\/\/github.com\//;
# Skip specified repos
if (defined $opt_ignore){
my @filters = ($opt_ignore);
@filters = split(',', $opt_ignore) if ($opt_ignore =~ /,/);
# https://stackoverflow.com/a/15951137
# Convert array elements into regex
my $rx = join('\b|\b', map quotemeta, @filters);
if ($url =~ /\b$rx\b/) {
print "[=] Skipping $url\n";
next;
}
}
print "[x] Starting work on $url....\n";
# Clone repo
system("git clone --quiet --no-checkout $url");
# Grab repo name (i.e., directory name)
(my $directory = (split(/\//, $url))[-1]) =~ s/\.[^.]+$//;
# Get contribution info
my $log = `git -C $directory --no-pager log --stat`;
my @commits = split(/commit /, $log);
print "[+] Got the commit history. Quantifying diff....\n";
foreach my $commit (@commits){
my @lines = split(/\n/, $commit);
# Grab relevant bits
my ($author) = grep(/Author: /, @lines);
my ($diff) = grep(/changed, /, @lines);
# Santiy check
next if (!defined $author || !defined $diff);
# Simplify bits
$author =~ s/Author: //g; # Remove prefix
$author =~ s/(<[^<>]*>)//g; # Remove email
$author =~ s/\s+$//; # Remove trailing space
my @changes = split(/, /, $diff);
# Do some math
foreach my $change (@changes){
if (index($change, "-") != -1 || index($change, "+") != -1) {
(my $num = $change) =~ s/\D//g;
$authors{$author} += $num if ($num ne "");
}
}
}
}
print "[!] The results are in. Sorting....\n";
# https://stackoverflow.com/a/9660407
# Sort hash keys according to respective val
# Need to maintain the order, hence the array
my @sorted = sort { $authors{$b} <=> $authors{$a} } keys %authors;
print "[-] Ordered list of contributors to $opt_org repositories:\n";
print "-------------------------------------------------------\n";
foreach my $author (@sorted) {
# https://stackoverflow.com/a/58104835
# Format each diff num nicely
my $diff = $authors{$author};
while($diff =~ s/(\d+)(\d\d\d)/$1\,$2/){};
print "◉ $author: $diff edits\n";
}
print "-------------------------------------------------------\n";
chdir $dir;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment