Documentation Team Approval Check #28348
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
| name: Documentation Team Approval Check | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, review_requested] | |
| pull_request_review: | |
| types: [submitted, dismissed] | |
| permissions: {} | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| check-documentation-team-approval: | |
| runs-on: ubuntu-latest | |
| if: github.event.pull_request.head.repo.full_name == github.repository | |
| permissions: | |
| pull-requests: write | |
| contents: read | |
| id-token: write # Needed to federate tokens. | |
| outputs: | |
| approval_status: ${{ steps.check_approvals.outputs.approval_status }} | |
| team_requested: ${{ steps.check_approvals.outputs.team_requested }} | |
| steps: | |
| - uses: DataDog/dd-octo-sts-action@08f2144903ced3254a3dafec2592563409ba2aa0 # v1.0.1 | |
| id: octo-sts | |
| with: | |
| scope: DataDog | |
| policy: documentation.read-org-members | |
| - name: Check Documentation Team Approval | |
| id: check_approvals | |
| uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7 | |
| env: | |
| GITHUB_TOKEN: ${{ steps.octo-sts.outputs.token }} | |
| with: | |
| github-token: ${{ env.GITHUB_TOKEN }} | |
| script: | | |
| const { owner, repo, number } = context.issue; | |
| // Get pull request details | |
| const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: number }); | |
| // Skip if not targeting master | |
| if (pr.base.ref !== 'master') { | |
| console.log(`Base branch is ${pr.base.ref}. Skipping check.`); | |
| return; | |
| } | |
| // Define the documentation team slug (also check for webops-platform) | |
| const DOCUMENTATION_TEAM = 'documentation'; | |
| const WEBOPSTEAM = 'webops-platform'; | |
| // Get all reviews | |
| const { data: reviews } = await github.rest.pulls.listReviews({ | |
| owner, | |
| repo, | |
| pull_number: number, | |
| }); | |
| // Track latest review state per user | |
| const latestReviews = new Map(); | |
| reviews.forEach(review => { | |
| const existing = latestReviews.get(review.user.login); | |
| if (!existing || review.submitted_at > existing.submitted_at) { | |
| latestReviews.set(review.user.login, review); | |
| } | |
| }); | |
| // Get current approvals (will be filtered by team membership later) | |
| const allApprovals = new Set( | |
| Array.from(latestReviews.values()) | |
| .filter(review => review.state === 'APPROVED') | |
| .map(review => review.user.login) | |
| ); | |
| // Check if documentation team is required via CODEOWNERS (requested reviewers) | |
| let hasDocumentationApproval = false; | |
| let isDocumentationTeamRequested = false; | |
| try { | |
| // Get requested reviewers to see if documentation team is currently requested | |
| const { data: reviewers } = await github.rest.pulls.listRequestedReviewers({ | |
| owner, | |
| repo, | |
| pull_number: number, | |
| }); | |
| // Check if documentation or webops-platform teams are currently in requested reviewers | |
| const currentlyRequested = reviewers.teams.some(team => | |
| team.slug === DOCUMENTATION_TEAM || team.slug === WEBOPSTEAM | |
| ); | |
| // Also check if any documentation or webops-platform team member has reviewed (indicating they were requested) | |
| const { data: docTeamMembers } = await github.rest.teams.listMembersInOrg({ | |
| org: owner, | |
| team_slug: DOCUMENTATION_TEAM, | |
| }); | |
| const { data: webopsTeamMembers } = await github.rest.teams.listMembersInOrg({ | |
| org: owner, | |
| team_slug: WEBOPSTEAM, | |
| }); | |
| const teamMembers = [...docTeamMembers, ...webopsTeamMembers]; | |
| const teamMemberLogins = new Set(teamMembers.map(member => member.login)); | |
| const hasTeamMemberReview = reviews.some(review => | |
| teamMemberLogins.has(review.user.login) | |
| ); | |
| // Documentation team is considered "requested" if: | |
| // 1. They are currently in requested reviewers, OR | |
| // 2. Any team member has already reviewed (meaning they were requested but fulfilled the request) | |
| isDocumentationTeamRequested = currentlyRequested || hasTeamMemberReview; | |
| console.log(`Documentation team currently requested: ${currentlyRequested}`); | |
| console.log(`Documentation team member has reviewed: ${hasTeamMemberReview}`); | |
| console.log(`Documentation team considered requested: ${isDocumentationTeamRequested}`); | |
| if (isDocumentationTeamRequested) { | |
| // Check for approval from documentation team members only | |
| const docsTeamApprovals = teamMembers.filter(member => | |
| allApprovals.has(member.login) | |
| ); | |
| hasDocumentationApproval = docsTeamApprovals.length > 0; | |
| console.log(`Documentation team members who approved: ${docsTeamApprovals.map(m => m.login).join(', ')}`); | |
| console.log(`Documentation team approval status: ${hasDocumentationApproval}`); | |
| } else { | |
| // If documentation team is not requested, no approval needed | |
| hasDocumentationApproval = true; | |
| console.log('Documentation team not required for this PR - no approval needed.'); | |
| } | |
| } catch (error) { | |
| console.error(`Error checking documentation team: ${error}`); | |
| core.setFailed(`Error checking documentation team approval: ${error.message}`); | |
| return; | |
| } | |
| // Set output for the comment job | |
| core.setOutput('approval_status', hasDocumentationApproval); | |
| core.setOutput('team_requested', isDocumentationTeamRequested); | |
| // Set final status | |
| if (isDocumentationTeamRequested && !hasDocumentationApproval) { | |
| core.setFailed(`Team approval required from @${owner}/${DOCUMENTATION_TEAM} or @${owner}/${WEBOPSTEAM}`); | |
| } else if (!isDocumentationTeamRequested) { | |
| console.log('Documentation team review not required for this PR.'); | |
| } else { | |
| console.log('Documentation team has approved this PR.'); | |
| } | |
| update-pr-status: | |
| runs-on: ubuntu-latest | |
| needs: check-documentation-team-approval | |
| if: always() && github.event.pull_request.head.repo.full_name == github.repository | |
| permissions: | |
| pull-requests: write | |
| contents: read | |
| statuses: write | |
| steps: | |
| - name: Update PR status | |
| uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| github-token: ${{ env.GITHUB_TOKEN }} | |
| script: | | |
| const { owner, repo, number } = context.issue; | |
| const approvalStatus = '${{ needs.check-documentation-team-approval.outputs.approval_status }}' === 'true'; | |
| const teamRequested = '${{ needs.check-documentation-team-approval.outputs.team_requested }}' === 'true'; | |
| // Debug logging | |
| console.log('Raw outputs from first job:'); | |
| console.log('approval_status:', '${{ needs.check-documentation-team-approval.outputs.approval_status }}'); | |
| console.log('team_requested:', '${{ needs.check-documentation-team-approval.outputs.team_requested }}'); | |
| console.log('Parsed values:'); | |
| console.log('approvalStatus:', approvalStatus); | |
| console.log('teamRequested:', teamRequested); | |
| // Get pull request details for commit SHA | |
| const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: number }); | |
| // Create/update a consistent status check that both trigger types will update | |
| const statusState = (teamRequested && !approvalStatus) ? 'failure' : 'success'; | |
| const statusDescription = teamRequested | |
| ? (approvalStatus ? 'Documentation team has approved' : 'Documentation team approval required') | |
| : 'Documentation team review not required'; | |
| await github.rest.repos.createCommitStatus({ | |
| owner, | |
| repo, | |
| sha: pr.head.sha, | |
| state: statusState, | |
| target_url: `https://github.com/${owner}/${repo}/actions/runs/${context.runId}`, | |
| description: statusDescription, | |
| context: 'Documentation Team Approval' | |
| }); | |
| console.log(`Created commit status: ${statusState} - ${statusDescription}`); |