Skip to content

Commit fb68438

Browse files
add reviewers as assignees on PRs
Signed-off-by: Siddhartha Ganguly <gangulysiddhartha22@gmail.com>
1 parent ce4713d commit fb68438

4 files changed

Lines changed: 501 additions & 0 deletions
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// =============================================================================
2+
// Add Reviewers as Assignees Bot
3+
// =============================================================================
4+
//
5+
// This script automatically adds requested reviewers
6+
// as assignees on a PR. Teams are explicitly ignored
7+
// Makes it easier for reviewers to find their PRs via the "Assigned to me" filter.
8+
//
9+
// Triggered by:
10+
// - pull_request review_requested event
11+
// - workflow_dispatch (for manual testing)
12+
//
13+
// Key behavior:
14+
// - Only adds users who aren't already assignees
15+
// - Limits to maximum 2 assignees
16+
// - Supports dry-run mode
17+
// - Comprehensive logging for debugging
18+
//
19+
// Called by: .github/workflows/bot-pr-add-reviewers-as-assignees.yml
20+
21+
/**
22+
* Checks if the script is running in dry-run mode.
23+
*
24+
* Returns true if DRY_RUN env var is set to "true" (case-insensitive).
25+
*/
26+
function isDryRun() {
27+
return /^true$/i.test(process.env.DRY_RUN || '');
28+
}
29+
30+
/**
31+
* Main handler that adds requested reviewers (individuals + expanded team members)
32+
* as assignees on a Pull Request.
33+
*
34+
* @param {Object} params
35+
* @param {Object} params.github - GitHub Octokit client from actions/github-script
36+
* @param {Object} params.context - GitHub Actions context
37+
* @returns {Promise<void>}
38+
*/
39+
module.exports = async ({ github, context }) => {
40+
const dryRun = isDryRun();
41+
if (dryRun) {
42+
console.log('[reviewers-assignee] Running in DRY-RUN mode - no changes will be made');
43+
}
44+
45+
try {
46+
let prNumber = context.payload.pull_request?.number;
47+
let owner = context.repo.owner;
48+
let repo = context.repo.repo;
49+
50+
// Support workflow_dispatch with manual PR number
51+
if (context.eventName === 'workflow_dispatch' && context.payload.inputs?.pr_number !== undefined) {
52+
const manualPrNumber = Number(context.payload.inputs.pr_number);
53+
if (!Number.isInteger(manualPrNumber) || manualPrNumber <= 0) {
54+
console.log('[reviewers-assignee] Invalid PR number supplied. Skipping.');
55+
return;
56+
}
57+
prNumber = manualPrNumber;
58+
}
59+
if (!Number.isInteger(prNumber) || prNumber <= 0) {
60+
console.log('[reviewers-assignee] No PR number found. Skipping.');
61+
return;
62+
}
63+
64+
console.log(`[reviewers-assignee] Processing PR #${prNumber} in ${owner}/${repo}`);
65+
66+
// Fetch fresh PR data
67+
const { data: pr } = await github.rest.pulls.get({
68+
owner,
69+
repo,
70+
pull_number: prNumber
71+
});
72+
73+
const requestedReviewers = pr.requested_reviewers || [];
74+
const requestedTeams = pr.requested_teams || [];
75+
const currentAssignees = new Set(pr.assignees?.map(a => a.login) || []);
76+
77+
console.log(`[reviewers-assignee] Requested reviewers: ${requestedReviewers.length}, teams: ${requestedTeams.length}`);
78+
if (requestedTeams.length > 0) {
79+
console.log('[reviewers-assignee] Team reviewers detected but ignored by design.');
80+
}
81+
82+
const usersToAssign = new Set();
83+
84+
// Add individual reviewers
85+
for (const reviewer of requestedReviewers) {
86+
if (reviewer?.login && !currentAssignees.has(reviewer.login)) {
87+
usersToAssign.add(reviewer.login);
88+
}
89+
}
90+
91+
const assigneesList = Array.from(usersToAssign).slice(0, 2);
92+
console.log(`[reviewers-assignee] Will assign: ${assigneesList.join(', ')}`);
93+
94+
if (dryRun) {
95+
console.log('[reviewers-assignee] DRY-RUN: Would have added the above users as assignees');
96+
return;
97+
}
98+
99+
// Assign both reviewers in a single API call
100+
try {
101+
await github.rest.issues.addAssignees({
102+
owner,
103+
repo,
104+
issue_number: prNumber,
105+
assignees: assigneesList
106+
});
107+
console.log(`[reviewers-assignee] Successfully added ${assigneesList.length} reviewer(s) as assignee(s) on PR #${prNumber}`);
108+
} catch (error) {
109+
if (error?.status === 403) {
110+
console.log('[reviewers-assignee] Insufficient permissions to add assignees');
111+
return;
112+
}
113+
console.error(`[reviewers-assignee] Failed to add assignees: ${error.message}`);
114+
throw error;
115+
}
116+
117+
} catch (error) {
118+
console.error('[reviewers-assignee] Error:', error.message);
119+
if (error.status) console.error('Status:', error.status);
120+
throw error;
121+
}
122+
};

0 commit comments

Comments
 (0)