feat_: implement PR review workflow #30
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: PR Review Workflow for Breaking Changes | |
on: | |
pull_request: | |
types: | |
- opened | |
- reopened | |
- labeled | |
- unlabeled | |
- synchronize | |
pull_request_review: | |
types: | |
- submitted | |
- edited | |
- dismissed | |
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: Fetch QA team members | |
if: steps.check_label.outputs.result == 'true' | |
id: fetch_qa_teams | |
uses: actions/github-script@v6 | |
with: | |
github-token: ${{secrets.GITHUB_TOKEN}} | |
script: | | |
async function getTeamMembers(teamSlug) { | |
const response = await github.request('GET /orgs/status-im/teams/{team_slug}/members', { | |
org: 'status-im', | |
team_slug: teamSlug, | |
headers: { | |
'X-GitHub-Api-Version': '2022-11-28' | |
} | |
}); | |
return response.data.map(member => member.login); | |
} | |
const mobileQA = await getTeamMembers('mobile-qa'); | |
const desktopQA = await getTeamMembers('desktop-qa'); | |
console.log('Mobile QA team:', mobileQA); | |
console.log('Desktop QA team:', desktopQA); | |
core.setOutput('mobile_qa', mobileQA.join(',')); | |
core.setOutput('desktop_qa', desktopQA.join(',')); | |
- 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.fetch_qa_teams.outputs.mobile_qa }}'.split(','); | |
const desktopQA = '${{ steps.fetch_qa_teams.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.fetch_qa_teams.outputs.mobile_qa }}'.split(','); | |
const desktopQA = '${{ steps.fetch_qa_teams.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: | | |
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 tested by Mobile and Desktop QA teams. This PR can now be merged.' | |
}); |