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 / ^ t r u e $ / 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