Skip to content

Instantly share code, notes, and snippets.

@edmondop
Created January 7, 2026 01:08
Show Gist options
  • Select an option

  • Save edmondop/c0a65f786cdec42d8021e6a435ca128f to your computer and use it in GitHub Desktop.

Select an option

Save edmondop/c0a65f786cdec42d8021e6a435ca128f to your computer and use it in GitHub Desktop.
Github contribution stats
#!/bin/bash
# GitHub Contribution Stats Fetcher (SSO-Compatible)
# Usage: ./gh-contrib-stats.sh <username> <year> <org>
# Check for required arguments
if [ $# -lt 3 ]; then
echo "Usage: $0 <username> <year> <org>"
echo ""
echo "Example: $0 octocat 2025 github"
echo ""
echo "Arguments:"
echo " username - GitHub username"
echo " year - Year to query (e.g., 2025)"
echo " org - GitHub organization name"
exit 1
fi
USERNAME="$1"
YEAR="$2"
ORG="$3"
echo "πŸ“Š GitHub Stats for @${USERNAME} in ${YEAR} (org: ${ORG})"
echo ""
# PRs created
PR_COUNT=$(gh api -X GET "/search/issues" \
-f q="author:${USERNAME} type:pr org:${ORG} created:${YEAR}-01-01..${YEAR}-12-31" \
--jq '.total_count')
# First and last PR
PR_FIRST=$(gh api -X GET "/search/issues" \
-f q="author:${USERNAME} type:pr org:${ORG} created:${YEAR}-01-01..${YEAR}-12-31" \
-f sort="created" -f order="asc" -f per_page="1" \
--jq '.items[0].created_at // empty' | cut -d'T' -f1)
PR_LAST=$(gh api -X GET "/search/issues" \
-f q="author:${USERNAME} type:pr org:${ORG} created:${YEAR}-01-01..${YEAR}-12-31" \
-f sort="created" -f order="desc" -f per_page="1" \
--jq '.items[0].created_at // empty' | cut -d'T' -f1)
# PRs reviewed
REVIEW_COUNT=$(gh api -X GET "/search/issues" \
-f q="reviewed-by:${USERNAME} type:pr org:${ORG} created:${YEAR}-01-01..${YEAR}-12-31" \
--jq '.total_count')
REVIEW_FIRST=$(gh api -X GET "/search/issues" \
-f q="reviewed-by:${USERNAME} type:pr org:${ORG} created:${YEAR}-01-01..${YEAR}-12-31" \
-f sort="created" -f order="asc" -f per_page="1" \
--jq '.items[0].created_at // empty' | cut -d'T' -f1)
REVIEW_LAST=$(gh api -X GET "/search/issues" \
-f q="reviewed-by:${USERNAME} type:pr org:${ORG} created:${YEAR}-01-01..${YEAR}-12-31" \
-f sort="created" -f order="desc" -f per_page="1" \
--jq '.items[0].created_at // empty' | cut -d'T' -f1)
# Issues created
ISSUE_COUNT=$(gh api -X GET "/search/issues" \
-f q="author:${USERNAME} type:issue org:${ORG} created:${YEAR}-01-01..${YEAR}-12-31" \
--jq '.total_count')
ISSUE_FIRST=$(gh api -X GET "/search/issues" \
-f q="author:${USERNAME} type:issue org:${ORG} created:${YEAR}-01-01..${YEAR}-12-31" \
-f sort="created" -f order="asc" -f per_page="1" \
--jq '.items[0].created_at // empty' | cut -d'T' -f1)
ISSUE_LAST=$(gh api -X GET "/search/issues" \
-f q="author:${USERNAME} type:issue org:${ORG} created:${YEAR}-01-01..${YEAR}-12-31" \
-f sort="created" -f order="desc" -f per_page="1" \
--jq '.items[0].created_at // empty' | cut -d'T' -f1)
# Commits
COMMIT_COUNT=$(gh api -X GET "/search/commits" \
-H "Accept: application/vnd.github.cloak-preview" \
-f q="author:${USERNAME} org:${ORG} committer-date:${YEAR}-01-01..${YEAR}-12-31" \
--jq '.total_count')
COMMIT_FIRST=$(gh api -X GET "/search/commits" \
-H "Accept: application/vnd.github.cloak-preview" \
-f q="author:${USERNAME} org:${ORG} committer-date:${YEAR}-01-01..${YEAR}-12-31" \
-f sort="committer-date" -f order="asc" -f per_page="1" \
--jq '.items[0].commit.committer.date // empty' | cut -d'T' -f1)
COMMIT_LAST=$(gh api -X GET "/search/commits" \
-H "Accept: application/vnd.github.cloak-preview" \
-f q="author:${USERNAME} org:${ORG} committer-date:${YEAR}-01-01..${YEAR}-12-31" \
-f sort="committer-date" -f order="desc" -f per_page="1" \
--jq '.items[0].commit.committer.date // empty' | cut -d'T' -f1)
# Calculate total
TOTAL=$((COMMIT_COUNT + PR_COUNT + REVIEW_COUNT + ISSUE_COUNT))
# Find overall first and last activity dates
ALL_DATES=("$COMMIT_FIRST" "$PR_FIRST" "$REVIEW_FIRST" "$ISSUE_FIRST")
ALL_DATES_LAST=("$COMMIT_LAST" "$PR_LAST" "$REVIEW_LAST" "$ISSUE_LAST")
FIRST_ACTIVITY=$(printf '%s\n' "${ALL_DATES[@]}" | grep -v '^$' | sort | head -1)
LAST_ACTIVITY=$(printf '%s\n' "${ALL_DATES_LAST[@]}" | grep -v '^$' | sort -r | head -1)
# Display results
echo "═══════════════════════════════════════════════════════════════════"
echo " CONTRIBUTION SUMMARY"
echo "═══════════════════════════════════════════════════════════════════"
echo ""
printf " %-20s %8s %14s %14s\n" "TYPE" "COUNT" "FIRST" "LAST"
echo " ─────────────────────────────────────────────────────────────────"
printf " πŸ’» %-17s %8d %14s %14s\n" "Commits" "$COMMIT_COUNT" "${COMMIT_FIRST:--}" "${COMMIT_LAST:--}"
printf " πŸ”€ %-17s %8d %14s %14s\n" "Pull Requests" "$PR_COUNT" "${PR_FIRST:--}" "${PR_LAST:--}"
printf " πŸ‘€ %-17s %8d %14s %14s\n" "Reviews" "$REVIEW_COUNT" "${REVIEW_FIRST:--}" "${REVIEW_LAST:--}"
printf " πŸ› %-17s %8d %14s %14s\n" "Issues" "$ISSUE_COUNT" "${ISSUE_FIRST:--}" "${ISSUE_LAST:--}"
echo " ─────────────────────────────────────────────────────────────────"
printf " πŸ“ˆ %-17s %8d %14s %14s\n" "TOTAL" "$TOTAL" "${FIRST_ACTIVITY:--}" "${LAST_ACTIVITY:--}"
echo "═══════════════════════════════════════════════════════════════════"
# Calculate percentages if total > 0
if [ "$TOTAL" -gt 0 ]; then
echo ""
echo " BREAKDOWN:"
echo " ─────────────────────────────────────────"
printf " πŸ’» Commits: %3d%% " "$((COMMIT_COUNT * 100 / TOTAL))"
printf "%-30s\n" "$(printf 'β–ˆ%.0s' $(seq 1 $((COMMIT_COUNT * 30 / TOTAL))))"
printf " πŸ”€ PRs: %3d%% " "$((PR_COUNT * 100 / TOTAL))"
printf "%-30s\n" "$(printf 'β–ˆ%.0s' $(seq 1 $((PR_COUNT * 30 / TOTAL))))"
printf " πŸ‘€ Reviews: %3d%% " "$((REVIEW_COUNT * 100 / TOTAL))"
printf "%-30s\n" "$(printf 'β–ˆ%.0s' $(seq 1 $((REVIEW_COUNT * 30 / TOTAL))))"
printf " πŸ› Issues: %3d%% " "$((ISSUE_COUNT * 100 / TOTAL))"
printf "%-30s\n" "$(printf 'β–ˆ%.0s' $(seq 1 $((ISSUE_COUNT * 30 / TOTAL))))"
echo ""
fi
# Calculate active days
if [ -n "$FIRST_ACTIVITY" ] && [ -n "$LAST_ACTIVITY" ]; then
if [[ "$OSTYPE" == "darwin"* ]]; then
FIRST_SEC=$(date -j -f "%Y-%m-%d" "$FIRST_ACTIVITY" "+%s" 2>/dev/null)
LAST_SEC=$(date -j -f "%Y-%m-%d" "$LAST_ACTIVITY" "+%s" 2>/dev/null)
else
FIRST_SEC=$(date -d "$FIRST_ACTIVITY" "+%s" 2>/dev/null)
LAST_SEC=$(date -d "$LAST_ACTIVITY" "+%s" 2>/dev/null)
fi
if [ -n "$FIRST_SEC" ] && [ -n "$LAST_SEC" ]; then
DAYS_SPAN=$(( (LAST_SEC - FIRST_SEC) / 86400 + 1 ))
AVG_PER_DAY=$(echo "scale=2; $TOTAL / $DAYS_SPAN" | bc)
echo " πŸ“… Active span: ${DAYS_SPAN} days (${FIRST_ACTIVITY} β†’ ${LAST_ACTIVITY})"
echo " ⚑ Average: ${AVG_PER_DAY} contributions/day"
echo ""
fi
fi
echo "═══════════════════════════════════════════════════════════════════"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment