263 lines
11 KiB
YAML
263 lines
11 KiB
YAML
# 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: '20,50 * * * *' # Run every hour at 20 and 50 minutes after
|
|
|
|
permissions:
|
|
contents: write
|
|
pull-requests: write
|
|
|
|
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@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
|
|
|
|
# Set up npm and run npm ci to run husky to get githooks for LFS
|
|
- name: Setup node
|
|
uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516
|
|
with:
|
|
node-version: '16.17.0'
|
|
cache: npm
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- 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
|
|
uses: repo-sync/pull-request@65785d95a5a466e46a9d0708933a3bd51bbf9dde
|
|
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: automated-reposync-pr
|
|
github_token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }}
|
|
# This will exit 0 if there's no difference between `repo-sync`
|
|
# and `main`. And if so, no PR will be created.
|
|
pr_allow_empty: false
|
|
|
|
- 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
|
|
|
|
# Because we get far too much spam ;_;
|
|
- name: Lock conversations
|
|
if: ${{ github.repository == 'github/docs' && steps.find-pull-request.outputs.number }}
|
|
uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d
|
|
with:
|
|
script: |
|
|
try {
|
|
await github.issues.lock({
|
|
...context.repo,
|
|
issue_number: parseInt(${{ steps.find-pull-request.outputs.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}`)
|
|
}
|
|
|
|
# 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: Check pull request file count after updating
|
|
if: ${{ steps.find-pull-request.outputs.number }}
|
|
uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d
|
|
id: pr-files
|
|
env:
|
|
PR_NUMBER: ${{ steps.find-pull-request.outputs.number }}
|
|
with:
|
|
github-token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }}
|
|
result-encoding: string
|
|
script: |
|
|
const { data: prFiles } = await github.pulls.listFiles({
|
|
...context.repo,
|
|
pull_number: process.env.PR_NUMBER,
|
|
})
|
|
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.find-pull-request.outputs.number && steps.pr-files.outputs.count == '0' }}
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh pr close ${{ steps.find-pull-request.outputs.number }} --repo $GITHUB_REPOSITORY
|
|
|
|
- name: Approve pull request
|
|
if: ${{ steps.find-pull-request.outputs.number && steps.pr-files.outputs.count != '0' }}
|
|
uses: juliangruber/approve-pull-request-action@c67a4808d52e44ea03656f6646ba24a010304f03
|
|
with:
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
number: ${{ steps.find-pull-request.outputs.number }}
|
|
|
|
- name: Admin merge the pull request
|
|
if: ${{ steps.find-pull-request.outputs.number && steps.pr-files.outputs.count != '0' }}
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }}
|
|
PR_NUMBER: ${{ steps.find-pull-request.outputs.number }}
|
|
run: |
|
|
gh pr merge $PR_NUMBER --admin --merge
|
|
|
|
- name: Send Slack notification if workflow fails
|
|
uses: someimportantcompany/github-actions-slack-message@f8d28715e7b8a4717047d23f48c39827cacad340
|
|
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
|