diff --git a/scripts/migrate_github_org_to_gitea.sh b/scripts/migrate_github_org_to_gitea.sh new file mode 100644 index 00000000..95c7098f --- /dev/null +++ b/scripts/migrate_github_org_to_gitea.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash +set -euo pipefail +set +x + +# ====== Fill these values before running ====== +GITHUB_TOKEN="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" +GITEA_TOKEN="1928666b31ab82a12a87a9050c764d0e534e2f21" + +GH_ORG="ai-camerist" +GITEA_OWNER="banlv" +GITEA_URL="https://gitea.banlv-ai.com" + +# Set to true if you want Gitea to keep pulling from GitHub after migration. +MIRROR=false +MIRROR_INTERVAL="8h" + +# Migrated repository options. +MIGRATE_ISSUES=true +MIGRATE_LABELS=true +MIGRATE_MILESTONES=true +MIGRATE_PULL_REQUESTS=true +MIGRATE_RELEASES=true +MIGRATE_WIKI=true +MIGRATE_LFS=true + +require_cmd() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Missing required command: $1" >&2 + exit 1 + fi +} + +api_get() { + curl -fsSL \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${GITHUB_TOKEN}" \ + "$1" +} + +gitea_repo_status() { + local repo_name="$1" + + curl -sS -o /dev/null -w "%{http_code}" \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${repo_name}" +} + +migrate_repo() { + local repo_json="$1" + local repo_name clone_url description private status payload + + repo_name="$(echo "$repo_json" | jq -r '.name')" + clone_url="$(echo "$repo_json" | jq -r '.clone_url')" + description="$(echo "$repo_json" | jq -r '.description // ""')" + private="$(echo "$repo_json" | jq -r '.private')" + + echo "==> Migrating ${GH_ORG}/${repo_name}" + + status="$(gitea_repo_status "$repo_name")" + if [ "$status" = "200" ]; then + echo " Skip existing repository: ${GITEA_OWNER}/${repo_name}" + return 0 + fi + + payload="$(jq -n \ + --arg clone_addr "$clone_url" \ + --arg repo_name "$repo_name" \ + --arg repo_owner "$GITEA_OWNER" \ + --arg description "$description" \ + --arg auth_token "$GITHUB_TOKEN" \ + --arg mirror_interval "$MIRROR_INTERVAL" \ + --argjson private "$private" \ + --argjson mirror "$MIRROR" \ + --argjson issues "$MIGRATE_ISSUES" \ + --argjson labels "$MIGRATE_LABELS" \ + --argjson milestones "$MIGRATE_MILESTONES" \ + --argjson pull_requests "$MIGRATE_PULL_REQUESTS" \ + --argjson releases "$MIGRATE_RELEASES" \ + --argjson wiki "$MIGRATE_WIKI" \ + --argjson lfs "$MIGRATE_LFS" \ + '{ + service: "github", + clone_addr: $clone_addr, + repo_name: $repo_name, + repo_owner: $repo_owner, + description: $description, + auth_token: $auth_token, + private: $private, + issues: $issues, + labels: $labels, + milestones: $milestones, + pull_requests: $pull_requests, + releases: $releases, + wiki: $wiki, + lfs: $lfs, + mirror: $mirror, + mirror_interval: $mirror_interval + }')" + + curl -fsSL -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "$payload" \ + "${GITEA_URL}/api/v1/repos/migrate" >/dev/null + + echo " Done: ${GITEA_OWNER}/${repo_name}" +} + +main() { + require_cmd curl + require_cmd jq + + if [ "$GITHUB_TOKEN" = "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ]; then + echo "Please edit GITHUB_TOKEN in this script first." >&2 + exit 1 + fi + + if [ "$GITEA_TOKEN" = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ]; then + echo "Please edit GITEA_TOKEN in this script first." >&2 + exit 1 + fi + + local page repos_json count total + page=1 + total=0 + + while true; do + echo "Fetching GitHub repositories page ${page}..." + + repos_json="$(api_get "https://api.github.com/orgs/${GH_ORG}/repos?type=all&per_page=100&page=${page}&sort=full_name&direction=asc")" + count="$(echo "$repos_json" | jq 'length')" + + if [ "$count" -eq 0 ]; then + break + fi + + while IFS= read -r repo; do + migrate_repo "$repo" + total=$((total + 1)) + done < <(echo "$repos_json" | jq -c '.[]') + + page=$((page + 1)) + done + + echo "All done. Processed repositories: ${total}" +} + +main "$@"