Sync GitHub → GitLab #109
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Sync GitHub → GitLab | |
| # Trigger this workflow nightly at 03:00 UTC and also allow manual dispatch | |
| on: | |
| schedule: | |
| - cron: '0 3 * * *' # Adjust cron schedule as needed | |
| workflow_dispatch: # Allows manual run from Actions tab | |
| jobs: | |
| mirror: | |
| runs-on: ubuntu-latest # Use the latest Ubuntu runner | |
| # Environment variables—update these for your own setup: | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GH_PAT }} # Required: GitHub PAT with “repo” scope | |
| GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} # Required: GitLab PAT with api+write_repository | |
| GITLAB_GROUP_ID: ${{ secrets.GITLAB_GROUP_ID }} # Required: Numeric GitLab group ID (e.g., “123456”) | |
| GITLAB_GROUP_PATH: mao1910-group # GitLab namespace path (public) — change to your group | |
| GITHUB_USER: "mao1910,le-fork" # Comma-separated GitHub usernames to mirror | |
| steps: | |
| - uses: actions/checkout@v3 | |
| # Check out the workflow repository so we can run the Python script | |
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.x' # Use Python 3 | |
| - name: Install dependencies | |
| run: | | |
| pip install --upgrade pip | |
| pip install requests # Required by mirror_repos.py | |
| - name: Create GitLab projects & configure pull mirrors | |
| run: python sync_repos.py | |
| # Runs the Python script that: | |
| # 1) Fetches all repos (private, owned, public forks) | |
| # 2) Creates any missing GitLab projects under your group | |
| # 3) Configures GitLab pull mirrors for each repo | |
| - name: Install jq | |
| run: sudo apt-get update && sudo apt-get install -y jq | |
| # jq is used in the fallback step to parse JSON | |
| - name: Mirror via push (fallback) | |
| shell: bash | |
| run: | | |
| # Ensure commits are attributed to “GitHub Actions” | |
| git config --global user.name "GitHub Actions" | |
| git config --global user.email "actions@github.com" | |
| # Prepare combined list of repos: | |
| owner="${GITHUB_USER%%,*}" | |
| # 1) All private & owned repos | |
| owned=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ | |
| "https://api.github.com/user/repos?per_page=100&sort=updated" \ | |
| | jq -r '.[] | "\(.owner.login) \(.name)"') | |
| # 2) All public repos under the first username (including forks) | |
| public=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ | |
| "https://api.github.com/users/${owner}/repos?per_page=100&sort=updated" \ | |
| | jq -r '.[] | "\(.owner.login) \(.name)"') | |
| # Combine and dedupe | |
| combined=$(printf "%s\n%s" "$owned" "$public" | sort -u) | |
| # Clone & push each repo that exists in GitLab | |
| while read user repo; do | |
| # Skip hidden repos (dot-prefixed) | |
| if [[ "$repo" == .* ]]; then | |
| continue | |
| fi | |
| # Check GitLab for this project by URL-encoded path | |
| enc=$(echo "${GITLAB_GROUP_PATH}/${repo}" | sed 's#/#%2F#g') | |
| code=$(curl -s -o /dev/null -w '%{http_code}' \ | |
| -H "PRIVATE-TOKEN: $GITLAB_TOKEN" \ | |
| "https://gitlab.com/api/v4/projects/${enc}") | |
| if [[ "$code" != "200" ]]; then | |
| echo "Skipping $repo (not found in GitLab)" | |
| continue | |
| fi | |
| # Mirror via Git push: | |
| git clone --mirror "https://oauth2:${GITHUB_TOKEN}@github.com/${user}/${repo}.git" | |
| cd "${repo}.git" | |
| git push --mirror "https://oauth2:${GITLAB_TOKEN}@gitlab.com/${GITLAB_GROUP_PATH}/${repo}.git" | |
| cd .. | |
| rm -rf "${repo}.git" | |
| done <<< "$combined" | |
| # End of fallback push step |