@@ -7,6 +7,10 @@ const EXEMPT_PERMISSION_LEVELS = (process.env.INTERMEDIATE_EXEMPT_PERMISSIONS ||
77 . filter ( Boolean ) ;
88const DRY_RUN = / ^ t r u e $ / i. test ( process . env . DRY_RUN || '' ) ;
99
10+ function isSafeSearchToken ( value ) {
11+ return typeof value === 'string' && / ^ [ a - z A - Z 0 - 9 . _ / - ] + $ / . test ( value ) ;
12+ }
13+
1014function hasLabel ( issue , labelName ) {
1115 if ( ! issue ?. labels ?. length ) {
1216 return false ;
@@ -52,63 +56,53 @@ async function hasExemptPermission(github, owner, repo, username) {
5256}
5357
5458async function countCompletedBeginnerIssues ( github , owner , repo , username ) {
59+ if (
60+ ! isSafeSearchToken ( owner ) ||
61+ ! isSafeSearchToken ( repo ) ||
62+ ! isSafeSearchToken ( username ) ||
63+ ! isSafeSearchToken ( BEGINNER_LABEL )
64+ ) {
65+ console . log ( 'Invalid search inputs' , {
66+ owner,
67+ repo,
68+ username,
69+ label : BEGINNER_LABEL ,
70+ } ) ;
71+ return null ;
72+ }
73+
5574 try {
56- console . log ( `Checking closed '${ BEGINNER_LABEL } ' issues in ${ owner } /${ repo } for ${ username } ` ) ;
57-
58- const query = `
59- query ($owner: String!, $repo: String!) {
60- repository(owner: $owner, name: $repo) {
61- issues(
62- first: 10
63- states: CLOSED
64- labels: ["beginner"]
65- orderBy: { field: UPDATED_AT, direction: DESC }
66- ) {
67- nodes {
68- number
69- assignees(first: 10) {
70- nodes {
71- login
72- }
73- }
74- }
75- }
75+ const searchQuery = [
76+ `repo:${ owner } /${ repo } ` ,
77+ `label:${ BEGINNER_LABEL } ` ,
78+ 'is:issue' ,
79+ 'is:closed' ,
80+ `assignee:${ username } ` ,
81+ ] . join ( ' ' ) ;
82+
83+ console . log ( 'GraphQL search query:' , searchQuery ) ;
84+
85+ const result = await github . graphql (
86+ `
87+ query ($query: String!) {
88+ search(type: ISSUE, query: $query) {
89+ issueCount
7690 }
7791 }
78- ` ;
92+ ` ,
93+ { query : searchQuery }
94+ ) ;
7995
80- const data = await github . graphql ( query , { owner, repo } ) ;
81- const normalizedUsername = username . toLowerCase ( ) ;
82- const issues = data ?. repository ?. issues ?. nodes ?? [ ] ;
83- console . log (
84- `Retrieved ${ issues . length } closed Beginner issue(s) for evaluation` ) ;
85-
86- for ( const issue of issues ) {
87- const assignees = issue . assignees ?. nodes ?? [ ] ;
88-
89- const wasAssigned = assignees . some (
90- ( assignee ) =>
91- assignee ?. login ?. toLowerCase ( ) === normalizedUsername
92- ) ;
93-
94- console . log (
95- `Issue #${ issue . number } : assignees=[${ assignees
96- . map ( a => a . login )
97- . join ( ', ' ) } ], matched=${ wasAssigned } `
98- ) ;
99-
100- if ( wasAssigned ) {
101- console . log ( `Found completed Beginner issue #${ issue . number } for ${ username } ` ) ;
102- return 1 ;
103- }
104- }
96+ const count = result ?. search ?. issueCount ?? 0 ;
10597
106- console . log ( `No completed Beginner issues found for ${ username } ` ) ;
107- return 0 ;
98+ console . log ( `Completed Beginner issues for ${ username } : ${ count } ` ) ;
10899
100+ return count ;
109101 } catch ( error ) {
110102 const message = error instanceof Error ? error . message : String ( error ) ;
111- console . log ( `Unable to verify completed Beginner issues for ${ username } : ${ message } ` ) ;
103+ console . log (
104+ `Failed to count Beginner issues for ${ username } : ${ message } `
105+ ) ;
112106 return null ;
113107 }
114108}
0 commit comments