commit 6ba5b43dda21eac6c3dad885986c00794a417d80 Author: andwati Date: Tue Aug 18 15:59:40 2026 +0300 initial commit diff --git a/.github/workflows/mirror-to-gitea.yml b/.github/workflows/mirror-to-gitea.yml new file mode 100644 index 0000000..ae01943 --- /dev/null +++ b/.github/workflows/mirror-to-gitea.yml @@ -0,0 +1,27 @@ +name: Mirror GitHub repos to Gitea + +on: + schedule: + # Runs at 00:00 UTC daily. Adjust if you want midnight EAT (UTC+3) instead — see note below. + - cron: "0 0 * * *" + workflow_dispatch: {} # allows manual trigger from the Actions tab too + +jobs: + mirror: + runs-on: ubuntu-latest + steps: + - name: Checkout (for the script) + uses: actions/checkout@v4 + + - name: Install jq + run: sudo apt-get update && sudo apt-get install -y jq + + - name: Mirror all repos to Gitea + env: + GITHUB_TOKEN: ${{ secrets.GH_PAT }} + GITEA_URL: ${{ secrets.GITEA_URL }} + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + GITEA_OWNER: ${{ secrets.GITEA_OWNER }} + run: | + chmod +x ./mirror-github-to-gitea.sh + ./mirror-github-to-gitea.sh diff --git a/mirror-github-to-gitea.sh b/mirror-github-to-gitea.sh new file mode 100644 index 0000000..82bd6f6 --- /dev/null +++ b/mirror-github-to-gitea.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +set -euo pipefail + +# --- Config --- +GITHUB_USER="andwati" +GITHUB_TOKEN="${GITHUB_TOKEN:?Set GITHUB_TOKEN env var (PAT with repo read access, incl. private repos)}" +GITEA_URL="${GITEA_URL:?Set GITEA_URL env var, e.g. https://gitea.andwati.com}" +GITEA_TOKEN="${GITEA_TOKEN:?Set GITEA_TOKEN env var (Gitea access token, repo scope)}" +GITEA_OWNER="${GITEA_OWNER:-andwati}" +MIRROR_INTERVAL="${MIRROR_INTERVAL:-8h0m0s}" + +# --- Fetch all repos for the GitHub user (paginated), including private ones --- +# Uses /user/repos (authenticated, sees private repos owned by the token's user) +# rather than /users/{user}/repos (public-only). +page=1 +repos_json="[]" +while :; do + resp=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \ + "https://api.github.com/user/repos?per_page=100&page=${page}&affiliation=owner") + count=$(echo "$resp" | jq 'length') + [ "$count" -eq 0 ] && break + repos_json=$(jq -s '.[0] + .[1]' <(echo "$repos_json") <(echo "$resp")) + page=$((page + 1)) +done + +total=$(echo "$repos_json" | jq 'length') +echo "Found ${total} repos on GitHub for ${GITHUB_USER}" + +echo "$repos_json" | jq -c '.[] | {name, private}' | while read -r entry; do + name=$(echo "$entry" | jq -r '.name') + private=$(echo "$entry" | jq -r '.private') + + echo "Processing: ${name} (private=${private})" + + # Check if it already exists in Gitea + existing_status=$(curl -s -o /dev/null -w "%{http_code}" \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${name}") + + if [ "$existing_status" == "200" ]; then + echo " Already mirrored, skipping migration (existing mirror syncs on its own schedule)." + continue + fi + + http_code=$(curl -s -o /tmp/gitea_migrate_resp.json -w "%{http_code}" \ + -X POST "${GITEA_URL}/api/v1/repos/migrate" \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d @- <