diff --git a/.github/workflows/enforce-group-approvals.yml b/.github/workflows/enforce-group-approvals.yml index 6c9bbfe641..b239e922d6 100644 --- a/.github/workflows/enforce-group-approvals.yml +++ b/.github/workflows/enforce-group-approvals.yml @@ -15,46 +15,103 @@ jobs: uses: actions/github-script@v6 with: script: | - // Define the team slugs - const QA_TEAM = 'qa'; - const DEV_TEAM = 'explorer-devs'; - - // Fetch team members dynamically - const fetchTeamMembers = async (teamSlug) => { - const { data: teamMembers } = await github.rest.teams.listMembersInOrg({ - org: context.repo.owner, - team_slug: teamSlug, - }); - return teamMembers.map(member => member.login); + const fetchTeamMembers = async (teamSlug, token) => { + const url = `https://api.github.com/orgs/${context.repo.owner}/teams/${teamSlug}/members`; + console.log(`Fetching team members for team: ${teamSlug}`); + + try { + const response = await fetch(url, { + method: "GET", + headers: { + authorization: `Bearer ${token}`, + accept: "application/vnd.github.v3+json", + }, + }); + + console.log("Response Status:", response.status, response.statusText); + + const data = await response.json(); + if (!response.ok) { + console.error("Response Headers:", JSON.stringify([...response.headers], null, 2)); + throw new Error(`HTTP ${response.status} - ${data.message}`); + } + + console.log(`Fetched Members for ${teamSlug}:`, data.map(member => member.login)); + return data.map(member => member.login); + } catch (error) { + console.error(`Error fetching team members for ${teamSlug}:`, error.message); + throw error; + } + }; + + const fetchPullRequestReviews = async (token) => { + if (!context.payload.pull_request) { + throw new Error("This workflow must be triggered by a pull_request event."); + } + + const url = `https://api.github.com/repos/${context.repo.owner}/${context.repo.repo}/pulls/${context.payload.pull_request.number}/reviews`; + console.log("Fetching PR reviews..."); + + try { + const response = await fetch(url, { + method: "GET", + headers: { + authorization: `Bearer ${token}`, + accept: "application/vnd.github.v3+json", + }, + }); + + console.log("Response Status:", response.status, response.statusText); + + const data = await response.json(); + if (!response.ok) { + console.error("Response Headers:", JSON.stringify([...response.headers], null, 2)); + throw new Error(`HTTP ${response.status} - ${data.message}`); + } + + console.log("Fetched PR Reviews:", data); + return data; + } catch (error) { + console.error("Error fetching PR reviews:", error.message); + throw error; + } }; + const token = process.env.ORG_ACCESS_TOKEN; + const QA_TEAM = "qa"; + const DEV_TEAM = "explorer-devs"; + // Fetch team members - const qaMembers = await fetchTeamMembers(QA_TEAM); - const devMembers = await fetchTeamMembers(DEV_TEAM); + const qaMembers = await fetchTeamMembers(QA_TEAM, token); + const devMembers = await fetchTeamMembers(DEV_TEAM, token); - // Fetch reviews for the PR - const { data: reviews } = await github.rest.pulls.listReviews({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.pull_request.number, - }); + // Fetch PR reviews + const reviews = await fetchPullRequestReviews(token); // Filter unique APPROVED reviews - const uniqueApprovals = reviews.filter((review, index, self) => - self.findIndex(r => r.user.login === review.user.login && r.state === 'APPROVED') === index + const uniqueApprovals = reviews.filter( + (review, index, self) => + review.state === "APPROVED" && + self.findIndex(r => r.user.login === review.user.login) === index ); - // Check if there's at least 1 QA approval - const hasQaApproval = uniqueApprovals.some(review => - qaMembers.includes(review.user.login) - ); + console.log("Unique Approvals:", uniqueApprovals.map(approval => approval.user.login)); - // Check if there's at least 1 approval from DEV - const hasDevApproval = uniqueApprovals.some(review => - devMembers.includes(review.user.login) - ); + // Validate approvals + const hasQaApproval = uniqueApprovals.some(review => qaMembers.includes(review.user.login)); + const hasDevApproval = uniqueApprovals.some(review => devMembers.includes(review.user.login)); - // Fail if criteria are not met if (!hasQaApproval || !hasDevApproval) { - core.setFailed('PR must have at least 1 QA approval and 1 DEV approval.'); + const missing = [ + !hasQaApproval ? "QA approval" : null, + !hasDevApproval ? "DEV approval" : null, + ] + .filter(Boolean) + .join(" and "); + + throw new Error(`PR must have at least 1 ${missing}.`); } + + console.log("PR has the required approvals."); + env: + ORG_ACCESS_TOKEN: ${{ secrets.ORG_ACCESS_TOKEN }}