Skip to content

Instantly share code, notes, and snippets.

@obradovic
Last active September 12, 2015 14:25
Show Gist options
  • Save obradovic/306e58049cab5b8a4b40 to your computer and use it in GitHub Desktop.
Save obradovic/306e58049cab5b8a4b40 to your computer and use it in GitHub Desktop.
#!/bin/bash
#
# README
# On OSX please do first:
# brew install coreutils jq
# export GITHUB_ORG="fooproject"
# export GITHUB_REPOS="foo bar baz"
# export GITHUB_USER="your username"
# export GITHUB_PASS="your password"
#
# TODO:
# How long between last commit and release to production? (look at tags)
# Ignore WIP tags/titles
#
# Sentiment analysis on comments
# Handle Github's API Rate Limiting better
#
# Our Github org and list of repos
ORG=$GITHUB_ORG
# Can pass in a list of repos as arg 1. Otherwise, will just do all of them
REPOS=( $1 )
if [ -z "$1" ]
then
REPOS=( $GITHUB_REPOS )
fi
# just some DRYness
CURL="curl -s"
AUTH="--user $GITHUB_USER:$GITHUB_PASS"
GITHUB="$CURL $AUTH https://api.github.com"
# Iterate over the repos
for REPO in "${REPOS[@]}"
do
LATEST_PR=$2
# If LATEST_PR was not specified on the cmd line, find the latest pr
if [ -z "$2" ]
then
# What is the most recent PR for this repo?
LATEST_PR=`$GITHUB/repos/$ORG/$REPO/pulls?state=all | jq ".[0].number"`
# Sanity check - maybe we got an empty response? If there hasnt been any PRs
if [ `echo $LATEST_PR | jq 'length'` -eq 0 ]
then
continue
fi
fi
# Now iterate over all PRs for this repo, starting at the most recent and then decrementing
for ((PR_ID=$LATEST_PR; PR_ID>=1; PR_ID--))
do
# get the details of this PR
PR=`$GITHUB/repos/$ORG/$REPO/pulls/$PR_ID | jq "."`
# Sanity check - maybe we had an error?
if [[ `echo $PR | jq ".message"` = "\"Not Found\"" ]]
then
echo "$REPO PR $PR_ID not found"
continue
fi
# get create and update times
STATE=`echo $PR | jq ".state" | tr -d '"'`
CREATED_SECS=`echo $PR | jq ".created_at" | xargs gdate +%s -d`
UPDATED_SECS=`echo $PR | jq ".updated_at" | xargs gdate +%s -d`
# get some stats on changes
NUM_ADDITIONS=`echo $PR | jq ".additions"`
NUM_DELETIONS=`echo $PR | jq ".deletions"`
NUM_CHANGES=`expr $NUM_ADDITIONS + $NUM_DELETIONS`
NUM_CHANGED_FILES=`echo $PR | jq ".changed_files"`
# minor date arithmetic
SECS_PER_MIN=60
MINS_PER_DAY=1440
NOW=`gdate +%s`
SECS_SINCE_CREATION=`expr $NOW - $CREATED_SECS`
MINS_SINCE_CREATION=`expr $SECS_SINCE_CREATION / $SECS_PER_MIN`
DAYS_SINCE_CREATION=`expr $MINS_SINCE_CREATION / $MINS_PER_DAY`
# Is the PR still open?
if [ $STATE = "open" ];
then
echo "$REPO PR `printf "%5s" $PR_ID` is open: `printf "%3s" $DAYS_SINCE_CREATION` days: `printf "%6s" $MINS_SINCE_CREATION` minutes"
continue
fi
# Was the PR closed but not merged?
MERGED_AT=`echo $PR | jq ".merged_at" | tr -d '"'`
if [ $MERGED_AT = "null" ]
then
CLOSED=`echo $PR | jq ".closed_at" | xargs gdate +%s -d`
SECS_TO_CLOSE=`expr $CLOSED - $CREATED_SECS`
MINS_TO_CLOSE=`expr $SECS_TO_CLOSE / $SECS_PER_MIN`
DAYS_TO_CLOSE=`expr $MINS_TO_CLOSE / $MINS_PER_DAY`
echo "$REPO PR `printf "%5s" $PR_ID` was closed: `printf "%3s" $DAYS_TO_CLOSE` days: `printf "%6s" $MINS_TO_CLOSE` minutes"
continue
fi
# ok the PR was merged. Munge!
MERGED_SECS=`gdate -d $MERGED_AT +%s`
SECS_TO_MERGE=`expr $MERGED_SECS - $CREATED_SECS`
MINS_TO_MERGE=`expr $SECS_TO_MERGE / $SECS_PER_MIN`
DAYS_TO_MERGE=`expr $MINS_TO_MERGE / $MINS_PER_DAY`
# VSCO time periods
VSCO_EPOCH="2013-04-01"
VSCO_YEAR=$(expr `gdate -d $MERGED_AT +%Y` - `gdate -d $VSCO_EPOCH +%Y`)
VSCO_MONTH=$(expr $VSCO_YEAR \* 12 + `gdate -d $MERGED_AT +%m`)
VSCO_DAY=$(expr $VSCO_YEAR \* 365 + `gdate -d $MERGED_AT +%j`)
VSCO_WEEK=$(expr $VSCO_DAY / 7 )
# Get creator and merger
CREATOR=`echo $PR | jq ".user.login" | tr -d '"'`
MERGER=`echo $PR | jq ".merged_by.login" | tr -d '"'`
#
# ITERATE over the COMMITS
#
COMMITS=`$GITHUB/repos/$ORG/$REPO/pulls/$PR_ID/commits`
NUM_COMMITS=`echo $COMMITS | jq 'length'`
# Initially, set the first commit to be both the first and last date
FIRST_COMMIT_SECS=`echo $COMMITS | jq '.[0].commit.author.date' | tr -d '"' | xargs gdate +%s -d`
LAST_COMMIT_SECS=$FIRST_COMMIT_SECS
# start iterating at 1 instead of 0, cause we just looked at the first one
for ((COMMIT_ID = 1; COMMIT_ID < $NUM_COMMITS; COMMIT_ID++))
do
COMMIT=`echo $COMMITS | jq ".[$COMMIT_ID]"`
AUTHOR_SECS=`echo $COMMIT | jq ".commit.author.date" | tr -d '"' | xargs gdate +%s -d`
if (( "$AUTHOR_SECS" < "$FIRST_COMMIT_SECS" ))
then
FIRST_COMMIT_SECS=$AUTHOR_SECS
fi
if (( "$AUTHOR_SECS" > "$LAST_COMMIT_SECS" ))
then
LAST_COMMIT_SECS=$AUTHOR_SECS
fi
done
#
# ITERATE over the COMMENTS
#
# GET: Time from first commit to first comment
# AND: Time from last commit to final merge
COMMENTS=`$GITHUB/repos/$ORG/$REPO/issues/$PR_ID/comments` # do not reference a portion of the unified diff
REVIEW_COMMENTS=`$GITHUB/repos/$ORG/$REPO/pulls/$PR_ID/comments` # Review Comments are comments on a portion of the unified diff
TOTAL_COMMENTS=`echo $COMMENTS $REVIEW_COMMENTS | jq -s add` # jams the above two jsons arrays into one
NUM_COMMENTS_TOTAL=`echo $TOTAL_COMMENTS | jq 'length'`
FIRST_COMMENT_SECS=0
if [ "$NUM_COMMENTS_TOTAL" -gt 0 ];
then
FIRST_COMMENT_SECS=`echo $TOTAL_COMMENTS | jq '.[0].created_at' | tr -d '"' | xargs gdate +%s -d`
fi
LAST_COMMENT_SECS=$FIRST_COMMENT_SECS
# start iterating at 1 instead of 0, cause we just looked at the first one
for ((COMMENT_ID = 1; COMMENT_ID < $NUM_COMMENTS_TOTAL; COMMENT_ID++))
do
COMMENT=`echo $TOTAL_COMMENTS | jq ".[$COMMENT_ID]"`
COMMENT_SECS=`echo $COMMENT | jq ".created_at" | tr -d '"' | xargs gdate +%s -d`
if (( "$COMMENT_SECS" < "$FIRST_COMMENT_SECS" ))
then
FIRST_COMMENT_SECS=$COMMENT_SECS
fi
if (( "$COMMENT_SECS" > "$LAST_COMMENT_SECS" ))
then
LAST_COMMENT_SECS=$COMMENT_SECS
fi
done
# time between PR creation and first comment ("eyes on code")
PR_CREATE_TO_FIRST_COMMMENT_SECS=`expr $FIRST_COMMENT_SECS - $CREATED_SECS`
PR_CREATE_TO_FIRST_COMMMENT_MINS=`expr $PR_CREATE_TO_FIRST_COMMMENT_SECS / $SECS_PER_MIN`
# If there were no comments, lets write out "none"
if [ "$NUM_COMMENTS_TOTAL" -eq 0 ];
then
PR_CREATE_TO_FIRST_COMMMENT_MINS="none"
fi
# time between last commit and merge ("inventory")
INVENTORY_SECS=`expr $MERGED_SECS - $LAST_COMMIT_SECS`
INVENTORY_MINS=`expr $INVENTORY_SECS / $SECS_PER_MIN`
echo "$REPO PR `printf "%5s" $PR_ID` was merged `printf "%3s" $DAYS_TO_MERGE` days: `printf "%6s" $MINS_TO_MERGE` minutes: `printf "%3s" $NUM_CHANGED_FILES` files had `printf "%6s" $NUM_CHANGES` changes: `printf "%3s" $NUM_COMMITS` commits with `printf "%3s" $NUM_COMMENTS_TOTAL` comments. Epoch day $VSCO_DAY week $VSCO_WEEK month $VSCO_MONTH: `printf "%6s" $PR_CREATE_TO_FIRST_COMMMENT_MINS` mins_to_comment, `printf "%6s" $INVENTORY_MINS` mins_inv: $CREATOR $MERGER"
done
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment