From 1bd1feba0dea79605669cc81d19a1780bc8d8190 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Wed, 14 Jun 2023 08:00:15 -0700 Subject: [PATCH] Condense repo-sync process (#37822) --- .github/workflows/repo-sync.yml | 191 +++++++++++--------------------- 1 file changed, 65 insertions(+), 126 deletions(-) diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml index 9c16a52f57..72784657b7 100644 --- a/.github/workflows/repo-sync.yml +++ b/.github/workflows/repo-sync.yml @@ -35,8 +35,7 @@ jobs: destination_branch: repo-sync github_token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} - - name: Find or create pull request - id: pull-request + - name: Ship pull request uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 with: github-token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} @@ -45,31 +44,45 @@ jobs: const { owner, repo } = context.repo const head = 'repo-sync' const base = 'main' - const label = 'automated-reposync-pr' - // Check if a pull request already exists - let { data: pulls } = await github.rest.pulls.list({ owner, repo, head, base }) - let foundPull = pulls.find(pr => pr.labels.some(xlabel => xlabel.name === label)) - if (foundPull) { - console.log('Found pull request and will not create a new one', foundPull.html_url) - return foundPull.number + 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('Close any existing pull requests') + let { 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 (let pull of existingPulls) { + await closePullRequest(pull.number) + } + console.log('Closed existing pull requests') + } + + console.log('Create 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. ` - console.log('Create a new pull request') + let pull try { - var { data: pull } = await github.rest.pulls.create({ + pull = (await github.rest.pulls.create({ owner, repo, head, base, title: 'Repo sync', body, - }) + })).data } catch (err) { if (err.message?.includes('No commits')) { console.log(err.message) @@ -77,137 +90,63 @@ jobs: } throw err } - - console.log('Add label', label) - await github.rest.issues.addLabels({ - owner, - repo, - issue_number: pull.number, - labels: [label] - }) - console.log('Created pull request successfully', pull.html_url) - return pull.number - # Because we get far too much spam ;_; - - name: Lock conversations - if: ${{ github.repository == 'github/docs' && steps.pull-request.outputs.result }} - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 - env: - PR_NUMBER: ${{ steps.pull-request.outputs.result }} - with: - script: | + console.log('Count files') + const { data: prFiles } = await github.rest.pulls.listFiles({ owner, repo, pull_number: pull.number }) + if (!prFiles.length) { + console.log('No files changed, closing') + await closePullRequest(pull.number) + return + } + + console.log('Check mergeable') + if (!pull.mergeable) { + console.log('Pull request is not mergeable, closing') + await closePullRequest(pull.number) + return + } + + console.log('Lock conversations') try { await github.rest.issues.lock({ ...context.repo, - issue_number: parseInt(process.env.PR_NUMBER), + issue_number: pull.number, lock_reason: 'spam' }) console.log('Locked the pull request to prevent spam!') } catch (error) { - // Log the error but don't fail the workflow - console.error(`Failed to lock the pull request. Error: ${error}`) + console.error('Failed to lock the pull request.', error) + // Don't fail the workflow } - # 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.pull-request.outputs.result }} - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 - env: - PR_NUMBER: ${{ steps.pull-request.outputs.result }} - with: - github-token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} - script: | - const mainHeadSha = await github.rest.git.getRef({ - ...context.repo, - ref: 'heads/main' - }) - console.log(`heads/main sha: ${mainHeadSha.data.object.sha}`) - - const pull_number = parseInt(process.env.PR_NUMBER) - const pull = await github.rest.pulls.get({ - ...context.repo, - pull_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.rest.pulls.updateBranch({ - ...context.repo, - pull_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.rest.pulls.updateBranch({ - ...context.repo, - pull_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`) + console.log('Approve pull request') + try { + await github.rest.pulls.createReview({ + owner, + repo, + pull_number: pull.number, + event: 'APPROVE', + }) + } catch (error) { + console.error('Failed to approve the pull request.', error) + // Don't fail the workflow } - - name: Check pull request file count after updating - if: ${{ steps.pull-request.outputs.result }} - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 - id: pr-files - env: - PR_NUMBER: ${{ steps.pull-request.outputs.result }} - with: - github-token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} - result-encoding: string - script: | - const { data: prFiles } = await github.rest.pulls.listFiles({ - ...context.repo, - pull_number: process.env.PR_NUMBER, + console.log('Admin merge the pull request') + // Admin merge pull request to avoid squash + await github.rest.pulls.merge({ + owner, + repo, + pull_number: pull.number, + merge_method: 'merge', }) - core.setOutput('count', (prFiles && prFiles.length || 0).toString()) - - # Sometimes after updating the branch, there aren't any remaining files changed. - # If not, we should close the PR instead of merging it and triggering deployments. - - name: Close the pull request if no files remain - if: ${{ steps.pull-request.outputs.result && steps.pr-files.outputs.count == '0' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ steps.pull-request.outputs.result }} - run: | - gh pr close $PR_NUMBER --repo $GITHUB_REPOSITORY - - - name: Approve pull request - if: ${{ steps.steps.pull-request.outputs.result && steps.pr-files.outputs.count != '0' }} - uses: juliangruber/approve-pull-request-action@dcc4effb325c0b503408619918d56e40653dcc91 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - number: ${{ steps.pull-request.outputs.result }} - - # Admin merge to avoid being squashed in the merge queue - - name: Admin merge the pull request - if: ${{ steps.pull-request.outputs.result && steps.pr-files.outputs.count != '0' }} - env: - GITHUB_TOKEN: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }} - PR_NUMBER: ${{ steps.pull-request.outputs.result }} - run: | - gh pr merge $PR_NUMBER --admin --merge + // Error loud here, so no try/catch + console.log('Merged the pull request successfully') - name: Send Slack notification if workflow fails - uses: someimportantcompany/github-actions-slack-message@1d367080235edfa53df415bd8e0bbab480f29bad if: failure() + uses: someimportantcompany/github-actions-slack-message@1d367080235edfa53df415bd8e0bbab480f29bad with: channel: ${{ secrets.DOCS_ALERTS_SLACK_CHANNEL_ID }} bot-token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }}