Files
Christian Klotz 23cf2b948d feat: unify package artifact validation (#10197)
* feat: unify package artifact validation

* fix: restrict local artifact output paths

* fix: clarify package artifact module names

* feat: support pnpm package artifact consumers

* fix: preserve local release package installs

Keep npm and Bun consumer installs as runnable release artifacts while retaining isolated validation for other packages. Simplify smoke fixtures and avoid redundant release work.
2026-10-06 13:30:02 +02:00

65 lines
2.7 KiB
JavaScript

#!/usr/bin/env node
import { existsSync, rmSync } from "node:fs";
import { join } from "node:path";
import { produceArtifactSet } from "./package-artifacts.mjs";
import { getPublicWorkspacePackages } from "./release-packages.mjs";
import { execNpmSync } from "./npm-command.mjs";
const dryRun = process.argv.includes("--dry-run");
const unknownArgs = process.argv.slice(2).filter((arg) => arg !== "--dry-run");
if (unknownArgs.length > 0) throw new Error("Usage: node scripts/publish.mjs [--dry-run]");
function isPublished(name, version) {
try {
const output = execNpmSync(["view", `${name}@${version}`, "version", "--json"], {
encoding: "utf8",
stdio: ["inherit", "pipe", "pipe"],
});
if (!output.trim()) throw new Error(`npm returned no version for ${name}@${version}`);
return true;
} catch (error) {
const output = error && typeof error === "object"
? ["stdout", "stderr"].map((key) => key in error ? String(error[key] ?? "") : "").filter(Boolean).join("\n")
: "";
if (output.includes("E404") || output.includes("404 Not Found")) return false;
throw new Error(`Failed to query ${name}@${version}${output ? `\n${output}` : ""}`, { cause: error });
}
}
const repoRoot = process.cwd();
const packages = getPublicWorkspacePackages();
const packageByName = new Map(packages.map((pkg) => [pkg.name, pkg]));
const versions = [...new Set(packages.map((pkg) => pkg.version))];
if (versions.length !== 1) throw new Error(`Publish packages are not lockstep versioned: ${versions.join(", ")}`);
for (const pkg of packages) {
if (!existsSync(join(pkg.directory, "dist"))) {
throw new Error(`${pkg.directory}/dist does not exist. Run npm run build before publishing.`);
}
}
console.log(`Publishing pi packages at ${versions[0]}${dryRun ? " (dry run)" : ""}\n`);
const artifactSet = produceArtifactSet({ build: false, repoRoot });
try {
const packageStates = artifactSet.packages.map((pkg) => ({ ...pkg, published: isPublished(pkg.name, pkg.version) }));
for (const pkg of packageStates) {
console.log(`${pkg.name}@${pkg.version} ${pkg.published ? "is already published" : "is ready to publish"}: ${pkg.tarballPath}`);
}
console.log(dryRun ? "\nDry-running tarball publication with provenance.\n" : "\nAll packages packed; starting publication.\n");
for (const pkg of packageStates) {
if (pkg.published) {
console.log(`Skipping ${pkg.name}@${pkg.version}: already published\n`);
continue;
}
const args = ["publish", pkg.tarballPath, "--access", "public", "--provenance", "--ignore-scripts"];
if (dryRun) args.push("--dry-run");
execNpmSync(args, {
cwd: packageByName.get(pkg.name).directory,
stdio: "inherit",
});
console.log();
}
} finally {
rmSync(artifactSet.artifactDirectory, { force: true, recursive: true });
}