1- const { getInfo, getInfoFromPullRequest } = require ( '@changesets/get-github-info' ) ;
2-
31const repo = 'clerk/javascript' ;
2+ const [ owner , repoName ] = repo . split ( '/' ) ;
3+
4+ // Cache to avoid duplicate fetches for the same commit/PR
5+ const cache = new Map ( ) ;
6+
7+ // Simple concurrency limiter to avoid hitting GitHub secondary rate limits
8+ const MAX_CONCURRENT = 6 ;
9+ let active = 0 ;
10+ const queue = [ ] ;
11+
12+ function withLimit ( fn ) {
13+ return ( ...args ) =>
14+ new Promise ( ( resolve , reject ) => {
15+ const run = async ( ) => {
16+ active ++ ;
17+ try {
18+ resolve ( await fn ( ...args ) ) ;
19+ } catch ( e ) {
20+ reject ( e ) ;
21+ } finally {
22+ active -- ;
23+ if ( queue . length > 0 ) queue . shift ( ) ( ) ;
24+ }
25+ } ;
26+ if ( active < MAX_CONCURRENT ) run ( ) ;
27+ else queue . push ( run ) ;
28+ } ) ;
29+ }
30+
31+ async function graphql ( query ) {
32+ const token = process . env . GITHUB_TOKEN ;
33+ if ( ! token ) {
34+ throw new Error ( 'GITHUB_TOKEN environment variable is required' ) ;
35+ }
36+
37+ const res = await fetch ( 'https://api.github.com/graphql' , {
38+ method : 'POST' ,
39+ headers : {
40+ Authorization : `Token ${ token } ` ,
41+ 'Content-Type' : 'application/json' ,
42+ } ,
43+ body : JSON . stringify ( { query } ) ,
44+ } ) ;
45+
46+ if ( ! res . ok ) {
47+ throw new Error ( `GitHub API responded with ${ res . status } : ${ await res . text ( ) } ` ) ;
48+ }
49+
50+ const json = await res . json ( ) ;
51+ if ( json . errors ) {
52+ throw new Error ( `GitHub GraphQL error: ${ JSON . stringify ( json . errors , null , 2 ) } ` ) ;
53+ }
54+ if ( ! json . data ) {
55+ throw new Error ( `Unexpected GitHub response: ${ JSON . stringify ( json ) } ` ) ;
56+ }
57+ return json . data ;
58+ }
59+
60+ // Fetches commit info with a single small GraphQL query per commit
61+ const fetchCommitInfo = withLimit ( async commit => {
62+ const key = `commit:${ commit } ` ;
63+ if ( cache . has ( key ) ) return cache . get ( key ) ;
64+
65+ const data = await graphql ( `query {
66+ repository(owner: ${ JSON . stringify ( owner ) } , name: ${ JSON . stringify ( repoName ) } ) {
67+ object(expression: ${ JSON . stringify ( commit ) } ) {
68+ ... on Commit {
69+ commitUrl
70+ associatedPullRequests(first: 50) {
71+ nodes { number url mergedAt author { login url } }
72+ }
73+ author { user { login url } }
74+ }
75+ }
76+ }
77+ }` ) ;
78+
79+ const obj = data . repository . object ;
80+ if ( ! obj ) {
81+ const result = {
82+ user : null ,
83+ pull : null ,
84+ links : {
85+ commit : `[\`${ commit . slice ( 0 , 7 ) } \`](https://github.com/${ repo } /commit/${ commit } )` ,
86+ pull : null ,
87+ user : null ,
88+ } ,
89+ } ;
90+ cache . set ( key , result ) ;
91+ return result ;
92+ }
93+
94+ let user = obj . author && obj . author . user ? obj . author . user : null ;
95+ const associatedPR =
96+ obj . associatedPullRequests &&
97+ obj . associatedPullRequests . nodes &&
98+ obj . associatedPullRequests . nodes . length
99+ ? obj . associatedPullRequests . nodes . sort ( ( a , b ) => {
100+ if ( a . mergedAt === null && b . mergedAt === null ) return 0 ;
101+ if ( a . mergedAt === null ) return 1 ;
102+ if ( b . mergedAt === null ) return - 1 ;
103+ return new Date ( b . mergedAt ) - new Date ( a . mergedAt ) ;
104+ } ) [ 0 ]
105+ : null ;
106+
107+ if ( associatedPR && associatedPR . author ) user = associatedPR . author ;
108+
109+ const result = {
110+ user : user ? user . login : null ,
111+ pull : associatedPR ? associatedPR . number : null ,
112+ links : {
113+ commit : `[\`${ commit . slice ( 0 , 7 ) } \`](${ obj . commitUrl } )` ,
114+ pull : associatedPR ? `[#${ associatedPR . number } ](${ associatedPR . url } )` : null ,
115+ user : user ? `[@${ user . login } ](${ user . url } )` : null ,
116+ } ,
117+ } ;
118+ cache . set ( key , result ) ;
119+ return result ;
120+ } ) ;
121+
122+ // Fetches pull request info with a single small GraphQL query per PR
123+ const fetchPullRequestInfo = withLimit ( async pull => {
124+ const key = `pull:${ pull } ` ;
125+ if ( cache . has ( key ) ) return cache . get ( key ) ;
126+
127+ const data = await graphql ( `query {
128+ repository(owner: ${ JSON . stringify ( owner ) } , name: ${ JSON . stringify ( repoName ) } ) {
129+ pullRequest(number: ${ pull } ) {
130+ url
131+ author { login url }
132+ mergeCommit { commitUrl abbreviatedOid }
133+ }
134+ }
135+ }` ) ;
136+
137+ const pr = data . repository . pullRequest ;
138+ const user = pr && pr . author ? pr . author : null ;
139+ const mergeCommit = pr && pr . mergeCommit ? pr . mergeCommit : null ;
140+
141+ const result = {
142+ user : user ? user . login : null ,
143+ commit : mergeCommit ? mergeCommit . abbreviatedOid : null ,
144+ links : {
145+ commit : mergeCommit
146+ ? `[\`${ mergeCommit . abbreviatedOid } \`](${ mergeCommit . commitUrl } )`
147+ : null ,
148+ pull : `[#${ pull } ](https://github.com/${ repo } /pull/${ pull } )` ,
149+ user : user ? `[@${ user . login } ](${ user . url } )` : null ,
150+ } ,
151+ } ;
152+ cache . set ( key , result ) ;
153+ return result ;
154+ } ) ;
155+
156+ // Drop-in replacements for @changesets/get-github-info
157+ async function getInfo ( { commit } ) {
158+ return fetchCommitInfo ( commit ) ;
159+ }
160+
161+ async function getInfoFromPullRequest ( { pull } ) {
162+ return fetchPullRequestInfo ( pull ) ;
163+ }
4164
5165const getDependencyReleaseLine = async ( changesets , dependenciesUpdated ) => {
6166 if ( dependenciesUpdated . length === 0 ) return '' ;
@@ -10,7 +170,6 @@ const getDependencyReleaseLine = async (changesets, dependenciesUpdated) => {
10170 changesets . map ( async cs => {
11171 if ( cs . commit ) {
12172 let { links } = await getInfo ( {
13- repo,
14173 commit : cs . commit ,
15174 } ) ;
16175 return links . commit ;
@@ -54,7 +213,6 @@ const getReleaseLine = async (changeset, type, options) => {
54213 const links = await ( async ( ) => {
55214 if ( prFromSummary !== undefined ) {
56215 let { links } = await getInfoFromPullRequest ( {
57- repo,
58216 pull : prFromSummary ,
59217 } ) ;
60218 if ( commitFromSummary ) {
@@ -68,7 +226,6 @@ const getReleaseLine = async (changeset, type, options) => {
68226 const commitToFetchFrom = commitFromSummary || changeset . commit ;
69227 if ( commitToFetchFrom ) {
70228 let { links } = await getInfo ( {
71- repo,
72229 commit : commitToFetchFrom ,
73230 } ) ;
74231 return links ;
0 commit comments