You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
319 lines
9.5 KiB
319 lines
9.5 KiB
#!/usr/bin/env bash |
|
set -euo pipefail |
|
|
|
# Gitea Repository Migration Script |
|
# Migrates Git repositories from local directory to Gitea server |
|
|
|
# Configuration (edit these values) |
|
GITEA_URL="${GITEA_URL:-http://localhost:3000}" # Gitea URL |
|
GITEA_TOKEN="${GITEA_TOKEN:-}" # Gitea API token (required) |
|
SOURCE_DIR="${SOURCE_DIR:-/home/mleku/Documents/github}" |
|
VPS_HOST="${VPS_HOST:-}" # SSH host for VPS (e.g., user@vps.example.com) |
|
USE_SSH="${USE_SSH:-false}" # Set to true to use SSH instead of HTTP for push |
|
DRY_RUN="${DRY_RUN:-false}" # Set to true for dry run |
|
|
|
# Colors |
|
GREEN='\033[0;32m' |
|
YELLOW='\033[1;33m' |
|
RED='\033[0;31m' |
|
BLUE='\033[0;34m' |
|
NC='\033[0m' |
|
|
|
# Stats |
|
TOTAL_REPOS=0 |
|
CREATED_REPOS=0 |
|
PUSHED_REPOS=0 |
|
SKIPPED_REPOS=0 |
|
FAILED_REPOS=0 |
|
|
|
echo -e "${GREEN}=== Gitea Repository Migration Script ===${NC}" |
|
echo "" |
|
|
|
# Validate configuration |
|
if [ -z "$GITEA_TOKEN" ]; then |
|
echo -e "${RED}Error: GITEA_TOKEN is required${NC}" |
|
echo "" |
|
echo "To generate a Gitea API token:" |
|
echo "1. Log in to Gitea at ${GITEA_URL}" |
|
echo "2. Go to Settings -> Applications -> Generate New Token" |
|
echo "3. Give it a name (e.g., 'migration') and select all scopes" |
|
echo "4. Copy the token and run:" |
|
echo " export GITEA_TOKEN='your-token-here'" |
|
echo "" |
|
exit 1 |
|
fi |
|
|
|
if [ ! -d "$SOURCE_DIR" ]; then |
|
echo -e "${RED}Error: Source directory does not exist: ${SOURCE_DIR}${NC}" |
|
exit 1 |
|
fi |
|
|
|
echo "Configuration:" |
|
echo " Gitea URL: ${GITEA_URL}" |
|
echo " Source directory: ${SOURCE_DIR}" |
|
echo " VPS Host: ${VPS_HOST:-<local installation>}" |
|
echo " Push method: $([ "$USE_SSH" = "true" ] && echo "SSH" || echo "HTTP with token")" |
|
echo " Dry run: ${DRY_RUN}" |
|
echo "" |
|
|
|
# Function to check if Gitea is accessible |
|
check_gitea() { |
|
echo -e "${YELLOW}Checking Gitea accessibility...${NC}" |
|
|
|
if ! curl -sf "${GITEA_URL}/api/v1/version" > /dev/null; then |
|
echo -e "${RED}Error: Cannot connect to Gitea at ${GITEA_URL}${NC}" |
|
echo "Make sure Gitea is running and accessible." |
|
exit 1 |
|
fi |
|
|
|
# Verify token |
|
if ! curl -sf -H "Authorization: token ${GITEA_TOKEN}" "${GITEA_URL}/api/v1/user" > /dev/null; then |
|
echo -e "${RED}Error: Invalid Gitea token${NC}" |
|
exit 1 |
|
fi |
|
|
|
GITEA_USER=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" "${GITEA_URL}/api/v1/user" | grep -o '"login":"[^"]*"' | cut -d'"' -f4) |
|
echo -e "${GREEN}✓ Connected to Gitea as: ${GITEA_USER}${NC}" |
|
} |
|
|
|
# Function to find all Git repositories |
|
find_repos() { |
|
echo -e "${YELLOW}Scanning for Git repositories...${NC}" |
|
mapfile -t REPOS < <(find "${SOURCE_DIR}" -maxdepth 2 -name .git -type d | sed 's|/.git$||') |
|
TOTAL_REPOS=${#REPOS[@]} |
|
echo -e "${GREEN}✓ Found ${TOTAL_REPOS} repositories${NC}" |
|
echo "" |
|
} |
|
|
|
# Function to get repository name from path |
|
get_repo_name() { |
|
basename "$1" |
|
} |
|
|
|
# Function to get repository description from Git config |
|
get_repo_description() { |
|
local repo_path="$1" |
|
local desc="" |
|
|
|
# Try to get description from .git/description |
|
if [ -f "${repo_path}/.git/description" ]; then |
|
desc=$(cat "${repo_path}/.git/description" | grep -v "^Unnamed repository" || true) |
|
fi |
|
|
|
# If empty, try README |
|
if [ -z "$desc" ] && [ -f "${repo_path}/README.md" ]; then |
|
desc=$(head -n 1 "${repo_path}/README.md" | sed 's/^#*\s*//') |
|
fi |
|
|
|
echo "$desc" |
|
} |
|
|
|
# Function to check if repo exists in Gitea |
|
repo_exists() { |
|
local repo_name="$1" |
|
curl -sf -H "Authorization: token ${GITEA_TOKEN}" \ |
|
"${GITEA_URL}/api/v1/repos/${GITEA_USER}/${repo_name}" > /dev/null |
|
} |
|
|
|
# Function to create repository in Gitea |
|
create_repo() { |
|
local repo_name="$1" |
|
local description="$2" |
|
|
|
if [ "$DRY_RUN" = "true" ]; then |
|
echo -e "${BLUE}[DRY RUN]${NC} Would create: ${repo_name}" |
|
return 0 |
|
fi |
|
|
|
local json_data=$(cat <<EOF |
|
{ |
|
"name": "${repo_name}", |
|
"description": "${description}", |
|
"private": false, |
|
"auto_init": false |
|
} |
|
EOF |
|
) |
|
|
|
if curl -sf -X POST \ |
|
-H "Authorization: token ${GITEA_TOKEN}" \ |
|
-H "Content-Type: application/json" \ |
|
-d "$json_data" \ |
|
"${GITEA_URL}/api/v1/user/repos" > /dev/null; then |
|
return 0 |
|
else |
|
return 1 |
|
fi |
|
} |
|
|
|
# Function to push repository to Gitea |
|
push_repo() { |
|
local repo_path="$1" |
|
local repo_name="$2" |
|
|
|
if [ "$DRY_RUN" = "true" ]; then |
|
echo -e "${BLUE}[DRY RUN]${NC} Would push: ${repo_name}" |
|
return 0 |
|
fi |
|
|
|
cd "$repo_path" |
|
|
|
# Check if gitea remote already exists |
|
if git remote | grep -q "^gitea$"; then |
|
git remote remove gitea 2>/dev/null || true |
|
fi |
|
|
|
# Build Git URL based on SSH or HTTP preference |
|
local git_url |
|
if [ "$USE_SSH" = "true" ]; then |
|
# Use SSH URL |
|
if [ -n "$VPS_HOST" ]; then |
|
# Extract host from VPS_HOST (format: user@host or just host) |
|
local ssh_host=$(echo "$VPS_HOST" | grep -oP '@\K.*' || echo "$VPS_HOST") |
|
git_url="git@${ssh_host}:${GITEA_USER}/${repo_name}.git" |
|
else |
|
# Use local host |
|
local gitea_host=$(echo "$GITEA_URL" | sed -E 's|https?://||' | cut -d':' -f1) |
|
git_url="git@${gitea_host}:${GITEA_USER}/${repo_name}.git" |
|
fi |
|
else |
|
# Use HTTP URL with token authentication (more reliable for automation) |
|
# Extract the base URL (http:// or https://) |
|
local protocol=$(echo "$GITEA_URL" | grep -oP '^https?') |
|
local url_without_protocol=$(echo "$GITEA_URL" | sed -E 's|^https?://||') |
|
|
|
# Build authenticated URL: http://username:token@host:port/username/repo.git |
|
git_url="${protocol}://${GITEA_USER}:${GITEA_TOKEN}@${url_without_protocol}/${GITEA_USER}/${repo_name}.git" |
|
fi |
|
|
|
# Add Gitea remote with URL |
|
git remote add gitea "$git_url" |
|
|
|
# Push all branches and tags |
|
local push_success=true |
|
|
|
# Push all branches |
|
if ! git push gitea --all 2>&1; then |
|
push_success=false |
|
fi |
|
|
|
# Push all tags |
|
if ! git push gitea --tags 2>&1; then |
|
push_success=false |
|
fi |
|
|
|
# Clean up - remove the remote to avoid storing token in git config (HTTP only) |
|
if [ "$USE_SSH" != "true" ]; then |
|
git remote remove gitea 2>/dev/null || true |
|
fi |
|
|
|
if [ "$push_success" = true ]; then |
|
return 0 |
|
else |
|
return 1 |
|
fi |
|
} |
|
|
|
# Main migration function |
|
migrate_repos() { |
|
echo -e "${GREEN}Starting migration...${NC}" |
|
echo "" |
|
|
|
local count=0 |
|
for repo_path in "${REPOS[@]}"; do |
|
count=$((count + 1)) |
|
local repo_name=$(get_repo_name "$repo_path") |
|
|
|
echo -e "${BLUE}[${count}/${TOTAL_REPOS}]${NC} Processing: ${repo_name}" |
|
|
|
# Check if repo already exists |
|
if repo_exists "$repo_name"; then |
|
echo -e " ${YELLOW}⚠${NC} Repository already exists in Gitea" |
|
SKIPPED_REPOS=$((SKIPPED_REPOS + 1)) |
|
|
|
# Ask if user wants to push updates |
|
if [ "$DRY_RUN" = "false" ]; then |
|
if push_repo "$repo_path" "$repo_name"; then |
|
echo -e " ${GREEN}✓${NC} Pushed updates" |
|
PUSHED_REPOS=$((PUSHED_REPOS + 1)) |
|
else |
|
echo -e " ${RED}✗${NC} Failed to push" |
|
FAILED_REPOS=$((FAILED_REPOS + 1)) |
|
fi |
|
fi |
|
echo "" |
|
continue |
|
fi |
|
|
|
# Get description |
|
local description=$(get_repo_description "$repo_path") |
|
if [ -n "$description" ]; then |
|
echo -e " Description: ${description}" |
|
fi |
|
|
|
# Create repository |
|
echo -e " Creating repository in Gitea..." |
|
if create_repo "$repo_name" "$description"; then |
|
echo -e " ${GREEN}✓${NC} Repository created" |
|
CREATED_REPOS=$((CREATED_REPOS + 1)) |
|
|
|
# Push repository |
|
echo -e " Pushing repository data..." |
|
if push_repo "$repo_path" "$repo_name"; then |
|
echo -e " ${GREEN}✓${NC} Repository pushed" |
|
PUSHED_REPOS=$((PUSHED_REPOS + 1)) |
|
else |
|
echo -e " ${RED}✗${NC} Failed to push" |
|
FAILED_REPOS=$((FAILED_REPOS + 1)) |
|
fi |
|
else |
|
echo -e " ${RED}✗${NC} Failed to create repository" |
|
FAILED_REPOS=$((FAILED_REPOS + 1)) |
|
fi |
|
|
|
echo "" |
|
done |
|
} |
|
|
|
# Print summary |
|
print_summary() { |
|
echo "" |
|
echo -e "${GREEN}=== Migration Summary ===${NC}" |
|
echo "Total repositories: ${TOTAL_REPOS}" |
|
echo "Created: ${CREATED_REPOS}" |
|
echo "Pushed: ${PUSHED_REPOS}" |
|
echo "Skipped: ${SKIPPED_REPOS}" |
|
echo "Failed: ${FAILED_REPOS}" |
|
echo "" |
|
|
|
if [ "$DRY_RUN" = "true" ]; then |
|
echo -e "${YELLOW}This was a dry run. No changes were made.${NC}" |
|
echo "Run without DRY_RUN=true to perform actual migration." |
|
elif [ $FAILED_REPOS -eq 0 ]; then |
|
echo -e "${GREEN}✓ Migration completed successfully!${NC}" |
|
echo "" |
|
echo "Visit your Gitea instance at: ${GITEA_URL}" |
|
else |
|
echo -e "${YELLOW}⚠ Migration completed with some failures${NC}" |
|
echo "Check the output above for details on failed repositories." |
|
fi |
|
} |
|
|
|
# Main execution |
|
check_gitea |
|
find_repos |
|
|
|
# Confirm before proceeding |
|
if [ "$DRY_RUN" = "false" ]; then |
|
echo -e "${YELLOW}Ready to migrate ${TOTAL_REPOS} repositories.${NC}" |
|
read -p "Continue? (y/N) " -n 1 -r |
|
echo |
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then |
|
echo "Migration cancelled." |
|
exit 0 |
|
fi |
|
echo "" |
|
fi |
|
|
|
migrate_repos |
|
print_summary
|
|
|