From 9af839d2961f794faef9810241b03cc6f731a33f Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 17:33:48 +0100 Subject: [PATCH 01/13] chore: split the workflows Signed-off-by: blam --- .github/workflows/awaiting-review.yaml | 34 ++++ .github/workflows/goalie.yaml | 223 +++++++++++++------------ 2 files changed, 148 insertions(+), 109 deletions(-) create mode 100644 .github/workflows/awaiting-review.yaml diff --git a/.github/workflows/awaiting-review.yaml b/.github/workflows/awaiting-review.yaml new file mode 100644 index 0000000000..eabcd85a3e --- /dev/null +++ b/.github/workflows/awaiting-review.yaml @@ -0,0 +1,34 @@ +# on a PR open on PR review or comment, assign the awaiting-review label if the actor is the author +name: Set Awaiting Review +on: + pull_request_review: + types: [submitted] + pull_request_review_comment: + types: [created] + pull_request: + types: [opened, reopened, synchronize] + +permissions: + issues: write + pull-requests: write + +jobs: + label: + runs-on: ubuntu-latest + steps: + + - uses: actions/github-script@v5 + id: fix-labels + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // if it's the author, always add awaiting-review label + const isAuthor = context.payload.pull_request.user.login === context.actor + if (isAuthor) { + await github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['awaiting-review'] + }); + } diff --git a/.github/workflows/goalie.yaml b/.github/workflows/goalie.yaml index baa230cdf0..5adfef4f5d 100644 --- a/.github/workflows/goalie.yaml +++ b/.github/workflows/goalie.yaml @@ -1,16 +1,8 @@ # on a review from someone in the reviewers group, remove the awaiting-review label and add the awaiting-author label name: Set Goalie Labels on: - pull_request_review: - types: [submitted] - pull_request_review_comment: - types: [created] - pull_request: - types: [opened, reopened, synchronize] - -permissions: - issues: write - pull-requests: write + schedule: + - cron: "0 0 * * *" jobs: label: @@ -24,120 +16,133 @@ jobs: application_private_key: ${{ secrets.BACKSTAGE_WORKFLOW_MEMBER_READ_PRIVATE_KEY }} organization: backstage - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: 14 - - run: npm install codeowners - - uses: actions/github-script@v5 - id: get-all-group-members + id: get-all-open-prs with: github-token: ${{ steps.get_workflow_token.outputs.token }} script: | - // Get all teams and their respective members - const {data: teams} = await github.request('GET /orgs/{org}/teams', { - org: context.payload.organization.login, - }) - - const groupMembers = await Promise.all( - teams.map( - async (team) => { - const { data } = await github.rest.teams.listMembersInOrg({ - org: context.payload.organization.login, - team_slug: team.slug, - }); - - return { team: `@backstage/${team.slug}`, data }; - } - ) - ) - - return groupMembers; - - - uses: actions/github-script@v5 - id: get-all-changed-files - with: - script: | - const { data: allFiles } = await github.rest.pulls.listFiles({ + const { data: allPrs } = await github.paginate(octokit.rest.pulls.list, { owner: context.repo.owner, repo: context.repo.repo, - pull_number: context.issue.number, + state: 'open', }); - return allFiles; + return allPrs; - - uses: actions/github-script@v5 - id: get-all-current-reviews - with: - script: | - const { data: allReviews } = await github.rest.pulls.listReviews({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - }); + # - uses: actions/checkout@v2 + # - uses: actions/setup-node@v2 + # with: + # node-version: 14 + # - run: npm install codeowners - return allReviews; + # - uses: actions/github-script@v5 + # id: get-all-group-members + # with: + # github-token: ${{ steps.get_workflow_token.outputs.token }} + # script: | + # // Get all teams and their respective members + # const {data: teams} = await github.request('GET /orgs/{org}/teams', { + # org: context.payload.organization.login, + # }) - - uses: actions/github-script@v5 - id: fix-labels - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - // if it's the author, always add awaiting-review label - const isAuthor = context.payload.pull_request.user.login === context.actor - if (isAuthor) { - await github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['awaiting-review'] - }); - return; - } + # const groupMembers = await Promise.all( + # teams.map( + # async (team) => { + # const { data } = await github.rest.teams.listMembersInOrg({ + # org: context.payload.organization.login, + # team_slug: team.slug, + # }); - // Go through each file changed and go through each codeowner entry and use minimatch to see if the file matches - // strip the backstage group from the name? - // If it does match push the owner to a list of reviewers - // check to see the reviews and if there is at least one matching reviewer from those group + # return { team: `@backstage/${team.slug}`, data }; + # } + # ) + # ) - const changedFiles = ${{ steps.get-all-changed-files.outputs.result }} - const allReviews = ${{ steps.get-all-current-reviews.outputs.result }} - const groupMembers = ${{ steps.get-all-group-members.outputs.result }} - const Codeowners = require('codeowners'); - const codeowners = new Codeowners(); + # return groupMembers; - const expectedReviewers = new Set(); + # - uses: actions/github-script@v5 + # id: get-all-changed-files + # with: + # script: | + # const { data: allFiles } = await github.rest.pulls.listFiles({ + # owner: context.repo.owner, + # repo: context.repo.repo, + # pull_number: context.issue.number, + # }); - for (const file of changedFiles) { - expectedReviewers.add(...codeowners.getOwner(file.filename)); - } + # return allFiles; - const hasReviewed = new Set(); + # - uses: actions/github-script@v5 + # id: get-all-current-reviews + # with: + # script: | + # const { data: allReviews } = await github.rest.pulls.listReviews({ + # owner: context.repo.owner, + # repo: context.repo.repo, + # pull_number: context.issue.number, + # }); - // For each reviewer in the group, check to see if they have a review set - for (const reviewer of expectedReviewers) { - const members = groupMembers.find(member => member.team === reviewer); - if (members) { - // then we are dealing with a group - const hasMemberReview = allReviews.some(review => members.data.some(member => member.login === review.user.login)); - if (hasMemberReview) { - hasReviewed.add(reviewer); - } - } else { - // reviewer is a person - const hasReview = allReviews.some(review => reviewer === `@${review.user.login}`); - if (hasReview) { - hasReviewed.add(reviewer); - } - } - } + # return allReviews; - if (hasReviewed.size === expectedReviewers.size) { - await github.rest.issues.removeLabel({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - name: 'awaiting-review' - }).catch(() => {}); - } + # - uses: actions/github-script@v5 + # id: fix-labels + # with: + # github-token: ${{ secrets.GITHUB_TOKEN }} + # script: | + # // if it's the author, always add awaiting-review label + # const isAuthor = context.payload.pull_request.user.login === context.actor + # if (isAuthor) { + # await github.rest.issues.addLabels({ + # issue_number: context.issue.number, + # owner: context.repo.owner, + # repo: context.repo.repo, + # labels: ['awaiting-review'] + # }); + # return; + # } + + # // Go through each file changed and go through each codeowner entry and use minimatch to see if the file matches + # // strip the backstage group from the name? + # // If it does match push the owner to a list of reviewers + # // check to see the reviews and if there is at least one matching reviewer from those group + + # const changedFiles = ${{ steps.get-all-changed-files.outputs.result }} + # const allReviews = ${{ steps.get-all-current-reviews.outputs.result }} + # const groupMembers = ${{ steps.get-all-group-members.outputs.result }} + # const Codeowners = require('codeowners'); + # const codeowners = new Codeowners(); + + # const expectedReviewers = new Set(); + + # for (const file of changedFiles) { + # expectedReviewers.add(...codeowners.getOwner(file.filename)); + # } + + # const hasReviewed = new Set(); + + # // For each reviewer in the group, check to see if they have a review set + # for (const reviewer of expectedReviewers) { + # const members = groupMembers.find(member => member.team === reviewer); + # if (members) { + # // then we are dealing with a group + # const hasMemberReview = allReviews.some(review => members.data.some(member => member.login === review.user.login)); + # if (hasMemberReview) { + # hasReviewed.add(reviewer); + # } + # } else { + # // reviewer is a person + # const hasReview = allReviews.some(review => reviewer === `@${review.user.login}`); + # if (hasReview) { + # hasReviewed.add(reviewer); + # } + # } + # } + + # if (hasReviewed.size === expectedReviewers.size) { + # await github.rest.issues.removeLabel({ + # issue_number: context.issue.number, + # owner: context.repo.owner, + # repo: context.repo.repo, + # name: 'awaiting-review' + # }).catch(() => {}); + # } From 196e572e98ea8d2fdc9bab4ab4e6eca3a707a326 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 17:35:50 +0100 Subject: [PATCH 02/13] chore: rename the workflow so it runs Signed-off-by: blam --- .github/workflows/goalie.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/goalie.yaml b/.github/workflows/goalie.yaml index 5adfef4f5d..59593b6cf6 100644 --- a/.github/workflows/goalie.yaml +++ b/.github/workflows/goalie.yaml @@ -1,5 +1,5 @@ # on a review from someone in the reviewers group, remove the awaiting-review label and add the awaiting-author label -name: Set Goalie Labels +name: Goalie Cron on: schedule: - cron: "0 0 * * *" From f38b57fcf79cf950c3f689fc979641de59e26dcd Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 17:38:06 +0100 Subject: [PATCH 03/13] chore: run every minute Signed-off-by: blam --- .github/workflows/goalie.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/goalie.yaml b/.github/workflows/goalie.yaml index 59593b6cf6..2716880997 100644 --- a/.github/workflows/goalie.yaml +++ b/.github/workflows/goalie.yaml @@ -2,7 +2,7 @@ name: Goalie Cron on: schedule: - - cron: "0 0 * * *" + - cron: "* * * * *" jobs: label: From c732188aeed0544a1fc408f84ad6f9319b97f7f9 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 17:42:14 +0100 Subject: [PATCH 04/13] chore: goalie cron Signed-off-by: blam --- .github/workflows/{goalie.yaml => goalie-cron.yaml} | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) rename .github/workflows/{goalie.yaml => goalie-cron.yaml} (99%) diff --git a/.github/workflows/goalie.yaml b/.github/workflows/goalie-cron.yaml similarity index 99% rename from .github/workflows/goalie.yaml rename to .github/workflows/goalie-cron.yaml index 2716880997..fb5a1a86e2 100644 --- a/.github/workflows/goalie.yaml +++ b/.github/workflows/goalie-cron.yaml @@ -1,8 +1,10 @@ # on a review from someone in the reviewers group, remove the awaiting-review label and add the awaiting-author label name: Goalie Cron -on: - schedule: - - cron: "* * * * *" +on: push + +# on: +# schedule: +# - cron: "* * * * *" jobs: label: From f2bb24298f43d4530152085688069703def56eaf Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 17:43:40 +0100 Subject: [PATCH 05/13] chore: added some debug logging Signed-off-by: blam --- .github/workflows/goalie-cron.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/goalie-cron.yaml b/.github/workflows/goalie-cron.yaml index fb5a1a86e2..05f1adf0b7 100644 --- a/.github/workflows/goalie-cron.yaml +++ b/.github/workflows/goalie-cron.yaml @@ -23,12 +23,14 @@ jobs: with: github-token: ${{ steps.get_workflow_token.outputs.token }} script: | - const { data: allPrs } = await github.paginate(octokit.rest.pulls.list, { + const { data: allPrs } = await github.paginate(github.rest.pulls.list, { owner: context.repo.owner, repo: context.repo.repo, state: 'open', }); + console.log(allPrs); + return allPrs; # - uses: actions/checkout@v2 From 65b0d1b3fdd310ad993e5e1735c81acd65269ba0 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 17:45:03 +0100 Subject: [PATCH 06/13] chore: dont destructure Signed-off-by: blam --- .github/workflows/goalie-cron.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/goalie-cron.yaml b/.github/workflows/goalie-cron.yaml index 05f1adf0b7..629c33d76b 100644 --- a/.github/workflows/goalie-cron.yaml +++ b/.github/workflows/goalie-cron.yaml @@ -23,7 +23,7 @@ jobs: with: github-token: ${{ steps.get_workflow_token.outputs.token }} script: | - const { data: allPrs } = await github.paginate(github.rest.pulls.list, { + const allPrs = await github.paginate(github.rest.pulls.list, { owner: context.repo.owner, repo: context.repo.repo, state: 'open', From 557ad7e4629bff927a129a15b3991e5ebcaa375c Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 18:04:46 +0100 Subject: [PATCH 07/13] feat: split out the workflow to run for each open PR signed-off-by: blam --- .github/workflows/goalie-cron.yaml | 170 +++++++++++++++-------------- 1 file changed, 87 insertions(+), 83 deletions(-) diff --git a/.github/workflows/goalie-cron.yaml b/.github/workflows/goalie-cron.yaml index 629c33d76b..72c160a30a 100644 --- a/.github/workflows/goalie-cron.yaml +++ b/.github/workflows/goalie-cron.yaml @@ -29,40 +29,38 @@ jobs: state: 'open', }); - console.log(allPrs); - return allPrs; - # - uses: actions/checkout@v2 - # - uses: actions/setup-node@v2 - # with: - # node-version: 14 - # - run: npm install codeowners + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 14 + - run: npm install codeowners - # - uses: actions/github-script@v5 - # id: get-all-group-members - # with: - # github-token: ${{ steps.get_workflow_token.outputs.token }} - # script: | - # // Get all teams and their respective members - # const {data: teams} = await github.request('GET /orgs/{org}/teams', { - # org: context.payload.organization.login, - # }) + - uses: actions/github-script@v5 + id: get-all-group-members + with: + github-token: ${{ steps.get_workflow_token.outputs.token }} + script: | + // Get all teams and their respective members + const {data: teams} = await github.request('GET /orgs/{org}/teams', { + org: context.repo.owner, + }) - # const groupMembers = await Promise.all( - # teams.map( - # async (team) => { - # const { data } = await github.rest.teams.listMembersInOrg({ - # org: context.payload.organization.login, - # team_slug: team.slug, - # }); + const groupMembers = await Promise.all( + teams.map( + async (team) => { + const { data } = await github.rest.teams.listMembersInOrg({ + org: context.repo.owner, + team_slug: team.slug, + }); - # return { team: `@backstage/${team.slug}`, data }; - # } - # ) - # ) + return { team: `@backstage/${team.slug}`, data }; + } + ) + ) - # return groupMembers; + return groupMembers; # - uses: actions/github-script@v5 # id: get-all-changed-files @@ -88,65 +86,71 @@ jobs: # return allReviews; - # - uses: actions/github-script@v5 - # id: fix-labels - # with: - # github-token: ${{ secrets.GITHUB_TOKEN }} - # script: | - # // if it's the author, always add awaiting-review label - # const isAuthor = context.payload.pull_request.user.login === context.actor - # if (isAuthor) { - # await github.rest.issues.addLabels({ - # issue_number: context.issue.number, - # owner: context.repo.owner, - # repo: context.repo.repo, - # labels: ['awaiting-review'] - # }); - # return; - # } + - uses: actions/github-script@v5 + id: fix-labels + with: + github-token: ${{ steps.get_workflow_token.outputs.token }} + script: | + // Go through each PR - # // Go through each file changed and go through each codeowner entry and use minimatch to see if the file matches - # // strip the backstage group from the name? - # // If it does match push the owner to a list of reviewers - # // check to see the reviews and if there is at least one matching reviewer from those group + const allPullRequests = ${{ steps.get-all-open-prs.outputs.result }} + const groupMembers = ${{ steps.get-all-group-members.outputs.result }} + const Codeowners = require('codeowners'); + const codeowners = new Codeowners(); - # const changedFiles = ${{ steps.get-all-changed-files.outputs.result }} - # const allReviews = ${{ steps.get-all-current-reviews.outputs.result }} - # const groupMembers = ${{ steps.get-all-group-members.outputs.result }} - # const Codeowners = require('codeowners'); - # const codeowners = new Codeowners(); - # const expectedReviewers = new Set(); + for (const pullRequest of allPullRequests) { + // Go through each file changed and go through each codeowner entry and use minimatch to see if the file matches + // strip the backstage group from the name? + // If it does match push the owner to a list of reviewers + // check to see the reviews and if there is at least one matching reviewer from those group - # for (const file of changedFiles) { - # expectedReviewers.add(...codeowners.getOwner(file.filename)); - # } + const changedFiles = await github.paginate(github.rest.pulls.listFiles, { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pullRequest.number, + }); - # const hasReviewed = new Set(); + const allReviews = await github.paginate(github.rest.pulls.listReviews, { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pullRequest.number, + }); + + const expectedReviewers = new Set(); - # // For each reviewer in the group, check to see if they have a review set - # for (const reviewer of expectedReviewers) { - # const members = groupMembers.find(member => member.team === reviewer); - # if (members) { - # // then we are dealing with a group - # const hasMemberReview = allReviews.some(review => members.data.some(member => member.login === review.user.login)); - # if (hasMemberReview) { - # hasReviewed.add(reviewer); - # } - # } else { - # // reviewer is a person - # const hasReview = allReviews.some(review => reviewer === `@${review.user.login}`); - # if (hasReview) { - # hasReviewed.add(reviewer); - # } - # } - # } + for (const file of changedFiles) { + expectedReviewers.add(...codeowners.getOwner(file.filename)); + } - # if (hasReviewed.size === expectedReviewers.size) { - # await github.rest.issues.removeLabel({ - # issue_number: context.issue.number, - # owner: context.repo.owner, - # repo: context.repo.repo, - # name: 'awaiting-review' - # }).catch(() => {}); - # } + const hasReviewed = new Set(); + + // For each reviewer in the group, check to see if they have a review set + for (const reviewer of expectedReviewers) { + const members = groupMembers.find(member => member.team === reviewer); + if (members) { + // then we are dealing with a group + const hasMemberReview = allReviews.some(review => members.data.some(member => member.login === review.user.login)); + if (hasMemberReview) { + hasReviewed.add(reviewer); + } + } else { + // reviewer is a person + const hasReview = allReviews.some(review => reviewer === `@${review.user.login}`); + if (hasReview) { + hasReviewed.add(reviewer); + } + } + } + + if (hasReviewed.size === expectedReviewers.size) { + console.log('should remove label'); + //await github.rest.issues.removeLabel({ + // issue_number: context.issue.number, + // owner: context.repo.owner, + // repo: context.repo.repo, + // name: 'awaiting-review' + //}).catch(() => {}); + } else { console.log('should add label')} + } + From 1129e3d1bc863f0008c4cc042f1f39c337c491e8 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 18:07:28 +0100 Subject: [PATCH 08/13] chore: remove some comments and maybe it works? Signed-off-by: blam --- .github/workflows/goalie-cron.yaml | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/.github/workflows/goalie-cron.yaml b/.github/workflows/goalie-cron.yaml index 72c160a30a..1a39bb7b5a 100644 --- a/.github/workflows/goalie-cron.yaml +++ b/.github/workflows/goalie-cron.yaml @@ -62,30 +62,6 @@ jobs: return groupMembers; - # - uses: actions/github-script@v5 - # id: get-all-changed-files - # with: - # script: | - # const { data: allFiles } = await github.rest.pulls.listFiles({ - # owner: context.repo.owner, - # repo: context.repo.repo, - # pull_number: context.issue.number, - # }); - - # return allFiles; - - # - uses: actions/github-script@v5 - # id: get-all-current-reviews - # with: - # script: | - # const { data: allReviews } = await github.rest.pulls.listReviews({ - # owner: context.repo.owner, - # repo: context.repo.repo, - # pull_number: context.issue.number, - # }); - - # return allReviews; - - uses: actions/github-script@v5 id: fix-labels with: @@ -98,13 +74,7 @@ jobs: const Codeowners = require('codeowners'); const codeowners = new Codeowners(); - for (const pullRequest of allPullRequests) { - // Go through each file changed and go through each codeowner entry and use minimatch to see if the file matches - // strip the backstage group from the name? - // If it does match push the owner to a list of reviewers - // check to see the reviews and if there is at least one matching reviewer from those group - const changedFiles = await github.paginate(github.rest.pulls.listFiles, { owner: context.repo.owner, repo: context.repo.repo, From 5cefc14d5176b9705119405be9deab54a94b408c Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 18:11:08 +0100 Subject: [PATCH 09/13] chore: more comment removal? Signed-off-by: blam --- .github/workflows/goalie-cron.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/goalie-cron.yaml b/.github/workflows/goalie-cron.yaml index 1a39bb7b5a..277692beaf 100644 --- a/.github/workflows/goalie-cron.yaml +++ b/.github/workflows/goalie-cron.yaml @@ -67,8 +67,6 @@ jobs: with: github-token: ${{ steps.get_workflow_token.outputs.token }} script: | - // Go through each PR - const allPullRequests = ${{ steps.get-all-open-prs.outputs.result }} const groupMembers = ${{ steps.get-all-group-members.outputs.result }} const Codeowners = require('codeowners'); @@ -115,12 +113,6 @@ jobs: if (hasReviewed.size === expectedReviewers.size) { console.log('should remove label'); - //await github.rest.issues.removeLabel({ - // issue_number: context.issue.number, - // owner: context.repo.owner, - // repo: context.repo.repo, - // name: 'awaiting-review' - //}).catch(() => {}); } else { console.log('should add label')} } From e23c2463d51cb098961e4e7e58fc264fa96df087 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 18:21:29 +0100 Subject: [PATCH 10/13] chore: move out script into one file instead because args were too long Signed-off-by: blam --- .github/workflows/goalie-cron.yaml | 88 +-------------------------- scripts/goalie-labels.js | 98 ++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 86 deletions(-) create mode 100644 scripts/goalie-labels.js diff --git a/.github/workflows/goalie-cron.yaml b/.github/workflows/goalie-cron.yaml index 277692beaf..24b037546a 100644 --- a/.github/workflows/goalie-cron.yaml +++ b/.github/workflows/goalie-cron.yaml @@ -18,101 +18,17 @@ jobs: application_private_key: ${{ secrets.BACKSTAGE_WORKFLOW_MEMBER_READ_PRIVATE_KEY }} organization: backstage - - uses: actions/github-script@v5 - id: get-all-open-prs - with: - github-token: ${{ steps.get_workflow_token.outputs.token }} - script: | - const allPrs = await github.paginate(github.rest.pulls.list, { - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - }); - - return allPrs; - - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: node-version: 14 - run: npm install codeowners - - uses: actions/github-script@v5 - id: get-all-group-members - with: - github-token: ${{ steps.get_workflow_token.outputs.token }} - script: | - // Get all teams and their respective members - const {data: teams} = await github.request('GET /orgs/{org}/teams', { - org: context.repo.owner, - }) - - const groupMembers = await Promise.all( - teams.map( - async (team) => { - const { data } = await github.rest.teams.listMembersInOrg({ - org: context.repo.owner, - team_slug: team.slug, - }); - - return { team: `@backstage/${team.slug}`, data }; - } - ) - ) - - return groupMembers; - - uses: actions/github-script@v5 id: fix-labels with: github-token: ${{ steps.get_workflow_token.outputs.token }} script: | - const allPullRequests = ${{ steps.get-all-open-prs.outputs.result }} - const groupMembers = ${{ steps.get-all-group-members.outputs.result }} - const Codeowners = require('codeowners'); - const codeowners = new Codeowners(); - - for (const pullRequest of allPullRequests) { - const changedFiles = await github.paginate(github.rest.pulls.listFiles, { - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: pullRequest.number, - }); - - const allReviews = await github.paginate(github.rest.pulls.listReviews, { - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: pullRequest.number, - }); - - const expectedReviewers = new Set(); - - for (const file of changedFiles) { - expectedReviewers.add(...codeowners.getOwner(file.filename)); - } - - const hasReviewed = new Set(); - - // For each reviewer in the group, check to see if they have a review set - for (const reviewer of expectedReviewers) { - const members = groupMembers.find(member => member.team === reviewer); - if (members) { - // then we are dealing with a group - const hasMemberReview = allReviews.some(review => members.data.some(member => member.login === review.user.login)); - if (hasMemberReview) { - hasReviewed.add(reviewer); - } - } else { - // reviewer is a person - const hasReview = allReviews.some(review => reviewer === `@${review.user.login}`); - if (hasReview) { - hasReviewed.add(reviewer); - } - } - } - - if (hasReviewed.size === expectedReviewers.size) { - console.log('should remove label'); - } else { console.log('should add label')} - } + const script = require('./scripts/goalie-labels.js') + await script({github, context, core}) diff --git a/scripts/goalie-labels.js b/scripts/goalie-labels.js new file mode 100644 index 0000000000..02d2e4a332 --- /dev/null +++ b/scripts/goalie-labels.js @@ -0,0 +1,98 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +module.exports = async ({ github, context, core }) => { + // first get all open pull requests + const allPullRequests = await github.paginate(github.rest.pulls.list, { + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + }); + + const { data: teams } = await github.request('GET /orgs/{org}/teams', { + org: context.repo.owner, + }); + + const groupMembers = await Promise.all( + teams.map(async team => { + const { data } = await github.rest.teams.listMembersInOrg({ + org: context.repo.owner, + team_slug: team.slug, + }); + + return { team: `@backstage/${team.slug}`, data }; + }), + ); + + for (const pullRequest of allPullRequests) { + // Go through each file changed and go through each codeowner entry and use minimatch to see if the file matches + // strip the backstage group from the name? + // If it does match push the owner to a list of reviewers + // check to see the reviews and if there is at least one matching reviewer from those group + const changedFiles = await github.paginate(github.rest.pulls.listFiles, { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pullRequest.number, + }); + + const allReviews = await github.paginate(github.rest.pulls.listReviews, { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pullRequest.number, + }); + + const expectedReviewers = new Set(); + + for (const file of changedFiles) { + expectedReviewers.add(...codeowners.getOwner(file.filename)); + } + + const hasReviewed = new Set(); + + // For each reviewer in the group, check to see if they have a review set + for (const reviewer of expectedReviewers) { + const members = groupMembers.find(member => member.team === reviewer); + if (members) { + // then we are dealing with a group + const hasMemberReview = allReviews.some(review => + members.data.some(member => member.login === review.user.login), + ); + if (hasMemberReview) { + hasReviewed.add(reviewer); + } + } else { + // reviewer is a person + const hasReview = allReviews.some( + review => reviewer === `@${review.user.login}`, + ); + if (hasReview) { + hasReviewed.add(reviewer); + } + } + } + + if (hasReviewed.size === expectedReviewers.size) { + console.log('should remove label'); + // await github.rest.issues.removeLabel({ + // issue_number: context.issue.number, + // owner: context.repo.owner, + // repo: context.repo.repo, + // name: 'awaiting-review' + // }).catch(() => {}); + } else { + console.log('should add label'); + } + } +}; From c6946dedbe707eba96777ea2ad222502305e1eb0 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 18:24:29 +0100 Subject: [PATCH 11/13] chore: missed the Codeowners Signed-off-by: blam --- scripts/goalie-labels.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/goalie-labels.js b/scripts/goalie-labels.js index 02d2e4a332..01ec5dee7c 100644 --- a/scripts/goalie-labels.js +++ b/scripts/goalie-labels.js @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +const Codeowners = require('codeowners'); + module.exports = async ({ github, context, core }) => { // first get all open pull requests const allPullRequests = await github.paginate(github.rest.pulls.list, { @@ -36,11 +39,13 @@ module.exports = async ({ github, context, core }) => { }), ); + const codeowners = new Codeowners(); + for (const pullRequest of allPullRequests) { // Go through each file changed and go through each codeowner entry and use minimatch to see if the file matches // strip the backstage group from the name? // If it does match push the owner to a list of reviewers - // check to see the reviews and if there is at least one matching reviewer from those group + // check to see the reviews and if there is at least one matching reviewer from those groupx const changedFiles = await github.paginate(github.rest.pulls.listFiles, { owner: context.repo.owner, repo: context.repo.repo, From 0163fb056f171c56adfab54a65c4685fb3ce0a30 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 18:31:19 +0100 Subject: [PATCH 12/13] chore: run the goalie Workflow Signed-off-by: blam --- .../workflows/{goalie-cron.yaml => goalie.yaml} | 14 ++++++-------- scripts/goalie-labels.js | 17 ++++++++--------- 2 files changed, 14 insertions(+), 17 deletions(-) rename .github/workflows/{goalie-cron.yaml => goalie.yaml} (76%) diff --git a/.github/workflows/goalie-cron.yaml b/.github/workflows/goalie.yaml similarity index 76% rename from .github/workflows/goalie-cron.yaml rename to .github/workflows/goalie.yaml index 24b037546a..ee0a6a248e 100644 --- a/.github/workflows/goalie-cron.yaml +++ b/.github/workflows/goalie.yaml @@ -1,10 +1,8 @@ # on a review from someone in the reviewers group, remove the awaiting-review label and add the awaiting-author label -name: Goalie Cron -on: push - -# on: -# schedule: -# - cron: "* * * * *" +name: Goalie Workflow +on: + schedule: + - cron: "* * * * *" jobs: label: @@ -14,8 +12,8 @@ jobs: id: get_workflow_token uses: peter-murray/workflow-application-token-action@v1 with: - application_id: ${{ secrets.BACKSTAGE_WORKFLOW_MEMBER_READ_APP_ID }} - application_private_key: ${{ secrets.BACKSTAGE_WORKFLOW_MEMBER_READ_PRIVATE_KEY }} + application_id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} + application_private_key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} organization: backstage - uses: actions/checkout@v2 diff --git a/scripts/goalie-labels.js b/scripts/goalie-labels.js index 01ec5dee7c..e9765aebb8 100644 --- a/scripts/goalie-labels.js +++ b/scripts/goalie-labels.js @@ -89,15 +89,14 @@ module.exports = async ({ github, context, core }) => { } if (hasReviewed.size === expectedReviewers.size) { - console.log('should remove label'); - // await github.rest.issues.removeLabel({ - // issue_number: context.issue.number, - // owner: context.repo.owner, - // repo: context.repo.repo, - // name: 'awaiting-review' - // }).catch(() => {}); - } else { - console.log('should add label'); + await github.rest.issues + .removeLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: 'awaiting-review', + }) + .catch(() => {}); } } }; From ec33c21c9d5664d14ee1fd65971afcb037538cb9 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 18 Jan 2022 18:33:21 +0100 Subject: [PATCH 13/13] chore: make the workflows pretty Signed-off-by: blam --- .github/workflows/awaiting-review.yaml | 1 - .github/workflows/goalie.yaml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/awaiting-review.yaml b/.github/workflows/awaiting-review.yaml index eabcd85a3e..06a85921d8 100644 --- a/.github/workflows/awaiting-review.yaml +++ b/.github/workflows/awaiting-review.yaml @@ -16,7 +16,6 @@ jobs: label: runs-on: ubuntu-latest steps: - - uses: actions/github-script@v5 id: fix-labels with: diff --git a/.github/workflows/goalie.yaml b/.github/workflows/goalie.yaml index ee0a6a248e..6aaaafc429 100644 --- a/.github/workflows/goalie.yaml +++ b/.github/workflows/goalie.yaml @@ -2,7 +2,7 @@ name: Goalie Workflow on: schedule: - - cron: "* * * * *" + - cron: '* * * * *' jobs: label: @@ -29,4 +29,3 @@ jobs: script: | const script = require('./scripts/goalie-labels.js') await script({github, context, core}) -