Skip to content

Commit ca832c2

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

4 files changed

Lines changed: 579 additions & 0 deletions
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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 individual reviewers as assignees on a Pull Request.
32+
* Team reviewers are intentionally ignored
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+
93+
if (assigneesList.length === 0) {
94+
console.log('[reviewers-assignee] No new users to assign. Done.');
95+
return;
96+
}
97+
console.log(`[reviewers-assignee] Will assign: ${assigneesList.join(', ')}`);
98+
99+
if (dryRun) {
100+
console.log('[reviewers-assignee] DRY-RUN: Would have added the above users as assignees');
101+
return;
102+
}
103+
104+
// Assign both reviewers in a single API call
105+
try {
106+
await github.rest.issues.addAssignees({
107+
owner,
108+
repo,
109+
issue_number: prNumber,
110+
assignees: assigneesList
111+
});
112+
console.log(`[reviewers-assignee] Successfully added ${assigneesList.length} reviewer(s) as assignee(s) on PR #${prNumber}`);
113+
} catch (error) {
114+
if (error?.status === 403) {
115+
console.log('[reviewers-assignee] Insufficient permissions to add assignees');
116+
return;
117+
}
118+
console.error(`[reviewers-assignee] Failed to add assignees: ${error.message}`);
119+
throw error;
120+
}
121+
122+
} catch (error) {
123+
console.error('[reviewers-assignee] Error:', error.message);
124+
if (error.status) console.error('Status:', error.status);
125+
throw error;
126+
}
127+
};

0 commit comments

Comments
 (0)