Last active
July 27, 2023 07:02
-
-
Save jfgordon2/d83f4f097d7aa62829542a632e1f77a3 to your computer and use it in GitHub Desktop.
Clone all GitHub repos for an organization
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
#!/bin/bash | |
# Clone all GitHub repos for an organization | |
# Usage: gh-clone.sh [--limit <number>] [--path <path>] [--update] <organization> | |
# Requires GitHub CLI: https://cli.github.com/manual/installation | |
# Requires jq: https://stedolan.github.io/jq/download/ | |
function check_requirements() { | |
# ensure GitHub CLI installed | |
if ! command -v gh &>/dev/null; then | |
printf "\e[1m%s\e[0m\n" "GitHub CLI not installed" | |
echo "See https://cli.github.com/manual/installation" | |
fi | |
# ensure jq installed | |
if ! command -v jq &>/dev/null; then | |
printf "\e[1m%s\e[0m\n" "jq not installed" | |
echo "See https://stedolan.github.io/jq/download/" | |
fi | |
if ! command -v gh &>/dev/null || ! command -v jq &>/dev/null; then | |
exit 1 | |
fi | |
} | |
function available_organizations() { | |
check_requirements | |
organizations=$(gh api user/orgs | jq -r '.[].login') | |
self=$(gh api user | jq -r '.login') | |
printf "\e[1m%s\e[0m\n" "Available organizations are:" | |
echo "$self" | |
echo "$organizations" | |
} | |
function update_git_at_directory() { | |
printf "\n\e[1m%s\e[0m" "Updating $1 " | |
# Get main branch name | |
main_branch=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5) | |
# Get current branch name | |
current_branch=$(git rev-parse --abbrev-ref HEAD) | |
# Update local checkout | |
git checkout -q "$main_branch" 2>/dev/null || true | |
# Pull latest changes and print full error output on new line if any | |
OUTPUT=$(git pull -q 2>&1) | |
if [ $? -ne 0 ]; then | |
printf "\e[31m%s\e[0m\n" "$OUTPUT" | |
else | |
printf "\e[34m%s\e[0m" " $main_branch updated" | |
fi | |
# restore previously checked out branch | |
git checkout -q "$current_branch" 2>/dev/null || true | |
} | |
function check_if_git_repo_and_update() { | |
cd "$1" || exit | |
if [ -d ".git" ]; then | |
if [ -z "$(git rev-parse --verify HEAD 2>/dev/null)" ]; then | |
# If empty, delete local checkout | |
printf "\e[31m%s\e[0m" " empty repo" | |
rm -rf "$1" | |
printf " - deleted" | |
else | |
update_git_at_directory "$1" | |
fi | |
fi | |
} | |
function show_help() { | |
printf "\e[1m%s\e[0m%s\n" "Usage: " "$0 <arguments> <organization>" | |
echo "" | |
printf "\e[1m%s\e[0m\n" "Arguments:" | |
echo " --limit <number> Limit number of repos to clone (default: 1000)" | |
echo " --help Show this help" | |
echo " --path Path to clone repos to (default: ~/GitHub)" | |
echo " --update Update local checkout" | |
echo "" | |
echo "Clones all repos for an organization" | |
echo "" | |
available_organizations | |
exit 0 | |
} | |
# ensure organization name provided in first argument | |
if [ -z "$1" ]; then | |
printf "\e[1m%s\e[0m\n" "Organization name required" | |
echo "" | |
show_help | |
fi | |
# defaults | |
limit=1000 | |
dir_path="$HOME/GitHub" | |
update=false | |
# Parse arguments | |
while true; do | |
case "$1" in | |
--limit) | |
limit="$2" | |
shift 2 | |
;; | |
--path) | |
dir_path="$2" | |
shift 2 | |
;; | |
--update) | |
update=true | |
shift | |
;; | |
--help) | |
show_help | |
;; | |
*) | |
break | |
;; | |
esac | |
done | |
printf "\e[1m%s\e[0m\n" "Cloning repos for $1" | |
gh repo list "$1" --limit "$limit" | while read -r repo _; do | |
printf "\nResolving %s..." "$repo" | |
# Check if repo is archived | |
archived=$(gh api "repos/$repo" | jq -r '.archived') | |
repo_path="$dir_path/$repo" | |
if [ "$archived" = "true" ]; then | |
IFS='/' read -r -a split_name <<<"$repo" | |
repo_path="$dir_path/${split_name[0]}/~archived/${split_name[1]}" | |
fi | |
# Clone repo if not already cloned | |
if [ ! -d "$repo_path" ]; then | |
if [ "$archived" = "true" ] && [ -d "$dir_path/$repo" ]; then | |
# If repo is archived, but not in archived folder, move it | |
mv "$dir_path/$repo" "$repo_path" | |
printf "\e[33m%s\e[0m" " archived" | |
printf " - moved" | |
else | |
gh repo clone "$repo" "$repo_path" -- -q 2>/dev/null || true | |
printf "\e[32m%s\e[0m" " cloned" | |
fi | |
else | |
printf "\e[32m%s\e[0m" " already exists" | |
fi | |
# Update local checkout | |
if [ "$update" = true ]; then | |
check_if_git_repo_and_update "$repo_path" | |
# Advise if repo is archived | |
if [ "$archived" == "true" ]; then | |
printf "\e[33m%s\e[0m" " - archived" | |
fi | |
fi | |
done | |
printf "\n\n\e[1m%s\e[0m\n" "Done!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment