import { createHash } from 'node:crypto'; import { readFileSync } from 'node:fs'; import { spawnSync } from 'node:child_process'; import { resolve } from 'node:path'; import { setTimeout as delay } from 'node:timers/promises'; import { fileURLToPath } from 'node:url'; export async function packageIsPublished({ version, integrity, request = fetch }) { const response = await request(`https://registry.npmjs.org/devpush/${version}`, { signal: AbortSignal.timeout(15000) }); if (response.status === 404) return false; if (!response.ok) throw new Error(`npm lookup failed (${response.status}).`); const pkg = await response.json(); if (pkg.name !== 'devpush' || pkg.version !== version || pkg.dist?.integrity !== integrity) throw new Error('Published npm package differs from the CI-tested archive.'); return true; } async function main() { const root = resolve(import.meta.dirname, '..'); const { version } = JSON.parse(readFileSync(resolve(root, 'app/package.json'))); if (process.env.RELEASE_TAG !== `v${version}`) throw new Error('Release tag does not match the package.'); const archive = resolve(root, `release/devpush-${version}.tgz`); const integrity = 'sha512-' + createHash('sha512').update(readFileSync(archive)).digest('base64'); const published = () => packageIsPublished({ version, integrity }); if (!await published()) { const started = Date.now(); const result = spawnSync('npm', ['publish', archive, '--access', 'public', '--provenance', '--tag', version.includes('-') ? 'beta' : 'latest'], { stdio: 'inherit' }); console.log(`npm publish completed in ${Math.round((Date.now() - started) / 1000)}s.`); if (result.error) throw result.error; if (result.status !== 0) throw new Error('npm publication failed.'); } const availabilityStarted = Date.now(); for (let attempt = 0; ; attempt++) { if (await published()) break; if (attempt >= 40) throw new Error('npm has not finished processing the package; retry this release workflow after it becomes available.'); await delay(15000); } console.log(`Registry availability verified in ${Math.round((Date.now() - availabilityStarted) / 1000)}s.`); console.log(`npm serves the verified devpush@${version} package.`); } if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) await main();