Skip to content

Commit 0465e24

Browse files
matthew29tangcopybara-github
authored andcommitted
chore: Add GitHub workflow to require 2 approvals for major releases
PiperOrigin-RevId: 938268484
1 parent 1870b96 commit 0465e24

1 file changed

Lines changed: 72 additions & 0 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# This workflow blocks release-please PRs for major versions unless they have at least 2 approvals.
2+
name: Enforce Multiple Approvals for Major Releases
3+
4+
on:
5+
pull_request_review:
6+
types: [submitted, dismissed]
7+
pull_request:
8+
types: [opened, synchronized, reopened]
9+
10+
jobs:
11+
check-major-approval:
12+
runs-on: ubuntu-latest
13+
if: github.event.pull_request.user.login == 'release-please[bot]'
14+
permissions:
15+
pull-requests: read
16+
steps:
17+
- name: Enforce Approvals for Major Releases
18+
uses: actions/github-script@v7
19+
with:
20+
script: |
21+
const prTitle = context.payload.pull_request.title;
22+
console.log(`PR Title: ${prTitle}`);
23+
24+
// 1. Extract proposed version securely from the PR title
25+
const versionMatch = prTitle.match(/\b\d+\.\d+\.\d+\b/);
26+
if (!versionMatch) {
27+
console.log("No version pattern found in the PR title. Skipping check.");
28+
return;
29+
}
30+
31+
const version = versionMatch[0];
32+
console.log(`Extracted Version: ${version}`);
33+
34+
// 2. Identify if this is a major release (ends with .0.0)
35+
if (!version.endsWith('.0.0')) {
36+
console.log("This is a minor or patch version release. Skipping check.");
37+
return;
38+
}
39+
40+
console.log(`MAJOR version release detected (${version})! Verifying approvals...`);
41+
42+
const prNumber = context.payload.pull_request.number;
43+
const owner = context.repo.owner;
44+
const repo = context.repo.repo;
45+
46+
// 3. Fetch reviews for the PR
47+
const { data: reviews } = await github.rest.pulls.listReviews({
48+
owner,
49+
repo,
50+
pull_number: prNumber,
51+
});
52+
53+
// 4. Count unique approvals from authorized maintainers (Googlers)
54+
const approvals = {};
55+
for (const review of reviews) {
56+
const isAuthorizedGoogler = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(review.author_association);
57+
58+
if (review.state === 'APPROVED' && isAuthorizedGoogler) {
59+
approvals[review.user.login] = true;
60+
} else if (review.state === 'CHANGES_REQUESTED' || review.state === 'DISMISSED') {
61+
delete approvals[review.user.login];
62+
}
63+
}
64+
65+
const approvalCount = Object.keys(approvals).length;
66+
console.log(`Current approved reviews by authorized maintainers: ${Object.keys(approvals).join(', ')} (${approvalCount})`);
67+
68+
if (approvalCount < 2) {
69+
core.setFailed(`Major version releases (${version}) require at least 2 approvals from authorized maintainers. Current approval count: ${approvalCount}.`);
70+
} else {
71+
console.log("Approval requirements successfully met.");
72+
}

0 commit comments

Comments
 (0)