159 lines
5.3 KiB
Bash
159 lines
5.3 KiB
Bash
#!/bin/bash
|
|
|
|
#clear the screen
|
|
clear
|
|
|
|
echo "This script will mirror a GitHub user's public repositories into gitea and assign them to a specific organiztion within Gitea."
|
|
echo "Before starting, you will need to create:"
|
|
echo "#1) A token with the appropriate rights (at least write:repository)"
|
|
echo "#2) An organization within Gitea to place all mirrored repositories into"
|
|
echo "If you enter a GitHub API token, you will be able to mirror non-public projects. This is optional!"
|
|
echo ""
|
|
echo "This application supports using a config file for user input variables"
|
|
|
|
#Config file:
|
|
CONFIG_FILE="migration_config.conf"
|
|
|
|
# Migration state file:
|
|
STATE_FILE="migration_state.txt"
|
|
|
|
#Check for config file. If none, ask user for input.
|
|
if [[ -f $CONFIG_FILE ]]; then
|
|
source $CONFIG_FILE
|
|
echo "Config file found! Continuing without asking for user input."
|
|
else
|
|
echo "Configuration file not found! Using manual input..."
|
|
|
|
### BEGIN User input ###
|
|
echo -n "Enter the GitHub username to migrate: "
|
|
read GITHUB_USERNAME
|
|
|
|
echo -n "Enter your GitHub Access Token (optional&hidden): "
|
|
read -s GITHUB_TOKEN
|
|
echo
|
|
|
|
echo -n "Enter your Gitea username: "
|
|
read GITEA_USER
|
|
|
|
echo -n "Enter your Gitea token (input will be hidden): "
|
|
read -s GITEA_TOKEN
|
|
echo
|
|
|
|
echo -n "Enter your Gitea URL (default: https://code.flatironnetworks.com): "
|
|
read GITEA_URL
|
|
GITEA_URL=${GITEA_URL:-https://code.flatironnetworks.com}
|
|
|
|
echo -n "Enter the Gitea organization to migrate into: "
|
|
read GITEA_ORG
|
|
### END User input ###
|
|
fi
|
|
|
|
# Check GitHub rate limit
|
|
check_rate_limit() {
|
|
local headers
|
|
local remaining
|
|
|
|
if [ -z "$GITHUB_TOKEN" ]; then
|
|
headers=$(curl -sI "https://api.github.com/users/$USERNAME/repos")
|
|
else
|
|
headers=$(curl -sI -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/users/$USERNAME/repos")
|
|
fi
|
|
|
|
remaining=$(echo "$headers" | grep X-RateLimit-Remaining: | awk '{print $2}')
|
|
|
|
# If the rate limit headers are not present, assume we can continue without delay.
|
|
if [ -z "$remaining" ]; then
|
|
return
|
|
fi
|
|
|
|
while [ "$remaining" -le 5 ]; do
|
|
echo "Rate limited by GitHub. Waiting for 10 minutes..."
|
|
sleep 600
|
|
|
|
if [ -z "$GITHUB_TOKEN" ]; then
|
|
remaining=$(curl -sI "https://api.github.com/users/$USERNAME/repos" | grep X-RateLimit-Remaining: | awk '{print $2}')
|
|
else
|
|
remaining=$(curl -sI -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/users/$USERNAME/repos" | grep X-RateLimit-Remaining: | awk '{print $2}')
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Check if Gitea UID is valid
|
|
USER_ID=$(curl -s "$GITEA_URL/api/v1/users/$GITEA_USER" -H "authorization: token $GITEA_TOKEN" | jq '.id')
|
|
if [ -z "$USER_ID" ] || [ "$USER_ID" == "null" ]; then
|
|
echo "Failed to retrieve UID for $GITEA_USER. Please check your Gitea token and username."
|
|
exit 1
|
|
fi
|
|
|
|
# If the state file exists, read the last page processed
|
|
if [ -f "$STATE_FILE" ]; then
|
|
page=$(cat $STATE_FILE)
|
|
else
|
|
page=1
|
|
fi
|
|
|
|
# Fetch repositories from GitHub
|
|
fetch_github_repos() {
|
|
local page=$1
|
|
if [ -z "$GITHUB_TOKEN" ]; then
|
|
echo $(curl -s "https://api.github.com/users/$USERNAME/repos?per_page=100&page=$page")
|
|
else
|
|
echo $(curl -s "https://api.github.com/user/repos?visibility=all&per_page=100&page=$page" -H "Authorization: token $GITHUB_TOKEN")
|
|
fi
|
|
}
|
|
|
|
#Main script:
|
|
while : ; do
|
|
check_rate_limit
|
|
|
|
repos_json=$(fetch_github_repos $page)
|
|
repos=$(echo "$repos_json" | jq -r '.[].clone_url')
|
|
|
|
if [ -z "$GITHUB_TOKEN" ]; then
|
|
repos=$(curl -s "https://api.github.com/users/$USERNAME/repos?per_page=100&page=$page" | jq -r '.[].clone_url')
|
|
else
|
|
repos=$(curl -s "https://api.github.com/users/$USERNAME/repos?per_page=100&page=$page" -H "Authorization: token $GITHUB_TOKEN" | jq -r '.[].clone_url')
|
|
fi
|
|
|
|
if [ -z "$repos" ]; then
|
|
echo "Migration complete!"
|
|
rm $STATE_FILE
|
|
break
|
|
fi
|
|
|
|
for repo_url in $repos; do
|
|
repo_name=$(echo $repo_url | awk -F'/' '{print $NF}' | sed 's/.git$//')
|
|
|
|
# Check if repository is private and modify the clone URL accordingly
|
|
github_response=$(curl -s "https://api.github.com/repos/$USERNAME/$repo_name" -H "Authorization: token $GITHUB_TOKEN")
|
|
is_private=$(echo "$github_response" | jq '.private')
|
|
if [ "$is_private" == "true" ]; then
|
|
repo_url="https://$GITHUB_TOKEN@github.com/$USERNAME/$repo_name.git"
|
|
fi
|
|
|
|
echo "Transferring repository: $repo_name..."
|
|
|
|
response=$(curl -X POST "$GITEA_URL/api/v1/repos/migrate" \
|
|
-H "accept: application/json" \
|
|
-H "authorization: token $GITEA_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"clone_addr": "'$repo_url'",
|
|
"mirror": true,
|
|
"private": false,
|
|
"repo_name": "'$repo_name'",
|
|
"uid": '$USER_ID',
|
|
"repo_owner": "'$REPO_OWNER'"
|
|
}' -w "%{http_code}" -s -o /tmp/gitea_response.txt)
|
|
|
|
if [ "$response" == "409" ]; then
|
|
echo "Repository $repo_name already exists in $REPO_OWNER. Skipping."
|
|
elif [ "$response" != "201" ]; then
|
|
echo "Error migrating $repo_name. HTTP status: $response"
|
|
cat /tmp/gitea_response.txt
|
|
fi
|
|
done
|
|
|
|
echo $page > $STATE_FILE
|
|
page=$((page + 1))
|
|
done |