diff --git a/.github/workflows/mirror-repositories.yml b/.github/workflows/mirror-repositories.yml new file mode 100644 index 0000000..6e28b1c --- /dev/null +++ b/.github/workflows/mirror-repositories.yml @@ -0,0 +1,171 @@ +name: Mirror to GitLab and Bitbucket + +on: + push: + branches: ['*'] + tags: ['*'] + delete: + # Triggered when branches or tags are deleted + +env: + GITLAB_URL: gitlab.com + BITBUCKET_URL: bitbucket.org + +jobs: + mirror-to-gitlab: + runs-on: ubuntu-latest + if: github.event_name == 'push' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch complete history for mirroring + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Git + run: | + git config --global user.name "GitHub Actions Mirror Bot" + git config --global user.email "actions@github.com" + + - name: Add GitLab remote and mirror + env: + GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + GITLAB_PROJECT_PATH: ${{ secrets.GITLAB_PROJECT_PATH }} # e.g., linksplatform/Interfaces + run: | + # Add GitLab remote + git remote add gitlab https://oauth2:${GITLAB_TOKEN}@${GITLAB_URL}/${GITLAB_PROJECT_PATH}.git + + # Push all branches and tags to GitLab + git push --mirror gitlab || { + echo "Mirror push failed, attempting individual branch push" + git push gitlab ${GITHUB_REF#refs/heads/} || echo "Failed to push current branch" + } + + mirror-to-bitbucket: + runs-on: ubuntu-latest + if: github.event_name == 'push' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch complete history for mirroring + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Git + run: | + git config --global user.name "GitHub Actions Mirror Bot" + git config --global user.email "actions@github.com" + + - name: Add Bitbucket remote and mirror + env: + BITBUCKET_USERNAME: ${{ secrets.BITBUCKET_USERNAME }} + BITBUCKET_APP_PASSWORD: ${{ secrets.BITBUCKET_APP_PASSWORD }} + BITBUCKET_PROJECT_PATH: ${{ secrets.BITBUCKET_PROJECT_PATH }} # e.g., linksplatform/interfaces + run: | + # Add Bitbucket remote + git remote add bitbucket https://${BITBUCKET_USERNAME}:${BITBUCKET_APP_PASSWORD}@${BITBUCKET_URL}/${BITBUCKET_PROJECT_PATH}.git + + # Push all branches and tags to Bitbucket + git push --mirror bitbucket || { + echo "Mirror push failed, attempting individual branch push" + git push bitbucket ${GITHUB_REF#refs/heads/} || echo "Failed to push current branch" + } + + handle-branch-deletion: + runs-on: ubuntu-latest + if: github.event_name == 'delete' && github.event.ref_type == 'branch' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Git + run: | + git config --global user.name "GitHub Actions Mirror Bot" + git config --global user.email "actions@github.com" + + - name: Check if branch was fully merged before deletion + id: check-merged + env: + DELETED_BRANCH: ${{ github.event.ref }} + run: | + echo "Checking if branch '${DELETED_BRANCH}' was fully merged..." + + # Check if the branch was merged into main/master + MAIN_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@' || echo "main") + + # Get the last commit of the deleted branch from GitHub API + LAST_COMMIT=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + "https://api.github.com/repos/${{ github.repository }}/commits/${DELETED_BRANCH}" \ + | jq -r '.sha' 2>/dev/null || echo "") + + if [ -n "${LAST_COMMIT}" ] && [ "${LAST_COMMIT}" != "null" ]; then + # Check if the commit exists in the main branch + if git merge-base --is-ancestor "${LAST_COMMIT}" "origin/${MAIN_BRANCH}" 2>/dev/null; then + echo "Branch was fully merged - safe to delete from mirrors" + echo "should_delete=true" >> $GITHUB_OUTPUT + else + echo "Branch was NOT fully merged - skipping deletion from mirrors" + echo "should_delete=false" >> $GITHUB_OUTPUT + fi + else + echo "Could not determine merge status - skipping deletion for safety" + echo "should_delete=false" >> $GITHUB_OUTPUT + fi + + - name: Delete branch from GitLab mirror + if: steps.check-merged.outputs.should_delete == 'true' + env: + GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + GITLAB_PROJECT_PATH: ${{ secrets.GITLAB_PROJECT_PATH }} + DELETED_BRANCH: ${{ github.event.ref }} + run: | + echo "Deleting branch '${DELETED_BRANCH}' from GitLab mirror..." + git remote add gitlab https://oauth2:${GITLAB_TOKEN}@${GITLAB_URL}/${GITLAB_PROJECT_PATH}.git + git push gitlab --delete "${DELETED_BRANCH}" || echo "Branch may not exist in GitLab or already deleted" + + - name: Delete branch from Bitbucket mirror + if: steps.check-merged.outputs.should_delete == 'true' + env: + BITBUCKET_USERNAME: ${{ secrets.BITBUCKET_USERNAME }} + BITBUCKET_APP_PASSWORD: ${{ secrets.BITBUCKET_APP_PASSWORD }} + BITBUCKET_PROJECT_PATH: ${{ secrets.BITBUCKET_PROJECT_PATH }} + DELETED_BRANCH: ${{ github.event.ref }} + run: | + echo "Deleting branch '${DELETED_BRANCH}' from Bitbucket mirror..." + git remote add bitbucket https://${BITBUCKET_USERNAME}:${BITBUCKET_APP_PASSWORD}@${BITBUCKET_URL}/${BITBUCKET_PROJECT_PATH}.git + git push bitbucket --delete "${DELETED_BRANCH}" || echo "Branch may not exist in Bitbucket or already deleted" + + mirror-tags: + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Git + run: | + git config --global user.name "GitHub Actions Mirror Bot" + git config --global user.email "actions@github.com" + + - name: Mirror tags to GitLab + env: + GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + GITLAB_PROJECT_PATH: ${{ secrets.GITLAB_PROJECT_PATH }} + run: | + git remote add gitlab https://oauth2:${GITLAB_TOKEN}@${GITLAB_URL}/${GITLAB_PROJECT_PATH}.git + git push gitlab --tags || echo "Failed to push tags to GitLab" + + - name: Mirror tags to Bitbucket + env: + BITBUCKET_USERNAME: ${{ secrets.BITBUCKET_USERNAME }} + BITBUCKET_APP_PASSWORD: ${{ secrets.BITBUCKET_APP_PASSWORD }} + BITBUCKET_PROJECT_PATH: ${{ secrets.BITBUCKET_PROJECT_PATH }} + run: | + git remote add bitbucket https://${BITBUCKET_USERNAME}:${BITBUCKET_APP_PASSWORD}@${BITBUCKET_URL}/${BITBUCKET_PROJECT_PATH}.git + git push bitbucket --tags || echo "Failed to push tags to Bitbucket" \ No newline at end of file diff --git a/MIRRORING_SETUP.md b/MIRRORING_SETUP.md new file mode 100644 index 0000000..a459bc8 --- /dev/null +++ b/MIRRORING_SETUP.md @@ -0,0 +1,132 @@ +# Repository Mirroring Setup Guide + +This document explains how to set up automatic mirroring to GitLab and Bitbucket for the Interfaces repository. + +## Overview + +The mirroring system automatically: +- Mirrors all branch and tag pushes to GitLab and Bitbucket +- Safely handles branch deletions (only if fully merged) +- Provides backup and multi-platform accessibility +- Maintains complete repository history + +## Prerequisites + +### 1. GitLab Setup +1. Create a project on GitLab at `gitlab.com/linksplatform/interfaces` +2. Generate a Personal Access Token: + - Go to GitLab → User Settings → Access Tokens + - Create token with `api`, `read_repository`, and `write_repository` scopes + - Save the token securely + +### 2. Bitbucket Setup +1. Create a repository on Bitbucket at `bitbucket.org/linksplatform/interfaces` +2. Generate an App Password: + - Go to Bitbucket → Personal settings → App passwords + - Create password with `Repositories: Write` permission + - Save the password securely + +## Required GitHub Secrets + +Add the following secrets to your GitHub repository settings: + +### GitLab Secrets +- `GITLAB_TOKEN`: Your GitLab Personal Access Token +- `GITLAB_PROJECT_PATH`: `linksplatform/interfaces` (or your preferred path) + +### Bitbucket Secrets +- `BITBUCKET_USERNAME`: Your Bitbucket username +- `BITBUCKET_APP_PASSWORD`: Your Bitbucket App Password +- `BITBUCKET_PROJECT_PATH`: `linksplatform/interfaces` (or your preferred path) + +## How to Add Secrets + +1. Go to your GitHub repository +2. Navigate to Settings → Secrets and variables → Actions +3. Click "New repository secret" +4. Add each secret with the exact names listed above + +## Workflow Features + +### Automatic Mirroring +- **Push Events**: All branch and tag pushes are automatically mirrored +- **Full History**: Complete repository history is maintained +- **Error Handling**: Graceful fallbacks if mirror operations fail + +### Safe Branch Deletion +- **Merge Check**: Only deletes branches from mirrors if they were fully merged +- **Safety First**: Skips deletion if merge status cannot be determined +- **Main Branch Protection**: Checks against main/master branch for merge status + +### Tag Synchronization +- **All Tags**: Mirrors all repository tags to both platforms +- **Release Sync**: Keeps release tags in sync across all platforms + +## Monitoring + +### Workflow Status +- Check GitHub Actions tab for mirror workflow status +- Each platform (GitLab/Bitbucket) has separate jobs for better isolation +- Failed mirrors don't block other operations + +### Troubleshooting + +#### Common Issues +1. **Authentication Failures** + - Verify tokens/passwords are correct and not expired + - Check that secrets are properly named in GitHub + +2. **Repository Not Found** + - Ensure target repositories exist on GitLab/Bitbucket + - Verify project paths match the configured secrets + +3. **Permission Errors** + - Confirm GitLab token has required scopes + - Verify Bitbucket app password has write permissions + +#### Logs +Check the GitHub Actions logs for detailed error messages: +1. Go to Actions tab in your repository +2. Click on the failed workflow run +3. Expand the failed job to see error details + +## Manual Sync (Emergency) + +If automatic mirroring fails, you can manually sync: + +```bash +# Clone the repository +git clone https://github.com/linksplatform/Interfaces.git +cd Interfaces + +# Add remotes +git remote add gitlab https://oauth2:TOKEN@gitlab.com/linksplatform/interfaces.git +git remote add bitbucket https://USERNAME:PASSWORD@bitbucket.org/linksplatform/interfaces.git + +# Push everything +git push --mirror gitlab +git push --mirror bitbucket +``` + +## Security Considerations + +- Tokens and passwords are stored as encrypted GitHub secrets +- Workflow runs only have access to secrets during execution +- All authentication uses secure HTTPS connections +- Branch deletion safety checks prevent accidental data loss + +## Testing + +After setup, test the mirroring by: +1. Creating a test branch and pushing it +2. Verifying it appears on both GitLab and Bitbucket +3. Merging the branch and deleting it +4. Confirming it's removed from mirrors + +## Support + +For issues with this mirroring setup: +1. Check the workflow logs in GitHub Actions +2. Verify all secrets are correctly configured +3. Ensure target repositories exist and are accessible +4. Review this documentation for troubleshooting steps \ No newline at end of file