-
-
Save spanthetree/f583667f97813b863171 to your computer and use it in GitHub Desktop.
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 | |
# A simple script to backup an organization's GitHub repositories. | |
GHBU_BACKUP_DIR=${GHBU_BACKUP_DIR-"/media/github_backup/bash_backup"} # where to place the backup files | |
GHBU_ORG=${GHBU_ORG-"YOUR_ORG"} # the GitHub organization whose repos will be backed up | |
# (if you're backing up a user's repos instead, this should be your GitHub username) | |
GHBU_UNAME=${GHBU_UNAME-"YOUR_USERNAME"} # the username of a GitHub account (to use with the GitHub API) | |
GHBU_PASSWD=${GHBU_PASSWD-"YOUR_PASSWORD"} # the password for that account | |
GHBU_GITHOST=${GHBU_GITHOST-"github.com"} # the GitHub hostname (see comments) | |
GHBU_PRUNE_OLD=${GHBU_PRUNE_OLD-true} # when `true`, old backups will be deleted | |
GHBU_PRUNE_AFTER_N_DAYS=${GHBU_PRUNE_AFTER_N_DAYS-30} # the min age (in days) of backup files to delete | |
GHBU_SILENT=${GHBU_SILENT-false} # when `true`, only show error messages | |
GHBU_API=${GHBU_API-"https://api.github.com"} # base URI for the GitHub API | |
#GHBU_GIT_CLONE_CMD="git clone --quiet --mirror git@${GHBU_GITHOST}:" # base command to use to clone GitHub repos | |
GHBU_GIT_CLONE_CMD="git clone --mirror git@${GHBU_GITHOST}:" # base command to use to clone GitHub repos | |
PERPAGE="?per_page=100" | |
TSTAMP=`date "+%Y%m%d-%H%M"` | |
# The function `check` will exit the script if the given command fails. | |
function check { | |
"$@" | |
status=$? | |
if [ $status -ne 0 ]; then | |
echo "ERROR: Encountered error (${status}) while running the following:" >&2 | |
echo " $@" >&2 | |
echo " (at line ${BASH_LINENO[0]} of file $0.)" >&2 | |
echo " Aborting." >&2 | |
exit $status | |
fi | |
} | |
# The function `tgz` will create a gzipped tar archive of the specified file ($1) and then remove the original | |
function tgz { | |
check tar zcf $1.tar.gz $1 && check rm -rf $1 | |
} | |
$GHBU_SILENT || (echo "" && echo "=== INITIALIZING ===" && echo "") | |
$GHBU_SILENT || echo "Using backup directory $GHBU_BACKUP_DIR" | |
check mkdir -p $GHBU_BACKUP_DIR | |
$GHBU_SILENT || echo -n "Fetching list of repositories for ${GHBU_ORG}..." | |
REPOCOUNT=100 | |
until [ $REPOCOUNT -ne 100 ]; do | |
((PAGE++)) | |
echo "" && echo "=== Starting repo list of page $PAGE ===" | |
REPOLIST=`check curl --silent -u $GHBU_UNAME:$GHBU_PASSWD "${GHBU_API}/orgs/${GHBU_ORG}/repos${PERPAGE}&page=${PAGE}" -q | check grep "\"name\"" | check awk -F': "' '{print $2}' | check sed -e 's/",//g'` | |
REPOCOUNT=`echo $REPOLIST | wc -w` | |
echo "=== Repos in page $PAGE: `echo $REPOCOUNT` ===" | |
echo "=== List of Repos for page $PAGE ===" | |
echo "" && echo $REPOLIST | |
$GHBU_SILENT || echo "found `echo $REPOLIST | wc -w` repositories." | |
$GHBU_SILENT || (echo "" && echo "=== BACKING UP $REPOCOUNT REPOS FROM PAGE $PAGE ===" && echo "") | |
for REPO in $REPOLIST; do | |
$GHBU_SILENT || echo "Backing up ${GHBU_ORG}/${REPO}" | |
check ${GHBU_GIT_CLONE_CMD}${GHBU_ORG}/${REPO}.git ${GHBU_BACKUP_DIR}/${GHBU_ORG}-${REPO}-${TSTAMP}.git && tgz ${GHBU_BACKUP_DIR}/${GHBU_ORG}-${REPO}-${TSTAMP}.git | |
$GHBU_SILENT || echo "Backing up ${GHBU_ORG}/${REPO}.wiki (if any)" | |
${GHBU_GIT_CLONE_CMD}${GHBU_ORG}/${REPO}.wiki.git ${GHBU_BACKUP_DIR}/${GHBU_ORG}-${REPO}.wiki-${TSTAMP}.git 2>/dev/null && tgz ${GHBU_BACKUP_DIR}/${GHBU_ORG}-${REPO}.wiki-${TSTAMP}.git | |
$GHBU_SILENT || echo "Backing up ${GHBU_ORG}/${REPO} issues" | |
check curl --silent -u $GHBU_UNAME:$GHBU_PASSWD ${GHBU_API}/repos/${GHBU_ORG}/${REPO}/issues -q > ${GHBU_BACKUP_DIR}/${GHBU_ORG}-${REPO}.issues-${TSTAMP} && tgz ${GHBU_BACKUP_DIR}/${GHBU_ORG}-${REPO}.issues-${TSTAMP} | |
sleep 1 | |
done | |
if $GHBU_PRUNE_OLD; then | |
$GHBU_SILENT || (echo "" && echo "=== Checking $REPOCOUNT REPOS for repos older than ${GHBU_PRUNE_AFTER_N_DAYS} from page $PAGE ===" && echo "") | |
$GHBU_SILENT || echo "Pruning backup files ${GHBU_PRUNE_AFTER_N_DAYS} days old or older." | |
$GHBU_SILENT || echo "Found `find $GHBU_BACKUP_DIR -name '*.tar.gz' -mtime +$GHBU_PRUNE_AFTER_N_DAYS | wc -l` files to prune." | |
find $GHBU_BACKUP_DIR -name '*.tar.gz' -mtime +$GHBU_PRUNE_AFTER_N_DAYS -exec rm -fv {} > /dev/null \; | |
sleep 1 | |
fi | |
echo "" && echo "=== Moving to next page ===" | |
done | |
echo "=== Total number of pages was: $PAGE ===" | |
echo "=== Finishing up ===" | |
# NOTE: if you're backing up a *user's* repos, not an organizations, use this instead: | |
# REPOLIST=`check curl --silent -u $GHBU_UNAME:$GHBU_PASSWD ${GHBU_API}/user/repos -q | check grep "\"name\"" | check awk -F': "' '{print $2}' | check sed -e 's/",//g'` | |
$GHBU_SILENT || (echo "" && echo "=== DONE ===" && echo "") | |
$GHBU_SILENT || (echo "GitHub backup completed." && echo "") |
Github by default pulls 30 repos each API call, but you can change this by adding "?per_page=100" to the call. However, you can only call a maximum of 100 repos per page, so it is necessary to parse multiple pages to complete all repos.
I added an until loop into this script to allow for parsing of all repos (it stops when the number of repos in a page is less than 100, i.e. the next page will be blank).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
*You must have SSH keys configured for your user in order to use this