Brand contributor check comments as AgenTrust #8
Workflow file for this run
This file contains hidden or 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
| # Requires a named-maintainer review before merge for external contributors. | |
| # AI-only approvals and bot approvals do not count. | |
| # Branch protection rules enforce the code-owner review requirement. | |
| name: "Policy: Awaiting maintainer review" | |
| on: | |
| pull_request_target: | |
| types: [opened, synchronize, reopened] | |
| branches: [main] | |
| # Re-evaluate the gate when a review is submitted or dismissed so an | |
| # approval clears the failing status without waiting for a new push. | |
| # Note: pull_request_review does not support a branches filter, so the | |
| # base-branch check lives in the job-level `if` below. | |
| pull_request_review: | |
| types: [submitted, dismissed] | |
| permissions: | |
| contents: read | |
| jobs: | |
| check-approval: | |
| name: "Policy: Awaiting maintainer review" | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| if: github.event.pull_request.base.ref == 'main' | |
| steps: | |
| - name: Check for named-maintainer review | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const MAINTAINERS = ['imran-siddique']; | |
| // Maintainer-authored PRs skip the gate. author_association is | |
| // not reliable for this: it only reports MEMBER when the | |
| // author's org membership is public, and a PR author cannot | |
| // approve their own PR, so without this skip a maintainer with | |
| // private membership could never clear the gate. | |
| const author = context.payload.pull_request.user.login; | |
| if (MAINTAINERS.includes(author)) { | |
| core.info(`Author ${author} is a maintainer, skipping gate`); | |
| return; | |
| } | |
| const association = context.payload.pull_request.author_association; | |
| if (association === 'MEMBER' || association === 'OWNER') { | |
| core.info(`Author is ${association}, skipping gate`); | |
| return; | |
| } | |
| // Both pull_request_target and pull_request_review payloads | |
| // carry the PR at context.payload.pull_request. | |
| const prNumber = context.payload.pull_request.number; | |
| // Re-fetch the PR so we compare against the head SHA at | |
| // evaluation time, not a possibly stale SHA from the payload. | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber, | |
| }); | |
| const headSha = pr.head.sha; | |
| // Paginate: listReviews otherwise returns only the first 30. | |
| const reviews = await github.paginate(github.rest.pulls.listReviews, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber, | |
| }); | |
| // Only a maintainer's most recent non-comment review counts, | |
| // and it must approve the current head commit. An approval of | |
| // an older commit does not clear the gate after a later push | |
| // (prevents approve-then-swap). | |
| const latestByMaintainer = new Map(); | |
| for (const review of reviews) { | |
| if ( | |
| review.user && | |
| review.user.type === 'User' && | |
| MAINTAINERS.includes(review.user.login) && | |
| review.state !== 'COMMENTED' | |
| ) { | |
| latestByMaintainer.set(review.user.login, review); | |
| } | |
| } | |
| const maintainerApproval = [...latestByMaintainer.values()].find( | |
| (r) => r.state === 'APPROVED' && r.commit_id === headSha | |
| ); | |
| if (!maintainerApproval) { | |
| core.setFailed( | |
| 'This PR is awaiting a named-maintainer review of the current head commit.\n' + | |
| 'This is a policy gate, not a CI failure: the PR is mergeable once a named maintainer approves.\n' + | |
| 'Approvals of earlier commits do not count after new pushes.\n' + | |
| 'See CODEOWNERS or MAINTAINERS.md for the list of maintainers.' | |
| ); | |
| } else { | |
| core.info( | |
| `Maintainer @${maintainerApproval.user.login} approved this PR at head ${headSha}.` | |
| ); | |
| } |