# The docs.github.com project 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. # # For more details, see https://github.com/repo-sync/repo-sync#how-it-works name: Repo Sync # **What it does**: # - close-invalid-repo-sync: Close repo sync pull requests not created by Octomerger or a Hubber. # - repo-sync: Syncs docs and docs-internal. # **Why we have it**: # - close-invalid-repo-sync: Another form of spam prevention for the open-source repository. # - repo-sync: To keep the open-source repository up-to-date, while still having an internal # repository for sensitive work. # **Who does it impact**: Open-source. on: workflow_dispatch: schedule: - cron: '10,25,40,55 * * * *' # every 15 minutes jobs: close-invalid-repo-sync: name: Close invalid Repo Sync PRs runs-on: ubuntu-latest steps: - name: Find pull request if: ${{ github.repository == 'github/docs' }} uses: juliangruber/find-pull-request-action@db875662766249c049b2dcd85293892d61cb0b51 id: find-pull-request with: github-token: ${{ secrets.DOCS_BOT_SPAM_VISION }} branch: repo-sync base: main state: open - name: Close pull request if unwanted if: ${{ github.repository == 'github/docs' && steps.find-pull-request.outputs.number }} uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d with: github-token: ${{ secrets.DOCS_BOT_SPAM_VISION }} script: | const { owner, repo } = context.repo const { data: pr } = await github.pulls.get({ owner, repo, pull_number: parseInt(${{ steps.find-pull-request.outputs.number }}) }) const prCreator = pr.user.login // If the PR creator is the expected account, stop now if (prCreator === 'Octomerger') { return } try { await github.teams.getMembershipForUserInOrg({ org: 'github', team_slug: 'employees', username: prCreator }) // If the PR creator is a GitHub employee, stop now return } catch (err) { // An error will be thrown if the user is not a GitHub employee. // That said, we still want to proceed anyway! } // Close the PR and add the invalid label await github.issues.update({ owner, repo, issue_number: pr.number, labels: ['invalid'], state: 'closed' }) // Comment on the PR await github.issues.createComment({ owner, repo, issue_number: pr.number, body: "Please leave this `repo-sync` branch to the robots!\n\nI'm going to close this pull request now, but feel free to open a new issue or ask any questions in [discussions](https://github.com/github/docs/discussions)!" }) repo-sync: needs: close-invalid-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@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f - name: Sync repo to branch uses: repo-sync/github-sync@3832fe8e2be32372e1b3970bbae8e7079edeec88 env: GITHUB_TOKEN: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} with: source_repo: ${{ secrets.SOURCE_REPO }} # https://${access_token}@github.com/github/the-other-repo.git source_branch: main destination_branch: repo-sync github_token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} - name: Create pull request id: create-pull uses: repo-sync/pull-request@33777245b1aace1a58c87a29c90321aa7a74bd7d env: GITHUB_TOKEN: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} with: source_branch: repo-sync destination_branch: main pr_title: 'repo sync' pr_body: "This is an automated pull request to sync changes between the public and private repos.\n\n:robot: This pull request should be merged (not squashed) to preserve continuity across repos, so please let a bot do the merging!" pr_label: autoupdate,automated-reposync-pr github_token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} - name: Find pull request uses: juliangruber/find-pull-request-action@db875662766249c049b2dcd85293892d61cb0b51 id: find-pull-request with: github-token: ${{ secrets.GITHUB_TOKEN }} branch: repo-sync base: main author: Octomerger state: open - name: Approve pull request if: ${{ steps.find-pull-request.outputs.number }} uses: juliangruber/approve-pull-request-action@c530832d4d346c597332e20e03605aa94fa150a8 with: github-token: ${{ secrets.GITHUB_TOKEN }} number: ${{ steps.find-pull-request.outputs.number }} # There are cases where the branch becomes out-of-date in between the time this workflow began and when the pull request is created/updated - name: Update branch if: ${{ steps.find-pull-request.outputs.number }} uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d with: github-token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} script: | const mainHeadSha = await github.git.getRef({ ...context.repo, ref: 'heads/main' }) console.log(`heads/main sha: ${mainHeadSha.data.object.sha}`) const pull = await github.pulls.get({ ...context.repo, pull_number: parseInt(${{ steps.find-pull-request.outputs.number }}) }) console.log(`Pull request base sha: ${pull.data.base.sha}`) if (mainHeadSha.data.object.sha !== pull.data.base.sha || pull.data.mergeable_state === 'behind') { try { const updateBranch = await github.pulls.updateBranch({ ...context.repo, pull_number: parseInt(${{ steps.find-pull-request.outputs.number }}) }) console.log(updateBranch.data.message) } catch (error) { // When the head branch is modified an error with status 422 is thrown // We should retry one more time to update the branch if (error.status === 422) { try { const updateBranch = await github.pulls.updateBranch({ ...context.repo, pull_number: parseInt(${{ steps.find-pull-request.outputs.number }}) }) console.log(updateBranch.data.message) } catch (error) { // Only retry once. We'll rely on the update branch workflow to update // this PR in the case of a second failure. console.log(`Retried updating the branch, but an error occurred: ${error}`) } } else { // A failed branch update shouldn't fail this worklow. console.log(`An error occurred when updating the branch: ${error}`) } } } else { console.log(`Branch is already up-to-date`) } - name: Enable GitHub auto-merge if: ${{ steps.find-pull-request.outputs.number }} uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d with: github-token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} script: | const pull = await github.pulls.get({ ...context.repo, pull_number: parseInt(${{ steps.find-pull-request.outputs.number }}) }) const pullNodeId = pull.data.node_id console.log(`Pull request GraphQL Node ID: ${pullNodeId}`) const mutation = `mutation ($id: ID!) { enablePullRequestAutoMerge(input: { pullRequestId: $id, mergeMethod: MERGE }) { clientMutationId } }` const variables = { id: pullNodeId } const graph = await github.graphql(mutation, variables) console.log('GraphQL mutation result:\n' + JSON.stringify(graph)) if (graph.errors && graph.errors.length > 0) { console.error('ERROR! Failed to enable auto-merge:\n - ' + graph.errors.map(error => error.message).join('\n - ')) } else { console.log('Auto-merge enabled!') } - name: Send Slack notification if workflow fails uses: someimportantcompany/github-actions-slack-message@0b470c14b39da4260ed9e3f9a4f1298a74ccdefd if: failure() 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?query=workflow%3A%22Repo+Sync%22