Skip to content

Instantly share code, notes, and snippets.

@johannschopplich
Created August 1, 2025 11:49
Show Gist options
  • Save johannschopplich/964362450360b3fb1d1e5c657446aa12 to your computer and use it in GitHub Desktop.
Save johannschopplich/964362450360b3fb1d1e5c657446aa12 to your computer and use it in GitHub Desktop.
GitHub backup script
#!/usr/bin/env bash
# GitHub Backup Script
# Backs up all accessible repositories (public & private) to organized ZIP files
set -euo pipefail
# Terminal colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
NC='\033[0m'
# Check dependencies
for cmd in curl jq git zip; do
command -v "$cmd" >/dev/null 2>&1 || {
echo -e "${RED}❌ Missing dependency: $cmd${NC}" >&2
exit 1
}
done
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
echo -e "${RED}❌ Please export GITHUB_TOKEN first${NC}" >&2
exit 1
fi
# Configuration
PER_PAGE=100
TODAY=$(date +%F)
BACKUP_DIR="github-backups/$TODAY"
mkdir -p "$BACKUP_DIR"
echo -e "${BLUE}πŸ“¦ Output directory:${NC} $BACKUP_DIR"
gh_api() {
local url=$1
if ! curl -sSfL \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "User-Agent: github-backup-script" \
"$url"; then
echo -e "${RED}❌ API request failed: $url${NC}" >&2
return 1
fi
}
list_repos() {
local page=1
local repos=()
while true; do
local resp
resp=$(gh_api "https://api.github.com/user/repos?per_page=$PER_PAGE&page=$page&type=all")
local names
names=$(echo "$resp" | jq -r '.[] | select(.archived == false) | .full_name')
if [[ -z "$names" ]]; then
break
fi
while IFS= read -r name; do
[[ -n "$name" ]] && repos+=("$name")
done <<< "$names"
((page++))
done
printf '%s\n' "${repos[@]}"
}
backup_repo() {
local full_name=$1
local owner repo
owner=${full_name%%/*}
repo=${full_name##*/}
local owner_dir="$BACKUP_DIR/$owner"
mkdir -p "$owner_dir"
local repo_dir="$owner_dir/$repo"
echo -e "${YELLOW}πŸ”„ Cloning ${full_name}…${NC}"
git clone "https://github.com/${full_name}.git" "$repo_dir" --quiet --depth=1
echo -e "${GREEN}πŸ’Ύ Archiving ${full_name} β†’ ${owner}/${repo}.zip${NC}"
(cd "$repo_dir" && zip -qr "../${repo}.zip" .)
rm -rf "$repo_dir"
}
echo -e "${BLUE}πŸ”‘ Authenticating & fetching repo list…${NC}"
ALL_REPOS=()
while IFS= read -r repo; do
[[ -n "$repo" ]] && ALL_REPOS+=("$repo")
done < <(list_repos)
TOTAL=${#ALL_REPOS[@]}
echo -e "${BLUE}πŸ“š Found $TOTAL repositories${NC}"
COUNT=0
for r in "${ALL_REPOS[@]}"; do
backup_repo "$r" && ((COUNT++))
done
echo -e "${GREEN}βœ… Backup complete: ${COUNT}/${TOTAL} repos archived${NC}"
echo -e "${BLUE}πŸ“ Everything stored in $BACKUP_DIR${NC}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment