From a677c76103fdb4bf951c580c75d4a21a7ca2866a Mon Sep 17 00:00:00 2001 From: Siddarth Kumar Date: Wed, 25 Sep 2024 16:19:51 +0530 Subject: [PATCH] feat!: implement PR review workflow This commit Implements a PR review workflow like this : - This action will check if `breaking change` label exists on PR. - if it does then `@status-im/desktop-qa` and `@status-im/mobile-qa` are asked for review on this PR. - Unless 1 person from `@status-im/desktop-qa` and `@status-im/mobile-qa` approve that PR the Github action will block the PR. - Only after these conditions match the Github action will allow merging of this PR. --- .github/workflows/breaking-change-review.yml | 145 +++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 .github/workflows/breaking-change-review.yml diff --git a/.github/workflows/breaking-change-review.yml b/.github/workflows/breaking-change-review.yml new file mode 100644 index 00000000000..0d12d418d18 --- /dev/null +++ b/.github/workflows/breaking-change-review.yml @@ -0,0 +1,145 @@ +name: PR Review Workflow for Breaking Changes + +on: + pull_request: + types: + - opened + - reopened + - labeled + - unlabeled + - synchronize + +jobs: + check_breaking_change: + runs-on: ubuntu-latest + steps: + - name: Check for breaking change label + id: check_label + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + const hasBreakingChange = labels.some(label => label.name === 'breaking change'); + console.log(`Has breaking change label: ${hasBreakingChange}`); + return hasBreakingChange; + + - name: Set QA reviewers + if: steps.check_label.outputs.result == 'true' + id: set_reviewers + run: | + echo "mobile_qa=churik,yevh-berdnyk,VolodLytvynenko,pavloburykh,mariia-skrypnyk,Horupa-Olena" >> $GITHUB_OUTPUT + echo "desktop_qa=anastasiyaig,virginiabalducci,glitchminer,antdanchenko" >> $GITHUB_OUTPUT + + - name: Request QA reviews + if: steps.check_label.outputs.result == 'true' + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const mobileQA = '${{ steps.set_reviewers.outputs.mobile_qa }}'.split(','); + const desktopQA = '${{ steps.set_reviewers.outputs.desktop_qa }}'.split(','); + const reviewers = [...mobileQA, ...desktopQA]; + + await github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + reviewers: reviewers + }); + + - name: Check QA approvals + if: steps.check_label.outputs.result == 'true' + id: check_approvals + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const { data: reviews } = await github.rest.pulls.listReviews({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + const mobileQA = '${{ steps.set_reviewers.outputs.mobile_qa }}'.split(','); + const desktopQA = '${{ steps.set_reviewers.outputs.desktop_qa }}'.split(','); + + const mobileQAApproved = reviews.some(review => + review.state === 'APPROVED' && mobileQA.includes(review.user.login) + ); + const desktopQAApproved = reviews.some(review => + review.state === 'APPROVED' && desktopQA.includes(review.user.login) + ); + + console.log(`Mobile QA approved: ${mobileQAApproved}`); + console.log(`Desktop QA approved: ${desktopQAApproved}`); + + core.setOutput('mobile-qa-approved', mobileQAApproved); + core.setOutput('desktop-qa-approved', desktopQAApproved); + + return mobileQAApproved && desktopQAApproved; + + - name: Block PR if conditions not met + if: steps.check_label.outputs.result == 'true' && steps.check_approvals.outputs.result != 'true' + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const mobileQAApproved = ${{ steps.check_approvals.outputs.mobile-qa-approved }}; + const desktopQAApproved = ${{ steps.check_approvals.outputs.desktop-qa-approved }}; + + let message = 'This PR has the breaking change label and requires approval from both mobile-qa and desktop-qa teams before it can be merged.\n\n'; + + if (!mobileQAApproved && !desktopQAApproved) { + message += 'Both mobile-qa and desktop-qa teams have not approved this PR yet.'; + } else if (!mobileQAApproved) { + message += 'The mobile-qa team has not approved this PR yet.'; + } else if (!desktopQAApproved) { + message += 'The desktop-qa team has not approved this PR yet.'; + } + + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'QA Approval Check', + head_sha: context.payload.pull_request.head.sha, + status: 'completed', + conclusion: 'failure', + output: { + title: 'QA Approval Required', + summary: message + } + }); + + - name: Allow PR merge if conditions are met + if: steps.check_label.outputs.result == 'true' && steps.check_approvals.outputs.result == 'true' + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + + const { data: reviews } = await github.rest.pulls.listReviews({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + const botApprovalExists = reviews.some(review => + review.user.login === 'github-actions[bot]' && review.state === 'APPROVED' + ); + + if (!botApprovalExists) { + await github.rest.pulls.createReview({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + event: 'APPROVE', + body: 'Breaking changes have been approved by Mobile and Desktop QA. This PR can now be merged.' + }); + } else { + console.log('Bot approval already exists. No action taken.'); + } \ No newline at end of file