1- import  {  graphql  }  from  '@octokit/graphql' ; 
1+ import  {  graphql ,   GraphqlResponseError  }  from  '@octokit/graphql' ; 
22import  {  Octokit  }  from  '@octokit/rest' ; 
33import  {  $  }  from  'execa' ; 
44
@@ -35,31 +35,54 @@ export async function findLatestTaggedVersion(opts) {
3535 * @property  {string } lastRelease 
3636 * @property  {string } release 
3737 * @property  {string } [org="mui"] 
38-  * @property  {'rest' | 'graphql' } [method="rest"] 
3938 */ 
4039
4140/** 
41+  * Fetches commits between two refs (lastRelease..release) including PR details. 
42+  * It first tries to use the GraphQL API (more efficient) and falls back to the 
43+  * REST api if it fails with server error. 
44+  * 
4245 * @param  {FetchCommitsOptions } param0 
4346 * @returns  {Promise<FetchedCommitDetails[]> } 
4447 */ 
45- export  async  function  fetchCommitsBetweenRefs ( {  method  =   'rest' ,   org =  'mui' ,  ...options  } )  { 
48+ export  async  function  fetchCommitsBetweenRefs ( {  org =  'mui' ,  ...options  } )  { 
4649  if  ( ! options . token )  { 
4750    throw  new  Error ( 'Missing "token" option. The token needs `public_repo` permissions.' ) ; 
4851  } 
4952  const  opts  =  {  ...options ,  org } ; 
5053
51-   return  method  ===  'rest'  ? fetchCommitsRest ( opts )  : fetchCommitsGraphql ( opts ) ; 
54+   /** 
55+    * @type  {FetchedCommitDetails[] } 
56+    */ 
57+   try  { 
58+     return  fetchCommitsGraphql ( opts ) ; 
59+   }  catch  ( error )  { 
60+     let  status  =  0 ; 
61+     if  ( error  instanceof  GraphqlResponseError )  { 
62+       if  ( error . headers . status )  { 
63+         status  =  parseInt ( error . headers . status ,  10 ) ; 
64+         // only re-throw for client errors (4xx), for server errors (5xx) we want to fall back to the REST API 
65+         if  ( status  >=  400  &&  status  <  500 )  { 
66+           throw  error ; 
67+         } 
68+       } 
69+     } 
70+     console . warn ( 
71+       `Failed to fetch commits using the GraphQL API, falling back to the REST API. Status Code: ${ status }  , 
72+     ) ; 
73+     return  await  fetchCommitsRest ( opts ) ; 
74+   } 
5275} 
5376
5477/** 
5578 * Fetches commits between two refs using GitHub's GraphQL API over a single network call. 
5679 * Its efficient network-wise but is not as reliable as the REST API (in my findings). 
5780 * So keeping both implementations for the time being. 
5881 * 
59-  * @param  {Omit< FetchCommitsOptions, 'method'>  & {org: string} 
82+  * @param  {FetchCommitsOptions & {org: string} } param0 
6083 * @returns  {Promise<FetchedCommitDetails[]> } 
6184 */ 
62- async  function  fetchCommitsGraphql ( {  org,  token,  repo,  lastRelease,  release } )  { 
85+ export   async  function  fetchCommitsGraphql ( {  org,  token,  repo,  lastRelease,  release } )  { 
6386  const  gql  =  graphql . defaults ( { 
6487    headers : { 
6588      authorization : `token ${ token }  , 
@@ -129,11 +152,9 @@ async function fetchCommitsGraphql({ org, token, repo, lastRelease, release }) {
129152  let  allCommits  =  [ ] ; 
130153  // fetch all commits (with pagination) 
131154  do  { 
132-     /** 
133-      * @type  {CommitConnection } 
134-      */ 
135155    // eslint-disable-next-line no-await-in-loop 
136-     const  commits  =  ( await  fetchCommitsPaginated ( commitAfter ) ) . repository . ref . compare . commits ; 
156+     const  data  =  await  fetchCommitsPaginated ( commitAfter ) ; 
157+     const  commits  =  data . repository . ref . compare . commits ; 
137158    hasNextPage  =  ! ! commits . pageInfo . hasNextPage ; 
138159    commitAfter  =  hasNextPage  ? commits . pageInfo . endCursor  : null ; 
139160    allCommits . push ( ...commits . nodes ) ; 
@@ -142,23 +163,24 @@ async function fetchCommitsGraphql({ org, token, repo, lastRelease, release }) {
142163  allCommits  =  allCommits . filter ( ( commit )  =>  commit . associatedPullRequests . nodes . length  >  0 ) ; 
143164
144165  return  allCommits . map ( ( commit )  =>  { 
145-     const  labels  =  commit . associatedPullRequests . nodes . flatMap ( ( pr )  => 
146-       pr . labels . nodes . map ( ( label )  =>  label . name ) , 
147-     ) ; 
148-     const  firstPr  =  commit . associatedPullRequests . nodes [ 0 ] ; 
166+     const  pr  =  commit . associatedPullRequests . nodes [ 0 ] ; 
167+     const  labels  =  pr . labels . nodes . map ( ( label )  =>  label . name ) ; 
149168
150-     return  /** @type  {FetchedCommitDetails } */  ( { 
169+     /** 
170+      * @type  {FetchedCommitDetails } 
171+      */ 
172+     return  { 
151173      sha : commit . oid , 
152174      message : commit . message , 
153175      labels, 
154-       prNumber : firstPr . number , 
155-       author : firstPr . author . user ?. login 
176+       prNumber : pr . number , 
177+       author : pr . author . user ?. login 
156178        ? { 
157-             login : firstPr . author . user . login , 
158-             association : getAuthorAssociation ( firstPr . authorAssociation ) , 
179+             login : pr . author . user . login , 
180+             association : getAuthorAssociation ( pr . authorAssociation ) , 
159181          } 
160182        : null , 
161-     } ) ; 
183+     } ; 
162184  } ) ; 
163185} 
164186
@@ -167,11 +189,11 @@ async function fetchCommitsGraphql({ org, token, repo, lastRelease, release }) {
167189 * It is more reliable than the GraphQL API but requires multiple network calls (1 + n). 
168190 * One to list all commits between the two refs and then one for each commit to get the PR details. 
169191 * 
170-  * @param  {Omit< FetchCommitsOptions, 'method'>  & { org: string } 
192+  * @param  {FetchCommitsOptions & { org: string } } param0 
171193 * 
172194 * @returns  {Promise<FetchedCommitDetails[]> } 
173195 */ 
174- async  function  fetchCommitsRest ( {  token,  repo,  lastRelease,  release,  org } )  { 
196+ export   async  function  fetchCommitsRest ( {  token,  repo,  lastRelease,  release,  org } )  { 
175197  const  octokit  =  new  Octokit ( { 
176198    auth : token , 
177199  } ) ; 
0 commit comments