name: Repo Sync # **What it does**: GitHub Docs has two repositories: github/docs (public) and github/docs-internal (private). # This GitHub Actions workflow keeps the `main` branch of those two repos in sync. # **Why we have it**: To keep the open-source repository up-to-date # while still having an internal repository for sensitive work. # **Who does it impact**: Open-source. # For more details, see https://github.com/repo-sync/repo-sync#how-it-works on: workflow_dispatch: schedule: - cron: '20,50 * * * *' # Run every hour at 20 and 50 minutes after permissions: contents: write pull-requests: write jobs: repo-sync: if: github.repository == 'github/docs-internal' || github.repository == 'github/docs' name: Repo Sync runs-on: ubuntu-latest steps: - name: Check out repo uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Sync repo to branch uses: repo-sync/github-sync@3832fe8e2be32372e1b3970bbae8e7079edeec88 with: source_repo: https://${{ secrets.DOCS_BOT_PAT_WORKFLOW }}@github.com/github/${{ github.repository == 'github/docs-internal' && 'docs' || 'docs-internal' }}.git source_branch: main destination_branch: repo-sync github_token: ${{ secrets.DOCS_BOT_PAT_WORKFLOW }} - name: Ship pull request uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 with: github-token: ${{ secrets.DOCS_BOT_PAT_WORKFLOW }} result-encoding: string script: | const { owner, repo } = context.repo const head = 'github:repo-sync' const base = 'main' async function closePullRequest(prNumber) { console.log('Closing pull request', prNumber) await github.rest.pulls.update({ owner, repo, pull_number: prNumber, state: 'closed' }) // Error loud here, so no try/catch console.log('Closed pull request', prNumber) } console.log('Closing any existing pull requests') const { data: existingPulls } = await github.rest.pulls.list({ owner, repo, head, base }) if (existingPulls.length) { console.log('Found existing pull requests', existingPulls.map(pull => pull.number)) for (const pull of existingPulls) { await closePullRequest(pull.number) } console.log('Closed existing pull requests') } console.log('Creating a new pull request') const body = ` This is an automated pull request to sync changes between the public and private repos. Our bot will merge this pull request automatically. To preserve continuity across repos, _do not squash_ this pull request. ` let pull, pull_number try { const response = await github.rest.pulls.create({ owner, repo, head, base, title: 'Repo sync', body, }) pull = response.data pull_number = pull.number console.log('Created pull request successfully', pull.html_url) } catch (err) { // Don't error/alert if there's no commits to sync if (err.message?.includes('No commits')) { console.log(err.message) return } throw err } console.log('Locking conversations to prevent spam') try { await github.rest.issues.lock({ ...context.repo, issue_number: pull_number, lock_reason: 'spam' }) console.log('Locked the pull request to prevent spam') } catch (error) { console.error('Failed to lock the pull request.', error) // Don't fail the workflow } console.log('Counting files changed') const { data: prFiles } = await github.rest.pulls.listFiles({ owner, repo, pull_number }) if (prFiles.length) { console.log(prFiles.length, 'files have changed') } else { console.log('No files changed, closing') await closePullRequest(pull_number) return } console.log('Checking for merge conflicts') if (pull.mergeable_state === 'dirty') { console.log('Pull request has a conflict', pull.html_url) await closePullRequest(pull_number) throw new Error('Pull request has a conflict, please resolve manually') } console.log('No detected merge conflicts') console.log('Merging the pull request') // Admin merge pull request to avoid squash await github.rest.pulls.merge({ owner, repo, pull_number, merge_method: 'merge', }) // Error loud here, so no try/catch console.log('Merged the pull request successfully') - name: Send Slack notification if workflow fails if: failure() uses: someimportantcompany/github-actions-slack-message@1d367080235edfa53df415bd8e0bbab480f29bad with: channel: ${{ secrets.DOCS_ALERTS_SLACK_CHANNEL_ID }} bot-token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }} color: failure text: The last repo-sync run for ${{github.repository}} failed. See https://github.com/${{github.repository}}/actions/workflows/repo-sync.yml