feat: hybrid modeling #8
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: Random Review Assignment | |
| on: | |
| pull_request: | |
| types: [opened, ready_for_review] | |
| permissions: | |
| pull-requests: write | |
| contents: read | |
| jobs: | |
| assign: | |
| runs-on: ubuntu-latest | |
| if: github.event.pull_request.draft == false | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| sparse-checkout: .github/CODEOWNERS | |
| sparse-checkout-cone-mode: false | |
| - uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| // Get already requested reviewers | |
| const { data: pullRequest } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.pull_request.number | |
| }); | |
| const existingReviewers = pullRequest.requested_reviewers.map(r => r.login); | |
| console.log(`Already assigned: ${existingReviewers.join(', ')}`); | |
| // Parse CODEOWNERS for all @mentions (both users and teams) | |
| const content = fs.readFileSync('.github/CODEOWNERS', 'utf8'); | |
| const userMentions = content.match(/@[\w-]+(?!\/)/g) || []; | |
| const teamMentions = content.match(/@[\w-]+\/[\w-]+/g) || []; | |
| let allReviewers = [...new Set(userMentions.map(m => m.substring(1)))]; | |
| // Expand team mentions to get individual members | |
| for (const teamMention of [...new Set(teamMentions)]) { | |
| const [org, team] = teamMention.substring(1).split('/'); | |
| try { | |
| const { data: members } = await github.rest.teams.listMembersInOrg({ | |
| org: org, | |
| team_slug: team, | |
| per_page: 100 | |
| }); | |
| allReviewers.push(...members.map(m => m.login)); | |
| } catch (error) { | |
| console.log(`Warning: Could not fetch team ${teamMention}: ${error.message}`); | |
| } | |
| } | |
| // Remove duplicates | |
| allReviewers = [...new Set(allReviewers)]; | |
| // Filter out PR author and already assigned reviewers | |
| const author = context.payload.pull_request.user.login; | |
| const eligible = allReviewers.filter(r => | |
| r !== author && !existingReviewers.includes(r) | |
| ); | |
| // Pick one additional reviewer | |
| const needToAdd = 1; | |
| if (needToAdd > 0 && eligible.length > 0) { | |
| // Randomly select only the additional reviewers needed | |
| const selected = eligible | |
| .sort(() => Math.random() - 0.5) | |
| .slice(0, Math.min(needToAdd, eligible.length)); | |
| await github.rest.pulls.requestReviewers({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.payload.pull_request.number, | |
| reviewers: selected | |
| }); | |
| console.log(`✅ Added reviewer: ${selected[0]}`); | |
| } else { | |
| console.log(`⚠️ No eligible reviewers available to add`); | |
| } |