Skip to content

Require product eng approval for PRs #8

Require product eng approval for PRs

Require product eng approval for PRs #8

name: PR Approval Check
on:
pull_request_review:
types: [submitted]
pull_request:
types: [opened, reopened, synchronize]
jobs:
check-product-eng-approval:
name: Check Product Eng Approval
runs-on: ubuntu-latest
steps:
- name: Check for Product Engineering approval
uses: actions/github-script@v7
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.payload.pull_request.number
});
console.log(`Found ${reviews.length} reviews`);
// Get approved reviews
const approvals = reviews.filter(review => review.state === 'APPROVED');
console.log(`Found ${approvals.length} approvals`);
if (approvals.length === 0) {
console.log('No approvals found');
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.pull_request.head.sha,
state: 'pending',
context: 'product-eng-approval',
description: 'Waiting for approval from @trufflesecurity/product-eng team member'
});
return;
}
// Helper function to get all teams to check (parent + children)
async function getTeamsToCheck() {
const teamsToCheck = ['product-eng']; // Start with parent team
try {
// Get child teams of product-eng
const { data: childTeams } = await github.rest.teams.listChildInOrg({
org: 'trufflesecurity',
team_slug: 'product-eng'
});
// Add child team slugs
childTeams.forEach(team => {
teamsToCheck.push(team.slug);
});
console.log(`Teams to check: ${teamsToCheck.join(', ')}`);
} catch (error) {
console.log('Error fetching child teams, will only check parent team:', error.message);
}
return teamsToCheck;
}
// Helper function to check if user is member of any team
async function isUserMemberOfAnyTeam(username, teams) {
for (const teamSlug of teams) {
try {
const { data: membership } = await github.rest.teams.getMembershipForUserInOrg({
org: 'trufflesecurity',
team_slug: teamSlug,
username: username
});
if (membership.state === 'active') {
console.log(`✅ Found active member of @trufflesecurity/${teamSlug}: ${username}`);
return { isMember: true, teamSlug };
}
} catch (error) {
// User is not a member of this team (404) or other error
console.log(`❌ ${username} is not a member of @trufflesecurity/${teamSlug}`);
}
}
return { isMember: false, teamSlug: null };
}
// Get all teams to check (parent + children)
const teamsToCheck = await getTeamsToCheck();
// Check if any approver is a member of product-eng or its child teams
let hasProductEngApproval = false;
let approverInfo = null;
for (const approval of approvals) {
const membershipCheck = await isUserMemberOfAnyTeam(approval.user.login, teamsToCheck);
if (membershipCheck.isMember) {
hasProductEngApproval = true;
approverInfo = {
username: approval.user.login,
teamSlug: membershipCheck.teamSlug
};
break;
}
}
if (hasProductEngApproval) {
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.pull_request.head.sha,
state: 'success',
context: 'product-eng-approval',
description: `✅ Approved by @trufflesecurity/${approverInfo.teamSlug} member (${approverInfo.username})`
});
} else {
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.pull_request.head.sha,
state: 'failure',
context: 'product-eng-approval',
description: '❌ Requires approval from @trufflesecurity/product-eng team member'
});
}