@@ -36,16 +36,25 @@ function loadMentorRoster() {
3636 }
3737}
3838
39- function selectMentor ( roster ) {
39+ function selectMentor ( roster , mentee ) {
4040 if ( ! Array . isArray ( roster ) || roster . length === 0 ) {
4141 throw new Error ( 'Mentor roster must contain at least one entry.' ) ;
4242 }
4343
4444 const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000 ;
4545 const dayNumber = Math . floor ( Date . now ( ) / MILLISECONDS_PER_DAY ) ; // UTC day index
46- const index = dayNumber % roster . length ;
46+ const baseIndex = dayNumber % roster . length ;
47+ const normalizedMentee = typeof mentee === 'string' ? mentee . toLowerCase ( ) : null ;
4748
48- return roster [ index ] ;
49+ for ( let offset = 0 ; offset < roster . length ; offset += 1 ) {
50+ const candidate = roster [ ( baseIndex + offset ) % roster . length ] ;
51+
52+ if ( ! normalizedMentee || candidate . toLowerCase ( ) !== normalizedMentee ) {
53+ return candidate ;
54+ }
55+ }
56+
57+ return null ;
4958}
5059
5160function hasGoodFirstIssueLabel ( issue ) {
@@ -55,6 +64,41 @@ function hasGoodFirstIssueLabel(issue) {
5564 } ) ;
5665}
5766
67+ async function hasActiveMentorAssignment ( github , owner , repo , mentee , currentIssueNumber ) {
68+ try {
69+ const assignedIssues = await github . paginate (
70+ github . rest . issues . listForRepo ,
71+ {
72+ owner,
73+ repo,
74+ assignee : mentee ,
75+ state : 'open' ,
76+ per_page : 100 ,
77+ } ,
78+ ( response ) =>
79+ ( response . data || [ ] ) . filter ( ( issue ) => issue ?. number && issue . number !== currentIssueNumber ) ,
80+ ) ;
81+
82+ for ( const assignedIssue of assignedIssues ) {
83+ const comments = await github . paginate ( github . rest . issues . listComments , {
84+ owner,
85+ repo,
86+ issue_number : assignedIssue . number ,
87+ per_page : 100 ,
88+ } ) ;
89+
90+ if ( comments . some ( ( comment ) => comment . body ?. includes ( COMMENT_MARKER ) ) ) {
91+ return true ;
92+ }
93+ }
94+ } catch ( error ) {
95+ const message = error instanceof Error ? error . message : String ( error ) ;
96+ console . log ( `Unable to detect existing mentor assignments for ${ mentee } : ${ message } ` ) ;
97+ }
98+
99+ return false ;
100+ }
101+
58102async function isNewContributor ( github , owner , repo , login ) {
59103 const query = `repo:${ owner } /${ repo } type:pr state:closed is:merged author:${ login } ` ;
60104
@@ -132,19 +176,44 @@ module.exports = async ({ github, context }) => {
132176 return console . log ( `${ mentee } already has merged contributions. Skipping mentor assignment.` ) ;
133177 }
134178
179+ if ( await hasActiveMentorAssignment ( github , owner , repo , mentee , issue . number ) ) {
180+ return console . log ( `${ mentee } already has an active mentor assignment comment on another issue. Skipping.` ) ;
181+ }
182+
135183 const roster = loadMentorRoster ( ) ;
136- const mentor = selectMentor ( roster ) ;
184+ const mentor = selectMentor ( roster , mentee ) ;
185+
186+ if ( ! mentor ) {
187+ return console . log ( `No eligible mentor (excluding mentee ${ mentee } ) found. Skipping mentor assignment.` ) ;
188+ }
137189
138190 console . log ( `Assigning mentor @${ mentor } to mentee @${ mentee } for issue #${ issue . number } .` ) ;
139191
140192 const comment = buildComment ( { mentee, mentor, owner, repo } ) ;
141193
142- await github . rest . issues . createComment ( {
143- owner,
144- repo,
145- issue_number : issue . number ,
146- body : comment ,
147- } ) ;
194+ try {
195+ await github . rest . issues . createComment ( {
196+ owner,
197+ repo,
198+ issue_number : issue . number ,
199+ body : comment ,
200+ } ) ;
201+ } catch ( error ) {
202+ const message = error instanceof Error ? error . message : String ( error ) ;
203+
204+ const freshComments = await github . paginate ( github . rest . issues . listComments , {
205+ owner,
206+ repo,
207+ issue_number : issue . number ,
208+ per_page : 100 ,
209+ } ) ;
210+
211+ if ( freshComments . some ( ( existing ) => existing . body ?. includes ( COMMENT_MARKER ) ) ) {
212+ return console . log ( `Mentor assignment comment already exists on issue #${ issue . number } after concurrent run. Skipping. (${ message } )` ) ;
213+ }
214+
215+ throw error ;
216+ }
148217 } catch ( error ) {
149218 const message = error instanceof Error ? error . message : String ( error ) ;
150219 console . log ( `❌ Mentor assignment failed: ${ message } ` ) ;
0 commit comments