Add codeowner workflow check #2
Workflow file for this run
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: Code Owners Approval Check | |
on: | |
pull_request: | |
branches: | |
- master | |
types: [opened, synchronize, reopened, ready_for_review] | |
pull_request_review: | |
types: [submitted, dismissed] | |
permissions: {} | |
jobs: | |
check-code-owners-approval: | |
runs-on: ubuntu-latest | |
if: github.base_ref == 'master' | |
permissions: | |
pull-requests: write | |
contents: read | |
steps: | |
- name: Check Code Owners Approval | |
id: check_approvals | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{secrets.GITHUB_TOKEN}} | |
script: | | |
const { owner, repo, number } = context.issue; | |
// Get pull request details | |
const { data: pr } = await github.rest.pulls.get({ | |
owner, | |
repo, | |
pull_number: number | |
}); | |
// Get CODEOWNERS file content | |
let codeowners; | |
try { | |
const { data } = await github.rest.repos.getContent({ | |
owner, | |
repo, | |
path: 'CODEOWNERS', | |
}); | |
codeowners = Buffer.from(data.content, 'base64').toString('utf8'); | |
} catch (error) { | |
console.log('CODEOWNERS file not found. Skipping check.'); | |
return; | |
} | |
// Parse CODEOWNERS file | |
const codeownersLines = codeowners.split('\n').filter(line => line.trim() && !line.startsWith('#')); | |
const codeownersSet = new Set(codeownersLines.flatMap(line => { | |
const parts = line.split(/\s+/); | |
return parts.slice(1).filter(part => part.startsWith('@')); | |
})); | |
if (codeownersSet.size === 0) { | |
console.log('No code owners found. Skipping check.'); | |
return; | |
} | |
// Get reviews | |
const { data: reviews } = await github.rest.pulls.listReviews({ | |
owner, | |
repo, | |
pull_number: number | |
}); | |
const approvals = new Set( | |
reviews | |
.filter(review => review.state === 'APPROVED') | |
.map(review => review.user.login) | |
); | |
const codeOwnerStatus = Array.from(codeownersSet).map(owner => ({ | |
owner: owner.substring(1), // Remove '@' from the beginning | |
approved: approvals.has(owner.substring(1)) | |
})); | |
const missingApprovals = codeOwnerStatus.filter(status => !status.approved); | |
if (missingApprovals.length > 0) { | |
core.setFailed(`Missing approvals from code owners: ${missingApprovals.map(status => status.owner).join(', ')}`); | |
} else { | |
console.log('All code owners have approved the pull request.'); | |
} | |
core.setOutput('codeOwnerStatus', JSON.stringify(codeOwnerStatus)); | |
- name: Update PR status | |
if: failure() | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{secrets.GITHUB_TOKEN}} | |
script: | | |
const { owner, repo, number } = context.issue; | |
const codeOwnerStatus = JSON.parse(process.env.CODE_OWNER_STATUS); | |
const statusList = codeOwnerStatus.map(status => { | |
const emoji = status.approved ? '✅' : '❌'; | |
return `${emoji} ${status.owner}`; | |
}).join('\n'); | |
const comment = `This pull request is missing approvals from one or more code owners.\n\nCode Owner Status:\n${statusList}`; | |
await github.rest.pulls.createReview({ | |
owner, | |
repo, | |
pull_number: number, | |
event: 'COMMENT', | |
body: comment | |
}); | |
env: | |
CODE_OWNER_STATUS: ${{ steps.check_approvals.outputs.codeOwnerStatus }} |