Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/base64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@endo/base64",
"version": "1.1.0",
"version": "1.1.1",
"description": "Transcodes base64",
"keywords": [
"base64",
Expand Down
2 changes: 1 addition & 1 deletion packages/bytes/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
63 changes: 61 additions & 2 deletions scripts/release-npm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean>}
*/
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
Expand Down Expand Up @@ -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)`,
);
Loading