@@ -139,62 +139,58 @@ export async function getLatestDeploymentStatus(params: {
139139 : "" ;
140140 if ( ! selector ) return { url : null , failed : false } ;
141141 const opts = { token : params . token , apiVersion : params . apiVersion , rateLimitAdmissionKey : params . rateLimitAdmissionKey } ;
142- type DeploymentStatus = { state ?: string ; environment_url ?: string } ;
143- const selectStatuses = ( payload : unknown ) => ( Array . isArray ( payload ) ? ( payload as DeploymentStatus [ ] ) : [ ] ) ;
144- const probeStatusesForUrl = ( statuses : DeploymentStatus [ ] ) => {
145- for ( const status of statuses ) {
146- const ok = status . state === "success" || status . state === "in_progress" ;
147- if ( ok && status . environment_url ) return status . environment_url ;
148- }
149- return null ;
150- } ;
151- const inspectDeploymentStatuses = async ( deploymentId : number ) : Promise < { url : string | null ; latestState ?: string } > => {
152- let latestState : string | undefined ;
153- let capturedLatest = false ;
154- const url = await findAcrossPages < DeploymentStatus , string > (
155- `${ base } /deployments/${ deploymentId } /statuses?per_page=10` ,
142+
143+ // sawFailure/sawPending accumulate across every deployment (and every page of them), so the final
144+ // failed-vs-still-coming verdict reflects all deployments, not just the first page (#7805).
145+ let sawFailure = false ;
146+ let sawPending = false ;
147+
148+ // Scan one deployment's statuses across ALL pages (#7805): return its environment_url when a usable status is
149+ // found, else null after recording whether its latest status looked failed/pending.
150+ const findDeploymentUrl = ( id : number ) : Promise < string | null > =>
151+ findAcrossPages < { state ?: string ; environment_url ?: string } , string > (
152+ `${ base } /deployments/${ id } /statuses?per_page=10` ,
156153 opts ,
157- selectStatuses ,
154+ ( payload ) => ( Array . isArray ( payload ) ? ( payload as Array < { state ?: string ; environment_url ?: string } > ) : [ ] ) ,
158155 ( statuses ) => {
159- if ( ! capturedLatest ) {
160- latestState = statuses [ 0 ] ? .state ;
161- capturedLatest = true ;
156+ for ( const status of statuses ) {
157+ const ok = status . state === "success" || status . state === "in_progress" ;
158+ if ( ok && status . environment_url ) return status . environment_url ;
162159 }
163- return probeStatusesForUrl ( statuses ) ;
160+ // Only the first page's first entry is GitHub's "latest" status; later pages are older, so the
161+ // failed/pending bookkeeping keys off statuses[0] exactly as the pre-pagination single-page read did.
162+ const latest = statuses [ 0 ] ?. state ;
163+ if ( latest === "failure" || latest === "error" ) sawFailure = true ;
164+ else if ( latest === "in_progress" || latest === "queued" || latest === "pending" ) sawPending = true ;
165+ return null ;
164166 } ,
165167 ) . catch ( ( error ) => {
166- console . log ( JSON . stringify ( { event : "deployment_status_error" , deployment : deploymentId , message : String ( error ) . slice ( 0 , 200 ) } ) ) ;
168+ console . log ( JSON . stringify ( { event : "deployment_status_error" , deployment : id , message : String ( error ) . slice ( 0 , 200 ) } ) ) ;
167169 return null ;
168170 } ) ;
169- if ( url ) return { url } ;
170- return latestState !== undefined ? { url : null , latestState } : { url : null } ;
171- } ;
172- let sawFailure = false ;
173- let sawPending = false ;
171+
172+ let url : string | null ;
174173 try {
175- const url = await findAcrossPages < { id ?: number } , string > (
174+ // Walk every page of deployments, and on each page scan each deployment's statuses; return the first usable
175+ // environment_url found, letting findAcrossPages stop as soon as a page yields one.
176+ url = await findAcrossPages < { id ?: number } , string > (
176177 `${ base } /deployments?${ selector } &per_page=10` ,
177178 opts ,
178179 ( payload ) => ( Array . isArray ( payload ) ? ( payload as Array < { id ?: number } > ) : [ ] ) ,
179180 async ( deployments ) => {
180- for ( const deployment of deployments ) {
181- if ( deployment . id == null ) continue ;
182- const { url : foundUrl , latestState } = await inspectDeploymentStatuses ( deployment . id ) ;
183- if ( foundUrl ) return foundUrl ;
184- if ( latestState === "failure" || latestState === "error" ) sawFailure = true ;
185- else if ( latestState === "in_progress" || latestState === "queued" || latestState === "pending" ) sawPending = true ;
186- }
187- return null ;
181+ const ids = deployments . map ( ( d ) => d . id ) . filter ( ( id ) : id is number => id != null ) ;
182+ const statusUrls = await Promise . all ( ids . map ( ( id ) => findDeploymentUrl ( id ) ) ) ;
183+ return statusUrls . find ( ( found ) : found is string => found !== null ) ?? null ;
188184 } ,
189185 ) ;
190- if ( url ) return { url, failed : false } ;
191186 } catch ( error ) {
192187 // 404 → the ref genuinely has no deployments. Any other failure (403 missing scope, rate limit, 5xx) is
193188 // NOT "no preview"; report `error` so the caller keeps polling rather than showing a false terminal state.
194189 if ( error instanceof PreviewGitHubError && error . status === 404 ) return { url : null , failed : false } ;
195190 console . log ( JSON . stringify ( { event : "deployment_lookup_error" , repo : `${ params . repo . owner } /${ params . repo . repo } ` , selector, message : String ( error ) . slice ( 0 , 200 ) } ) ) ;
196191 return { url : null , failed : false , error : true } ;
197192 }
193+ if ( url !== null ) return { url, failed : false } ;
198194 return { url : null , failed : sawFailure && ! sawPending } ;
199195}
200196
0 commit comments