From f5fcb3d2451417e1dc2b454c206ff2edde1ac8b2 Mon Sep 17 00:00:00 2001 From: abs2023 <93659489+abs2023@users.noreply.github.com> Date: Tue, 22 Sep 2026 13:02:13 -0400 Subject: [PATCH] Publish the first mainnet manifest at 3.0.0. npm will not reuse 1.0.0 after it was unpublished. The release plan skips any major that already appears in the registry. Co-authored-by: Cursor --- collateral-abi/README.md | 2 +- collateral-abi/scripts/plan-release.mjs | 33 ++++++++++++++++++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/collateral-abi/README.md b/collateral-abi/README.md index a9148c8..f79ac41 100644 --- a/collateral-abi/README.md +++ b/collateral-abi/README.md @@ -37,4 +37,4 @@ Contents are generated — do not edit by hand: - `src/` is copied from `../contracts/abi` (the Hardhat codegen output) by `scripts/build.mjs`, then compiled to `dist/`. - `deployments.json` is the canonical address manifest for this repo; it is updated when contracts are (re)deployed. -Publishing happens from CI (see `.github/workflows/publish-collateral-abi.yml`). A `dev` publish updates `environments.testnet` and is tagged `dev` (minor bump). A `main` publish updates `environments.mainnet` and is tagged `latest`; the first mainnet cut is `1.0.0`. +Publishing happens from CI (see `.github/workflows/publish-collateral-abi.yml`). A `dev` publish updates `environments.testnet` and is tagged `dev` (minor bump). A `main` publish updates `environments.mainnet` and is tagged `latest`; the first mainnet cut is `3.0.0`, skipping any major npm has already used. diff --git a/collateral-abi/scripts/plan-release.mjs b/collateral-abi/scripts/plan-release.mjs index 6464c99..a630c6f 100644 --- a/collateral-abi/scripts/plan-release.mjs +++ b/collateral-abi/scripts/plan-release.mjs @@ -4,8 +4,10 @@ // The base is the `dev` tag, or the highest published 0.x if that // tag does not exist yet. Never moves `latest`. // main -> dist-tag `latest`. The first publish whose mainnet block has -// addresses is 1.0.0. After that, an ABI break bumps major, an -// ABI addition bumps minor, and a mainnet address change bumps patch. +// addresses is 3.0.0, or the next free major if that version was +// already published (npm will not reuse an unpublished version). +// After that, an ABI break bumps major, an ABI addition bumps +// minor, and a mainnet address change bumps patch. // // Writes package.json's version in the working copy (npm holds the version // of record; nothing is committed). Set DRY_RUN=1 to print the decision only. @@ -56,6 +58,24 @@ function versionsOf() { } } +// `npm view versions` hides unpublished releases. `time` still lists them, +// and npm rejects a publish that reuses one of those version numbers. +function takenVersions() { + try { + const time = npmJson(["view", pkg, "time", "--json"]); + return new Set(Object.keys(time).filter((key) => /^\d+\.\d+\.\d+$/.test(key))); + } catch { + return new Set(versionsOf()); + } +} + +function firstMainnetVersion() { + const taken = takenVersions(); + let major = 3; + while (taken.has(`${major}.0.0`)) major += 1; + return `${major}.0.0`; +} + function parts(version) { return version.split(".").map((part) => Number(part)); } @@ -123,7 +143,7 @@ if (channel === "main" && !hasAddresses(current.environments?.mainnet)) { } if (!tags.latest) { - const version = channel === "main" ? "1.0.0" : JSON.parse(readFileSync(path.join(root, "package.json"), "utf8")).version; + const version = channel === "main" ? firstMainnetVersion() : JSON.parse(readFileSync(path.join(root, "package.json"), "utf8")).version; setVersion(version); console.log(`First publish ${pkg}@${version} (${distTag})`); emit({ level: "first", version, dist_tag: distTag }); @@ -135,9 +155,10 @@ if (channel === "main") { try { const published = JSON.parse(readFileSync(path.join(latest.pkgRoot, "deployments.json"), "utf8")); if (!hasAddresses(published.environments?.mainnet)) { - setVersion("1.0.0"); - console.log(`First mainnet manifest — publishing ${pkg}@1.0.0 on latest`); - emit({ level: "mainnet", version: "1.0.0", dist_tag: distTag }); + const version = firstMainnetVersion(); + setVersion(version); + console.log(`First mainnet manifest — publishing ${pkg}@${version} on latest`); + emit({ level: "mainnet", version, dist_tag: distTag }); process.exit(0); } } finally {