Skip to content

Instantly share code, notes, and snippets.

@spanthetree
Forked from rodw/backup-github.sh
Last active August 29, 2015 14:18
Show Gist options
  • Save spanthetree/f583667f97813b863171 to your computer and use it in GitHub Desktop.
Save spanthetree/f583667f97813b863171 to your computer and use it in GitHub Desktop.
#!/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 "")
@spanthetree
Copy link
Author

*You must have SSH keys configured for your user in order to use this

@spanthetree
Copy link
Author

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