diff --git a/.github/workflows/check-team-approval.yml b/.github/workflows/check-team-approval.yml new file mode 100644 index 0000000000..f010a7907f --- /dev/null +++ b/.github/workflows/check-team-approval.yml @@ -0,0 +1,96 @@ +name: Check Team Approval + +on: + workflow_call: + inputs: + pr_number: + required: true + type: number + description: 'Pull request number to check' + outputs: + has_approval: + description: 'Whether the PR has approval from a team/admin member' + value: ${{ jobs.check-approval.outputs.has_approval }} + +jobs: + check-approval: + runs-on: ubuntu-latest + permissions: + pull-requests: read + contents: read + outputs: + has_approval: ${{ steps.check-review.outputs.has_approval }} + + steps: + - name: Check for team/admin review + id: check-review + uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const prNumber = ${{ inputs.pr_number }}; + + console.log(`Checking reviews for PR #${prNumber}`); + + try { + // Fetch the users.json file from immich-app/devtools repository + const { data: usersFile } = await github.rest.repos.getContent({ + owner: 'immich-app', + repo: 'devtools', + path: 'tf/deployment/data/users.json' + }); + + const usersData = JSON.parse(Buffer.from(usersFile.content, 'base64').toString()); + console.log(`Loaded ${usersData.length} users from devtools repo`); + + // Create a map of GitHub IDs to user roles for efficient lookup + const userRoles = new Map(); + for (const user of usersData) { + if (user.github && user.github.id && (user.role === 'team' || user.role === 'admin')) { + userRoles.set(user.github.id, { + username: user.github.username, + role: user.role + }); + } + } + + console.log(`Found ${userRoles.size} team/admin users`); + + // Get all reviews for the pull request + const { data: reviews } = await github.rest.pulls.listReviews({ + owner, + repo, + pull_number: prNumber + }); + + console.log(`Found ${reviews.length} reviews`); + + // Check if any review is from a team/admin member + let hasValidReview = false; + + for (const review of reviews) { + console.log(`Review by ${review.user.login} (ID: ${review.user.id}): state=${review.state}`); + + // Check if the reviewer is a team/admin member and the review is approved + const userInfo = userRoles.get(review.user.id); + if (userInfo && review.state === 'APPROVED') { + console.log(`✅ Found approved review from ${userInfo.role} member: ${review.user.login}`); + hasValidReview = true; + break; + } + } + + // Set output for the workflow + core.setOutput('has_approval', hasValidReview.toString()); + + if (!hasValidReview) { + console.log('❌ No approved review from team/admin member found'); + core.setFailed('This pull request requires an approved review from a team or admin member'); + } else { + console.log('✅ Required team/admin member review found'); + } + + } catch (error) { + console.error('Error checking reviews:', error); + core.setFailed(`Failed to check reviews: ${error.message}`); + } diff --git a/.github/workflows/required-reviewers.yml b/.github/workflows/required-reviewers.yml index 075465b659..a2c615087f 100644 --- a/.github/workflows/required-reviewers.yml +++ b/.github/workflows/required-reviewers.yml @@ -6,77 +6,9 @@ on: jobs: check-member-review: - runs-on: ubuntu-latest + uses: ./.github/workflows/check-team-approval.yml + with: + pr_number: ${{ github.event.pull_request.number }} permissions: pull-requests: read contents: read - - steps: - - name: Check for team/admin review - uses: actions/github-script@v7 - with: - script: | - const { owner, repo } = context.repo; - const prNumber = context.payload.pull_request.number; - - console.log(`Checking reviews for PR #${prNumber}`); - - try { - // Fetch the users.json file from immich-app/devtools repository - const { data: usersFile } = await github.rest.repos.getContent({ - owner: 'immich-app', - repo: 'devtools', - path: 'tf/deployment/data/users.json' - }); - - const usersData = JSON.parse(Buffer.from(usersFile.content, 'base64').toString()); - console.log(`Loaded ${usersData.length} users from devtools repo`); - - // Create a map of GitHub IDs to user roles for efficient lookup - const userRoles = new Map(); - for (const user of usersData) { - if (user.github && user.github.id && (user.role === 'team' || user.role === 'admin')) { - userRoles.set(user.github.id, { - username: user.github.username, - role: user.role - }); - } - } - - console.log(`Found ${userRoles.size} team/admin users`); - - // Get all reviews for the pull request - const { data: reviews } = await github.rest.pulls.listReviews({ - owner, - repo, - pull_number: prNumber - }); - - console.log(`Found ${reviews.length} reviews`); - - // Check if any review is from a team/admin member - let hasValidReview = false; - - for (const review of reviews) { - console.log(`Review by ${review.user.login} (ID: ${review.user.id}): state=${review.state}`); - - // Check if the reviewer is a team/admin member and the review is approved - const userInfo = userRoles.get(review.user.id); - if (userInfo && review.state === 'APPROVED') { - console.log(`✅ Found approved review from ${userInfo.role} member: ${review.user.login}`); - hasValidReview = true; - break; - } - } - - if (!hasValidReview) { - console.log('❌ No approved review from team/admin member found'); - core.setFailed('This pull request requires an approved review from a team or admin member'); - } else { - console.log('✅ Required team/admin member review found'); - } - - } catch (error) { - console.error('Error checking reviews:', error); - core.setFailed(`Failed to check reviews: ${error.message}`); - }