Skip to content

Instantly share code, notes, and snippets.

@skwid138
Last active August 17, 2024 21:52
Show Gist options
  • Save skwid138/05455df21c99198c06b6b51ae5f5ffbe to your computer and use it in GitHub Desktop.
Save skwid138/05455df21c99198c06b6b51ae5f5ffbe to your computer and use it in GitHub Desktop.
Get a list of repositories a user made commits to during a given date range. Make sure the script is executable, update GH_USERNAME and GH_TOKEN. Use `--help` for argument info.
#!/bin/bash
# Get a list of repositories the defined user made commits to in the passed in date range
# Example: ./gh_repo_search.sh 2018-07-01 2018-08-01
# GitHub username and personal access token
GH_USERNAME="gh_username"
GH_TOKEN="***************************************"
# Take 2 passed in dates in yyyy-mm-dd format
START_DATE=""
END_DATE=""
DEBUG=false # Default is no debug output
# Repo Default Parameters
PAGE=1
# Array to store the repositories the user has made commits to in the date range
COMMITTED_REPOS=()
# Store list of repo users and names to ignore
IGNORE_PATTERNS=()
# Display help information
display_help() {
echo "Usage: $0 [OPTIONS] START_DATE END_DATE"
echo
echo "This script retrieves a list of repositories the defined user made commits to during the passed-in date range."
echo
echo "Arguments:"
echo " START_DATE The start date for the commit search (format: yyyy-mm-dd)"
echo " END_DATE The end date for the commit search (format: yyyy-mm-dd)"
echo
echo "Options:"
echo " -d, --debug Enable debug output"
echo " --ignore=PATTERNS Comma-separated list of repository patterns to ignore, this accepts * as a wildcard and "
echo " (e.g., --ignore=\"username/repo-*, anotheruser/repo\")"
echo " -h, --help Display this help message and exit"
echo
echo "Examples:"
echo " $0 2021-12-01 2022-06-01 --ignore=\"username/repo-*, anotheruser/repo, user/*\""
echo " $0 2021-12-01 2022-06-01 -d"
echo
exit 0
}
# Make sure jq is installed
if ! command -v jq &>/dev/null; then
echo "jq is not installed. Please install it to use this script." >&2
exit 1
fi
# Parse command-line arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
-d | --debug)
DEBUG=true
;;
--ignore=*)
# Split the patterns by comma and trim any surrounding whitespace
IFS=',' read -ra patterns <<<"${1#*=}"
for pattern in "${patterns[@]}"; do
pattern=$(echo "$pattern" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
IGNORE_PATTERNS+=("$pattern")
done
;;
--ignore)
shift
IFS=',' read -ra patterns <<<"$1"
for pattern in "${patterns[@]}"; do
pattern=$(echo "$pattern" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
IGNORE_PATTERNS+=("$pattern")
done
;;
-h | --help)
display_help
;;
*)
if [ -z "$START_DATE" ]; then
START_DATE="$1"
elif [ -z "$END_DATE" ]; then
END_DATE="$1"
fi
;;
esac
shift
done
# Debugging statement to ensure the arguments are parsed correctly
if [ "$DEBUG" = true ]; then
echo "Arguments parsed. Start Date: $START_DATE, End Date: $END_DATE, Ignore Patterns: ${IGNORE_PATTERNS[@]}" >&2
fi
# Make sure the date arguments are passed in
if [ -z "$START_DATE" ] || [ -z "$END_DATE" ]; then
echo "Please provide a start date and end date in the format yyyy-mm-dd" >&2
exit 1
fi
# Get list of GitHub repos belonging to the defined user
get_repos() {
local page=$1
local request_url="https://api.github.com/user/repos?per_page=100&page=${page}"
# Debugging statement to ensure the URL is correct
if [ "$DEBUG" = true ]; then
echo "get_repos request_url: ${request_url}" >&2
fi
local response=$(curl -s -u "${GH_USERNAME}:${GH_TOKEN}" "${request_url}")
# Check if response is valid JSON before proceeding
if echo "$response" | jq . >/dev/null 2>&1; then
# Parse the response with jq to return the list of repository full_names
echo "$response" | jq -r '.[].full_name'
else
echo "Error: Unable to parse JSON response from GitHub API"
echo "$response"
exit 1
fi
}
# Get the commits for a given repo using the passed in date range
get_commits() {
local repo=$1
local start_date=$2
local end_date=$3
local request_url="https://api.github.com/repos/${repo}/commits?author=${GH_USERNAME}&since=${start_date}T00:00:00Z&until=${end_date}T23:59:59Z"
local response=$(curl -s -u "${GH_USERNAME}:${GH_TOKEN}" "${request_url}")
# Handle potential errors or empty repositories
if echo "$response" | jq . >/dev/null 2>&1; then
if echo "$response" | jq 'has("message") and .message == "Git Repository is empty."' >/dev/null 2>&1; then
if [ "$DEBUG" = true ]; then
echo "Repository is empty: $repo" >&2
fi
echo "[]" # Return an empty array
else
echo "$response"
fi
else
echo "Error: Unable to parse JSON response for commits"
echo "$response"
exit 1
fi
}
# Check if a repository should be ignored
should_ignore_repo() {
local repo_name=$1
for pattern in "${IGNORE_PATTERNS[@]}"; do
case "$repo_name" in
$pattern)
return 0 # Repo should be ignored
;;
esac
done
return 1 # Repo should NOT be ignored
}
# Loop through each repo and get the commits for the passed in date range
while true; do
# Get list of user's repos
REPOS=$(get_repos $PAGE)
# If no repos are returned, break the loop
if [ -z "$REPOS" ]; then
[ "$DEBUG" = true ] && echo "No repositories returned or reached end of list."
break
fi
# Convert REPOS to an array to avoid subshell issues
IFS=$'\n' read -rd '' -a repo_array <<<"$REPOS"
# Loop through each repo in the array
for REPO in "${repo_array[@]}"; do
[ "$DEBUG" = true ] && echo "Processing repository: $REPO"
# Skip the repo if it matches any ignore pattern
if should_ignore_repo "$REPO"; then
[ "$DEBUG" = true ] && echo "Ignoring repository: $REPO"
continue
fi
# Get the commits made by the user to the repo during the passed in date range
COMMITS=$(get_commits "$REPO" "$START_DATE" "$END_DATE")
# Ensure COMMITS is valid JSON and not an error message
if echo "$COMMITS" | jq . >/dev/null 2>&1; then
commit_count=$(echo "$COMMITS" | jq length)
[ "$DEBUG" = true ] && echo "Commit count for $REPO: $commit_count"
if [ "$commit_count" -gt 0 ]; then
[ "$DEBUG" = true ] && echo "Adding $REPO to COMMITTED_REPOS"
# Add the repo full_name to the list of arrays
#COMMITTED_REPOS+=("$REPO")
COMMITTED_REPOS+=("$REPO | $commit_count")
else
[ "$DEBUG" = true ] && echo "No commits found for $REPO in the specified timeframe."
fi
else
echo "Error: Unable to parse JSON response for commits"
echo "$COMMITS"
exit 1
fi
done
# If fewer than 100 repos are returned, break out of the loop
if [ "${#repo_array[@]}" -lt 100 ]; then
break
fi
# Bump the page number to get the next group of 100 repos for the user
PAGE=$((PAGE + 1))
done
# Echo the list of repositories the user has made commits to in the date range
[ "$DEBUG" = true ] && echo "Final COMMITTED_REPOS content: ${COMMITTED_REPOS[@]}"
if [ ${#COMMITTED_REPOS[@]} -gt 0 ]; then
echo "Repositories ${GH_USERNAME} committed to:"
for REPO in "${COMMITTED_REPOS[@]}"; do
echo "${REPO}"
done
else
echo "No commits found in the specified timeframe."
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment