@@ -179,28 +179,74 @@ function sleep(ms) {
179179 } ) ;
180180}
181181
182+ function nextRetryDelay ( attempt , attempts , delayMs , options ) {
183+ if ( attempt >= attempts ) return null ;
184+ if ( options . deadline === undefined ) return delayMs ;
185+
186+ const now = options . now || Date . now ;
187+ const remainingMs = options . deadline - now ( ) ;
188+ if ( remainingMs <= 0 ) return null ;
189+ return Math . min ( delayMs , remainingMs ) ;
190+ }
191+
182192async function waitForNpmLatest ( name , expectedVersion , options = { } ) {
183193 const attempts =
184194 options . attempts || Number ( process . env . RELEASE_ASSERT_ATTEMPTS || DEFAULT_ATTEMPTS ) ;
185195 const delayMs =
186196 options . delayMs || Number ( process . env . RELEASE_ASSERT_DELAY_MS || DEFAULT_DELAY_MS ) ;
197+ const wait = options . sleep || sleep ;
187198
188199 let latest = null ;
189200 for ( let attempt = 1 ; attempt <= attempts ; attempt += 1 ) {
190201 latest = npmLatest ( name ) ;
191202 if ( latest === expectedVersion ) return latest ;
192203
193- if ( attempt < attempts ) {
204+ const retryDelay = nextRetryDelay ( attempt , attempts , delayMs , options ) ;
205+ if ( retryDelay !== null ) {
194206 console . log (
195207 `npm latest for ${ name } is ${ latest } ; waiting for ${ expectedVersion } (${ attempt } /${ attempts } )`
196208 ) ;
197- await sleep ( delayMs ) ;
209+ await wait ( retryDelay ) ;
210+ } else {
211+ break ;
198212 }
199213 }
200214
201215 throw new Error ( `expected npm latest for ${ name } to be ${ expectedVersion } , got ${ latest } ` ) ;
202216}
203217
218+ async function waitForPublishedArtifact ( label , check , options = { } ) {
219+ const attempts =
220+ options . attempts || Number ( process . env . RELEASE_ASSERT_ATTEMPTS || DEFAULT_ATTEMPTS ) ;
221+ const delayMs =
222+ options . delayMs || Number ( process . env . RELEASE_ASSERT_DELAY_MS || DEFAULT_DELAY_MS ) ;
223+ const wait = options . sleep || sleep ;
224+ let lastError = null ;
225+ let attemptsMade = 0 ;
226+
227+ for ( let attempt = 1 ; attempt <= attempts ; attempt += 1 ) {
228+ attemptsMade = attempt ;
229+ try {
230+ return await check ( ) ;
231+ } catch ( error ) {
232+ lastError = error ;
233+ const retryDelay = nextRetryDelay ( attempt , attempts , delayMs , options ) ;
234+ if ( retryDelay !== null ) {
235+ console . log (
236+ `${ label } is not ready: ${ error . message } ; retrying (${ attempt } /${ attempts } )`
237+ ) ;
238+ await wait ( retryDelay ) ;
239+ } else {
240+ break ;
241+ }
242+ }
243+ }
244+
245+ throw new Error (
246+ `${ label } did not become ready after ${ attemptsMade } attempts: ${ lastError ?. message || 'unknown error' } `
247+ ) ;
248+ }
249+
204250async function main ( ) {
205251 const name = packageName ( ) ;
206252 const headTags = tagsPointingAtHead ( ) ;
@@ -218,30 +264,62 @@ async function main() {
218264
219265 console . log ( `tags on HEAD: ${ headTags . join ( ', ' ) || '(none)' } ` ) ;
220266 const expectedVersion = expectedTag . slice ( 1 ) ;
221- const latest = await waitForNpmLatest ( name , expectedVersion ) ;
267+ const retryAttempts = Number ( process . env . RELEASE_ASSERT_ATTEMPTS || DEFAULT_ATTEMPTS ) ;
268+ const retryDelayMs = Number ( process . env . RELEASE_ASSERT_DELAY_MS || DEFAULT_DELAY_MS ) ;
269+ const retryOptions = {
270+ attempts : retryAttempts ,
271+ delayMs : retryDelayMs ,
272+ deadline : Date . now ( ) + retryAttempts * retryDelayMs ,
273+ } ;
274+ const latest = await waitForNpmLatest ( name , expectedVersion , retryOptions ) ;
222275
223276 console . log ( `npm latest for ${ name } : ${ latest } ` ) ;
224277
225278 const expectedCommit = run ( 'git' , [ 'rev-parse' , 'HEAD' ] ) ;
226- const metadata = npmReleaseMetadata ( name , expectedVersion ) ;
227- if ( metadata . version !== expectedVersion ) {
228- throw new Error ( `npm metadata returned ${ metadata . version } ; expected ${ expectedVersion } ` ) ;
229- }
230- if ( metadata . gitHead !== expectedCommit ) {
231- throw new Error ( `npm gitHead ${ metadata . gitHead || '(missing)' } does not match HEAD` ) ;
232- }
279+ const metadata = await waitForPublishedArtifact (
280+ 'npm release metadata' ,
281+ ( ) => {
282+ const result = npmReleaseMetadata ( name , expectedVersion ) ;
283+ if ( result . version !== expectedVersion ) {
284+ throw new Error ( `npm metadata returned ${ result . version } ; expected ${ expectedVersion } ` ) ;
285+ }
286+ if ( result . gitHead !== expectedCommit ) {
287+ throw new Error ( `npm gitHead ${ result . gitHead || '(missing)' } does not match HEAD` ) ;
288+ }
289+ if ( ! result [ 'dist.attestations' ] ?. url ) {
290+ throw new Error ( 'npm attestation URL is missing' ) ;
291+ }
292+ return result ;
293+ } ,
294+ retryOptions
295+ ) ;
233296
234- const attestationUrl = metadata [ 'dist.attestations' ] ?. url ;
235- if ( ! attestationUrl ) throw new Error ( 'npm attestation URL is missing' ) ;
236- const attestations = await httpsJson ( attestationUrl ) ;
237- verifyProvenance ( provenanceStatement ( attestations ) , expectedCommit ) ;
297+ await waitForPublishedArtifact (
298+ 'npm provenance' ,
299+ async ( ) => {
300+ const attestations = await httpsJson ( metadata [ 'dist.attestations' ] . url ) ;
301+ verifyProvenance ( provenanceStatement ( attestations ) , expectedCommit ) ;
302+ } ,
303+ retryOptions
304+ ) ;
238305
239- const release = githubRelease ( expectedTag ) ;
240- if ( release . tagName !== expectedTag ) {
241- throw new Error ( `GitHub Release tag ${ release . tagName } does not match ${ expectedTag } ` ) ;
242- }
243- verifyCuratedNotes ( expectedTag , release ) ;
244- verifyInstalledCli ( name , expectedVersion ) ;
306+ await waitForPublishedArtifact (
307+ 'GitHub Release' ,
308+ ( ) => {
309+ const release = githubRelease ( expectedTag ) ;
310+ if ( release . tagName !== expectedTag ) {
311+ throw new Error ( `GitHub Release tag ${ release . tagName } does not match ${ expectedTag } ` ) ;
312+ }
313+ verifyCuratedNotes ( expectedTag , release ) ;
314+ } ,
315+ retryOptions
316+ ) ;
317+
318+ await waitForPublishedArtifact (
319+ 'installed CLI' ,
320+ ( ) => verifyInstalledCli ( name , expectedVersion ) ,
321+ retryOptions
322+ ) ;
245323
246324 console . log ( `Release publication verified: ${ name } @${ latest } ` ) ;
247325}
@@ -263,4 +341,5 @@ module.exports = {
263341 verifyInstalledCli,
264342 verifyProvenance,
265343 waitForNpmLatest,
344+ waitForPublishedArtifact,
266345} ;
0 commit comments