Skip to content

pr-approval-check: refactor, fix pagination, fix approval logic #10

pr-approval-check: refactor, fix pagination, fix approval logic

pr-approval-check: refactor, fix pagination, fix approval logic #10

name: PR Approval Check
on:
pull_request_review:
types: [submitted]
pull_request:
types: [opened, reopened, synchronize]
jobs:
request-team-review:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Request team review
uses: actions/github-script@v7
with:
script: |
const teamSlug = "OSS";
try {
await github.rest.pulls.requestReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
team_reviewers: [teamSlug] // Note: team_reviewers for teams
});
console.log(`✅ Successfully requested review from @${context.repo.owner}/${teamSlug}`);
} catch (error) {
// Don't fail if team already requested or other non-critical error
console.log(`⚠️ Could not request team review: ${error.message}`);
}
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 approvers = reviews.filter(review => review.state === 'APPROVED').map(a => a.user.login);
console.log(`Found ${approvers.length} approvers`);
if (approvers.length === 0) {
console.log('No approvers 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);
});
} catch (error) {
console.log('Error fetching child teams, will only check parent team:', error.status);
}
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/product-eng: ${username}`);
return true;
}
} catch (error) {
if (error.status !== 404) {
console.error(`⚠️ Error checking membership for ${username} in @trufflesecurity team:`, error.status);
}
}
}
return false;
}
// Get all teams to check (parent + children)
const teamsToCheck = await getTeamsToCheck();
let hasProductEngApproval = false;
let approverLogin = null;
for (const approver of approvers) {
const membershipCheck = await isUserMemberOfAnyTeam(approver, teamsToCheck);
if (membershipCheck.isMember) {
hasProductEngApproval = true;
approverLogin = approver;
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/product-eng team member (${approverLogin})`
});
} 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'
});
}