From b1be0b050c96663e35274c29e8b6b0cb8b08b32b Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 11 Jun 2026 21:47:07 -0700 Subject: [PATCH 1/3] fix: Ramp patch version for base64 --- packages/base64/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/base64/package.json b/packages/base64/package.json index b61344e210..bddaa89547 100644 --- a/packages/base64/package.json +++ b/packages/base64/package.json @@ -1,6 +1,6 @@ { "name": "@endo/base64", - "version": "1.1.0", + "version": "1.1.1", "description": "Transcodes base64", "keywords": [ "base64", From bd841f2213621e55f798aba7d76b8f886e62d79c Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 11 Jun 2026 21:48:18 -0700 Subject: [PATCH 2/3] fix: Ramp version of bytes for publish --- packages/bytes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bytes/package.json b/packages/bytes/package.json index d46c9ce9e4..ae38c85444 100644 --- a/packages/bytes/package.json +++ b/packages/bytes/package.json @@ -1,6 +1,6 @@ { "name": "@endo/bytes", - "version": "1.0.0", + "version": "1.0.1", "description": "Portable Uint8Array helpers for cross-realm byte handling", "keywords": [ "uint8array", From 1798d70b0123df51e0f5e7402f073adc7d708900 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 11 Jun 2026 21:52:27 -0700 Subject: [PATCH 3/3] fix(release): Idempotent in the face of partial success --- scripts/release-npm.mjs | 63 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/scripts/release-npm.mjs b/scripts/release-npm.mjs index 53277f0ea9..b236dab3db 100755 --- a/scripts/release-npm.mjs +++ b/scripts/release-npm.mjs @@ -20,6 +20,50 @@ import { promisify } from 'node:util'; const execFileAsync = promisify(execFile); +/** + * Read `name` and `version` from the package.json inside a published tarball. + * The tarball is the source of truth for what would be published, so we read + * it rather than re-deriving from the workspace tree. + * @param {string} tgz + * @returns {Promise<{name: string, version: string}>} + */ +const readTarballManifest = async tgz => { + // npm tarballs place every file under a top-level `package/` directory. + const { stdout } = await execFileAsync( + 'tar', + ['-xzOf', tgz, 'package/package.json'], + { cwd: repoRoot, maxBuffer: 16 * 1024 * 1024 }, + ); + const { name, version } = JSON.parse(stdout); + return { name, version }; +}; + +/** + * Determine whether `name@version` is already on the npm registry. Treats a + * missing package (E404) and a published-but-different-version (empty output) + * alike: only an exact name@version match counts as already published. + * @param {string} name + * @param {string} version + * @returns {Promise} + */ +const isPublished = async (name, version) => { + try { + const { stdout } = await execFileAsync( + 'npm', + ['view', `${name}@${version}`, 'version'], + { cwd: repoRoot, maxBuffer: 1024 * 1024 }, + ); + // npm view exits 0 with empty stdout when the package exists but the + // requested version does not; a non-empty result means the exact + // version is on the registry. + return stdout.trim() !== ''; + } catch { + // E404 (package name unknown to the registry) or any lookup failure: + // treat as not-yet-published so the publish below can proceed/report. + return false; + } +}; + /** * Run a command, inheriting stdio; reject on non-zero exit. * @param cmd @@ -91,12 +135,27 @@ if (tarballs.length !== expectedPackages.length) { console.error( `release:npm: publishing ${tarballs.length} tarball(s)${tag ? ` with --tag ${tag}` : ''}`, ); +let publishedCount = 0; +let skippedCount = 0; for (const tgz of tarballs) { - console.error(` npm publish ${path.relative(repoRoot, tgz)}`); + const rel = path.relative(repoRoot, tgz); + const { name, version } = await readTarballManifest(tgz); + // Idempotency: never attempt to publish a version that is already on the + // registry. This lets `release:npm` be re-run safely after a partial + // failure — already-published packages are skipped, the rest go out. + if (await isPublished(name, version)) { + console.error(` skip ${name}@${version} (already published)`); + skippedCount += 1; + continue; + } + console.error(` npm publish ${rel}`); const argv = ['publish']; if (tag) argv.push('--tag', tag); argv.push(tgz); await run('npm', argv, { cwd: repoRoot }); + publishedCount += 1; } -console.error('release:npm: done'); +console.error( + `release:npm: done (${publishedCount} published, ${skippedCount} skipped)`, +);