@@ -83,6 +83,7 @@ export async function deleteUnusedPrStacks(
8383 const cloudFormationClient = new CloudFormationClient ( { } )
8484 const route53Client = new Route53Client ( { } )
8585 const { hostedZoneId, cnameRecords} = await getHostedZoneInfo ( route53Client , hostedZoneName )
86+ const pullRequestStateCache = new Map < string , string > ( )
8687
8788 console . log ( "checking cloudformation stacks" )
8889
@@ -94,7 +95,7 @@ export async function deleteUnusedPrStacks(
9495 }
9596
9697 const stackName = stack . StackName
97- if ( ! ( await isClosedPullRequest ( stackName , baseStackName , repoName ) ) ) {
98+ if ( ! ( await isClosedPullRequest ( stackName , baseStackName , repoName , pullRequestStateCache ) ) ) {
9899 continue
99100 }
100101
@@ -188,34 +189,50 @@ async function getHostedZoneInfo(
188189 return { hostedZoneId, cnameRecords}
189190}
190191
191- async function isClosedPullRequest ( stackName : string , baseStackName : string , repoName : string ) : Promise < boolean > {
192+ async function isClosedPullRequest (
193+ stackName : string ,
194+ baseStackName : string ,
195+ repoName : string ,
196+ pullRequestStateCache : Map < string , string >
197+ ) : Promise < boolean > {
192198 const match = new RegExp ( String . raw `^${ baseStackName } -pr-(?<pullRequestId>\d+)(-[\w-]+)?$` ) . exec ( stackName )
193199 if ( ! match ?. groups ?. pullRequestId ) {
194200 return false
195201 }
196202
197203 const pullRequestId = match . groups . pullRequestId
198- console . log ( `Checking pull request id ${ pullRequestId } ` )
199- const url = `https://api.github.com/repos/NHSDigital/ ${ repoName } /pulls/ ${ pullRequestId } `
204+ let pullRequestState = pullRequestStateCache . get ( pullRequestId )
205+ if ( pullRequestState === undefined ) {
200206
201- const headers : Record < string , string > = {
202- Accept : "application/vnd.github+json" ,
203- Authorization : `Bearer ${ process . env . GITHUB_TOKEN } `
204- }
207+ console . log ( `Checking pull request id ${ pullRequestId } ` )
208+ const url = `https://api.github.com/repos/NHSDigital/${ repoName } /pulls/${ pullRequestId } `
205209
206- const response = await fetch ( url , { headers} )
207- if ( ! response . ok ) {
208- console . log ( `Failed to fetch PR ${ pullRequestId } : ${ response . status } ${ await response . text ( ) } ` )
209- return false
210+ const headers : Record < string , string > = {
211+ Accept : "application/vnd.github+json" ,
212+ Authorization : `Bearer ${ process . env . GITHUB_TOKEN } `
213+ }
214+
215+ const response = await fetch ( url , { headers} )
216+ if ( ! response . ok ) {
217+ console . log ( `Failed to fetch PR ${ pullRequestId } : ${ response . status } ${ await response . text ( ) } ` )
218+ // To avoid accidentally deleting stacks due to transient API failures, we treat errors as non-closed state
219+ // but do not cache the result to allow for retries on subsequent runs
220+ return false
221+ }
222+
223+ const data = ( await response . json ( ) ) as { state ?: string }
224+ pullRequestState = data . state
225+ if ( pullRequestState ) {
226+ pullRequestStateCache . set ( pullRequestId , pullRequestState )
227+ }
210228 }
211229
212- const data = ( await response . json ( ) ) as { state ?: string }
213- if ( data . state !== "closed" ) {
214- console . log ( `not going to delete stack ${ stackName } as PR state is ${ data . state } ` )
230+ if ( pullRequestState !== "closed" ) {
231+ console . log ( `not going to delete stack ${ stackName } as PR state is ${ pullRequestState } ` )
215232 return false
216233 }
217234
218- console . log ( `** going to delete stack ${ stackName } as PR state is ${ data . state } **` )
235+ console . log ( `** going to delete stack ${ stackName } as PR state is ${ pullRequestState } **` )
219236 return true
220237}
221238
0 commit comments