From ab7be7eb04c5bc70e4a8d54b0b0881f6c9d6227b Mon Sep 17 00:00:00 2001 From: RedYetiDev <38299977+RedYetiDev@users.noreply.github.com> Date: Sat, 25 May 2024 11:23:16 -0400 Subject: [PATCH] meta: request reviews from github actions --- .github/workflows/handle-pr-open.yml | 24 ++++++++ .github/workflows/label-pr.yml | 18 ------ tools/actions/pr-open.js | 91 ++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/handle-pr-open.yml delete mode 100644 .github/workflows/label-pr.yml create mode 100644 tools/actions/pr-open.js diff --git a/.github/workflows/handle-pr-open.yml b/.github/workflows/handle-pr-open.yml new file mode 100644 index 00000000000000..dd7a1c74155ee3 --- /dev/null +++ b/.github/workflows/handle-pr-open.yml @@ -0,0 +1,24 @@ +name: Handle PRs + +on: + pull_request_target: + types: [opened] + +permissions: + contents: read + pull-requests: write + issues: write + +jobs: + main: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + name: Checkout default branch + - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + name: Make initial comment + with: + script: | + const initialCommentBuilder = require('${{ github.workspace }}/tools/actions/pr-open.js'); + await initialCommentBuilder(github, context); diff --git a/.github/workflows/label-pr.yml b/.github/workflows/label-pr.yml deleted file mode 100644 index 95fdd42a4c700a..00000000000000 --- a/.github/workflows/label-pr.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Label PRs - -on: - pull_request_target: - types: [opened] - -permissions: - contents: read - -jobs: - label: - runs-on: ubuntu-latest - - steps: - - uses: nodejs/node-pr-labeler@d4cf1b8b9f23189c37917000e5e17e796c770a6b # v1 - with: - repo-token: ${{ secrets.GH_USER_TOKEN }} - configuration-path: .github/label-pr-config.yml diff --git a/tools/actions/pr-open.js b/tools/actions/pr-open.js new file mode 100644 index 00000000000000..c91eb9c922daa2 --- /dev/null +++ b/tools/actions/pr-open.js @@ -0,0 +1,91 @@ +'use strict'; + +module.exports = async (client, context) => { + function globToRegex(glob) { + const patterns = { + '*': '([^\\/]+)', + '**': '(.+\\/)?([^\\/]+)', + '**/': '(.+\\/)', + }; + + const escapedGlob = glob.replace(/\./g, '\\.').replace(/\*\*$/g, '(.+)'); + const regexString = '^(' + escapedGlob.replace(/\*\*\/|\*\*|\*/g, (match) => patterns[match] || '') + ')$'; + return new RegExp(regexString); + } + + class CodeOwners { + constructor(fileContent) { + this.owners = fileContent.split('\n').map((line) => line.trim()) + .filter((line) => line && !line.startsWith('#')) + .map((line) => { + const [path, ...owners] = line.split(/\s+/); + return { path: globToRegex(path), owners }; + }); + } + + getOwners(filePath) { + const normalizedPath = filePath.startsWith('/') ? filePath : `/${filePath}`; + return this.owners + .filter(({ path }) => path.test(normalizedPath)) + .flatMap(({ owners }) => owners); + } + } + + async function getChangedFiles(base, head) { + const { data } = await client.rest.repos.compareCommits({ + owner: context.repo.owner, + repo: context.repo.repo, + base, + head, + }); + return data.files.map((file) => file.filename); + } + + async function getCodeOwners() { + const { data } = await client.rest.repos.getContent({ + owner: context.repo.owner, + repo: context.repo.repo, + path: '.github/CODEOWNERS', + }); + + const content = Buffer.from(data.content, 'base64').toString('utf8'); + return new CodeOwners(content); + } + + async function makeInitialComment(changedFiles) { + const codeOwners = await getCodeOwners(); + + const owners = new Set(); + changedFiles.forEach((file) => { + codeOwners.getOwners(file).forEach((owner) => owners.add(owner)); + }); + + const ownersList = Array.from(owners).map((owner) => `- ${owner}`).join('\n'); + + const body = + 'Hi 👋! Thank you for submitting this pull-request!\n\n' + + 'According to the CODEOWNERS file, the following people are responsible for' + + "reviewing changes to the files you've modified:\n\n" + + ownersList + '\n\n' + + 'They, along with other project maintainers, will be notified of your pull request.' + + 'Please be patient and wait for them to review your changes.\n\n' + + "If you have any questions, please don't hesitate to ask!"; + + return client.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: body, + }); + } + + const base = context.payload.pull_request?.base?.sha; + const head = context.payload.pull_request?.head?.sha; + + if (!base || !head) { + throw new Error('Cannot get base or head commit'); + } + + const changedFiles = await getChangedFiles(base, head); + await makeInitialComment(changedFiles); +};