#!/bin/bash ######################################################################### # GitHub to Gitea Starred Repository Mirror # # This script automatically mirrors your GitHub starred repositories to # a Gitea instance. # # - Fetches all repositories starred on GitHub # - Recreate organization / owner (as org) to mirror repos into # - Creates mirror repositories on Gitea for each starred GitHub repo # - Handles pagination for users with many starred repositories # # Requirements: # - GitHub Personal Access Token with permissions to read starred repos # - Gitea Access Token with organization and repository write access # - curl, jq installed on your system ######################################################################### # Read variables from .env file. if ! [ -f ".env" ]; then echo "No .env file found to source variables from." echo "Please create a .env file with the variables:" echo 'GITHUB_USER="jane_doe"' echo 'GITHUB_TOKEN="github_pat_..."' echo 'GITEA_URL="https://gitea.example.com"' echo 'GITEA_USER="jane_doe"' echo 'GITEA_TOKEN="..."' exit 1 fi source '.env' # Make sure all variables are set. required_variables=("GITHUB_USER" "GITHUB_TOKEN" "GITEA_URL" "GITEA_USER" "GITEA_TOKEN") for variable_name in "${required_variables[@]}"; do if ! [ -v $variable_name ]; then echo "$variable_name is not set." exit 1 fi done # $1: method (e.g. POST) # $2: endpoint (e.g. users/some_user/starred?page=1) # $3: data (e.g. '{"key": "value"}') function github_query() { method=$1 endpoint=$2 if [ -v 3 ]; then data=$3 curl -s -X "$method" -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3+json" -d "$data" "https://api.github.com/$endpoint" else curl -s -X "$method" -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/$endpoint" fi } function gitea_query() { method=$1 endpoint=$2 if [ -v 3 ]; then data=$3 curl -s -X "$method" -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d "$data" "$GITEA_URL/api/v1/$endpoint" else curl -s -X "$method" -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" "$GITEA_URL/api/v1/$endpoint" fi } echo "Fetching Gitea organisations..." page=1 existing_orgs=() while true; do orgs_page=$(gitea_query "GET" "orgs?page=$page") if [ "$(echo "$orgs_page" | jq '. | length')" = "0" ]; then break fi page=$((page + 1)) while read -r org; do name=$(echo "$org" | jq -r '.name') existing_orgs+=("$name") done < <(echo "$orgs_page" | jq -c '.[]') done echo "Found ${#existing_orgs[@]} organisations on Gitea." # Flatten for regex-check below. existing_orgs=$(printf "%s\n" "${existing_orgs[@]}") echo "Fetching Gitea repositories..." page=1 existing_repos=() while true; do repos_page=$(gitea_query "GET" "repos/search?page=$page") if [ "$(echo "$repos_page" | jq '.data | length')" = "0" ]; then break fi page=$((page + 1)) while read -r repo; do name=$(echo "$repo" | jq -r '.full_name') existing_repos+=("$name") done < <(echo "$repos_page" | jq -c '.data[]') done echo "Found ${#existing_repos[@]} repositories on Gitea." # Flatten for regex-check below. existing_repos=$(printf "%s\n" "${existing_repos[@]}") echo "Fetching starred repositories from GitHub..." page=1 while true; do starred_page=$(github_query "GET" "users/$GITHUB_USER/starred?per_page=100&page=$page") if [ "$(echo "$starred_page" | jq '. | length')" = "0" ]; then break fi page=$((page + 1)) while read -r repo; do # Read values in one query for efficiency, needs readarray because of multi-line output. readarray -t repo_info < <(echo "$repo" | jq -r '.name, .clone_url, .description // "", .owner.login') name=${repo_info[0]} url=${repo_info[1]} description=${repo_info[2]} owner=${repo_info[3]} # Skip if repo already exists in Gitea. if echo "$existing_repos" | grep -q "^$owner/$name$"; then continue fi echo "Found new repository to mirror: $owner/$name..." if ! echo "$existing_orgs" | grep -q "^$owner$"; then echo "Creating new organisation: $owner..." query=$(jq -n --arg name "$owner" '{"username": $name, "website": "https://github.com/\($name)"}') result=$(gitea_query "POST" "orgs" "$query") id=$(echo "$result" | jq -r '.id') if [ -z "$id" ] || [ "$id" = "null" ]; then echo "Error creating Gitea organisation: $result" continue fi fi query=$(jq -n --arg name "$name" --arg url "$url" --arg description "$description" --arg owner "$owner" --arg user "$GITHUB_USER" --arg token "$GITHUB_TOKEN" '{ "clone_addr": $url, "repo_owner": $owner, "repo_name": $name, "description": $description, "mirror": true, "private": false, "lfs": true, "auth_username": $user, "auth_token": $token }') result=$(gitea_query "POST" "repos/migrate" "$query") gitea_repo_url=$(echo "$result" | jq -r '.clone_url') if [ -z "$gitea_repo_url" ] || [ "$gitea_repo_url" = "null" ]; then echo "Error creating mirror: $result" continue fi echo "Successfully set up mirror for $owner/$name." done < <(echo "$starred_page" | jq -c '.[]') done echo "Mirroring process completed!"