Created
March 20, 2025 17:24
-
-
Save dzogrim/695fce63006ad8fdf7b79e76a9f12d1b to your computer and use it in GitHub Desktop.
This script migrates repositories from a source Gitea instance to another Gitea instance
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
#!/usr/bin/env bash | |
# This script migrates repositories from a source Gitea instance to another Gitea instance. | |
# It clones all personal repositories from the source using HTTPS and pushes them to the | |
# destination using SSH, with an option for a default dry-run mode. | |
# | |
# USAGE: | |
# ./gitea-migr.sh # Runs in dry-run mode (NO actual cloning or pushing) | |
# ./gitea-migr.sh --no-dry-run # Performs the actual migration (cloning and pushing) | |
# πΉ Configuration | |
GITEA_SOURCE="https://" | |
TOKEN_SOURCE="" | |
USER_SOURCE="" | |
DEST_DOMAIN="git.toto.com" | |
GITEA_DEST="https://$DEST_DOMAIN" | |
GITEA_DEST_SSH="gitea@$DEST_DOMAIN" | |
TOKEN_DEST="" | |
USER_DEST="" | |
# Get the ID with: | |
# > curl --insecure -H 'Authorization: token $TOKEN_DEST' "$GITEA_DEST/api/v1/user" | jq '.id' | |
UID_DEST="55" | |
ERRORS=0 | |
BACKUP_DIR="./gitea_migration" | |
DRY_RUN=true | |
# πΉ Colors | |
RED='\033[0;31m' | |
GREEN='\033[0;32m' | |
YELLOW='\033[1;33m' | |
NC='\033[0m' | |
# πΉ Parse arguments | |
for arg in "$@"; do | |
if [[ "$arg" == "--no-dry-run" ]]; then | |
DRY_RUN=false | |
fi | |
done | |
# πΉ Create backup directory | |
mkdir -p "$BACKUP_DIR" | |
# πΉ Fetch all repositories from source Gitea | |
repos=$(curl --insecure -s -H "Authorization: token $TOKEN_SOURCE" \ | |
"$GITEA_SOURCE/api/v1/user/repos?visibility=all&type=all&limit=300" | \ | |
jq -r '.[] | select(.owner.login=="'"$USER_SOURCE"'") | .clone_url') | |
for repo in $repos; do | |
repo_name=$(basename "$repo" .git) | |
repo_path="$BACKUP_DIR/$repo_name.git" | |
# β Step 1: Clone repository from source Gitea (using HTTPS) | |
if [[ -d "$repo_path" ]]; then | |
echo -e "${YELLOW}πΉ $repo_name already cloned, skipping...${NC}" | |
else | |
echo -e "${GREEN}β¬οΈ Cloning $repo_name from $GITEA_SOURCE (HTTPS)...${NC}" | |
if [[ "$DRY_RUN" == "false" ]]; then | |
git clone --mirror "$repo" "$repo_path" | |
if [[ $? -ne 0 ]]; then | |
echo -e "${RED}β Failed to clone $repo_name.${NC}" | |
ERRORS=$((ERRORS+1)) | |
continue | |
fi | |
else | |
echo -e " π ${YELLOW}Dry-run: skipping clone.${NC}" | |
fi | |
fi | |
done | |
echo -e "${GREEN}β All repositories downloaded.${NC}" | |
# πΉ Push repositories to destination Gitea | |
for repo_path in "$BACKUP_DIR"/*.git; do | |
repo_name=$(basename "$repo_path" .git) | |
# β Step 2: Check if repository exists on destination (handling ghost repositories) | |
repo_check=$(curl --insecure -s -H "Authorization: token $TOKEN_DEST" \ | |
"$GITEA_DEST/api/v1/repos/$USER_DEST/$repo_name") | |
if echo "$repo_check" | jq -e '.message == "Not Found"' >/dev/null; then | |
echo -e "${GREEN}π Repository does not exist, creating it...${NC}" | |
if [[ "$DRY_RUN" == "false" ]]; then | |
curl -X POST -H "Content-Type: application/json" \ | |
-H "Authorization: token $TOKEN_DEST" \ | |
-d '{ | |
"name": "'"$repo_name"'", | |
"private": true, | |
"uid": '"$UID_DEST"' | |
}' "$GITEA_DEST/api/v1/user/repos" | |
if [[ $? -ne 0 ]]; then | |
echo -e "${RED}β Failed to create repository $repo_name.${NC}" | |
ERRORS=$((ERRORS+1)) | |
continue | |
fi | |
else | |
echo -e " π ${YELLOW}Dry-run: skipping repository creation.${NC}" | |
fi | |
else | |
echo -e "${YELLOW}π« Repository $repo_name already exists on destination, skipping creation but still trying to push.${NC}" | |
fi | |
# β Step 3: Check if the repository exists locally before pushing | |
if [[ ! -d "$repo_path" ]]; then | |
echo -e "${RED}β Error: Local repository folder '$repo_path' does not exist. Skipping push.${NC}" | |
ERRORS=$((ERRORS+1)) | |
continue | |
fi | |
# β Step 4: Push repository to destination (using SSH) | |
echo -e "${GREEN}β¬οΈ Pushing $repo_name to $GITEA_DEST_SSH...${NC}" | |
if [[ "$DRY_RUN" == "false" ]]; then | |
git -C "$repo_path" remote set-url origin "$GITEA_DEST_SSH:$USER_DEST/$repo_name.git" | |
# Push and handle errors | |
push_output=$(git -C "$repo_path" push --mirror 2>&1) | |
if echo "$push_output" | grep -q "Gitea: Push to create is not enabled"; then | |
echo -e "${RED}β Failed to push $repo_name: Push to create is not enabled.${NC}" | |
ERRORS=$((ERRORS+1)) | |
continue | |
elif echo "$push_output" | grep -q "fatal"; then | |
echo -e "${RED}β Failed to push $repo_name: $push_output${NC}" | |
ERRORS=$((ERRORS+1)) | |
continue | |
fi | |
else | |
echo -e " π ${YELLOW}Dry-run: skipping push.${NC}" | |
fi | |
echo -e "${GREEN}β $repo_name migration completed.${NC}" | |
done | |
# πΉ Final Message | |
if [[ "$DRY_RUN" == "true" ]]; then | |
echo -e "${YELLOW}β οΈ Dry-run mode: No actual changes were made.${NC}" | |
echo -e "${YELLOW} To perform the migration, run: ./gitea-migr.sh --no-dry-run${NC}" | |
elif [[ "$ERRORS" -gt 0 ]]; then | |
echo -e "${RED}β οΈ Migration completed with $ERRORS errors.${NC}" | |
exit 1 | |
else | |
echo -e "${GREEN}π Migration complete! All repositories have been successfully migrated.${NC}" | |
exit 0 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment