paperclip/scripts/verify-release-registry-state.test.mjs

129 lines
3.3 KiB
JavaScript
Raw Normal View History

Harden release flow with registry verification and dist-tag checks (#4800) ## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies > - Paperclip is distributed as npm packages, including plugins like `plugin-e2b` > - The release process publishes canary and stable builds via npm dist-tags > - But there was no automated verification that published packages actually landed with the correct dist-tags, and broken canary publishes could silently ship to users > - This PR adds a registry verification script that checks published packages match their expected dist-tags, and wires it into PR CI so regressions are caught before merge > - The benefit is release integrity is verified automatically, and broken dist-tag states are caught early ## What Changed - Added `scripts/verify-release-registry-state.mjs` — verifies that published npm packages have correct dist-tag assignments and detects orphaned or mispointed tags - Added `scripts/verify-release-registry-state.test.mjs` — test coverage for the verification logic - Updated `scripts/release.sh` to include canary dist-tag safety checks before publishing - Updated `.github/workflows/pr.yml` to run registry verification as a CI step - Updated `doc/PUBLISHING.md` and `doc/RELEASING.md` with the new verification workflow ## Verification - `pnpm test` — all tests pass including new verification script tests - `node scripts/verify-release-registry-state.mjs` — runs against the live npm registry and reports current state - CI: the new PR workflow step runs on every PR push ## Risks - Low risk. This is additive CI and tooling — no runtime code changes. The registry verification is read-only (queries npm, does not publish). The release script changes add safety checks that abort before publishing if state is unexpected. ## Model Used Codex GPT 5.4 high via Paperclip. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge
2026-04-29 15:56:20 -07:00
import assert from "node:assert/strict";
import test from "node:test";
import {
collectInternalDependencyProblems,
isCanaryVersion,
verifyPackageRegistryState,
} from "./verify-release-registry-state.mjs";
test("isCanaryVersion matches release canaries", () => {
assert.equal(isCanaryVersion("2026.427.0-canary.3"), true);
assert.equal(isCanaryVersion("2026.427.0"), false);
});
test("collectInternalDependencyProblems flags missing internal versions", () => {
const manifest = {
dependencies: {
"@paperclipai/plugin-sdk": "2026.425.0-canary.5",
e2b: "^2.19.0",
},
};
const packageDocsByName = new Map([
[
"@paperclipai/plugin-sdk",
{
versions: {
"2026.427.0-canary.3": {},
},
},
],
]);
assert.deepEqual(collectInternalDependencyProblems(manifest, packageDocsByName), [
"dependencies requires @paperclipai/plugin-sdk@2026.425.0-canary.5, but npm does not expose that version",
]);
});
test("verifyPackageRegistryState fails when canary latest is left in place by default", () => {
const packageDocsByName = new Map([
[
"@paperclipai/plugin-e2b",
{
"dist-tags": {
latest: "2026.425.0-canary.5",
canary: "2026.427.0-canary.3",
},
versions: {
"2026.425.0-canary.5": {
dependencies: {
"@paperclipai/plugin-sdk": "2026.425.0-canary.5",
},
},
"2026.427.0-canary.3": {
dependencies: {
"@paperclipai/plugin-sdk": "2026.427.0-canary.3",
},
},
},
},
],
[
"@paperclipai/plugin-sdk",
{
versions: {
"2026.427.0-canary.3": {},
},
},
],
]);
assert.deepEqual(
verifyPackageRegistryState({
packageName: "@paperclipai/plugin-e2b",
packageDoc: packageDocsByName.get("@paperclipai/plugin-e2b"),
packageDocsByName,
channel: "canary",
distTag: "canary",
targetVersion: "2026.427.0-canary.3",
allowCanaryLatest: false,
}),
[
"@paperclipai/plugin-e2b: latest dist-tag still resolves to canary 2026.425.0-canary.5; rerun with --allow-canary-latest only when that state is intentional",
"@paperclipai/plugin-e2b@2026.425.0-canary.5 via latest: dependencies requires @paperclipai/plugin-sdk@2026.425.0-canary.5, but npm does not expose that version",
],
);
});
test("verifyPackageRegistryState allows intentional canary latest but still checks dependencies", () => {
const packageDocsByName = new Map([
[
"paperclipai",
{
"dist-tags": {
latest: "2026.427.0-canary.3",
canary: "2026.427.0-canary.3",
},
versions: {
"2026.427.0-canary.3": {
dependencies: {
"@paperclipai/server": "2026.427.0-canary.3",
},
},
},
},
],
[
"@paperclipai/server",
{
versions: {
"2026.427.0-canary.3": {},
},
},
],
]);
assert.deepEqual(
verifyPackageRegistryState({
packageName: "paperclipai",
packageDoc: packageDocsByName.get("paperclipai"),
packageDocsByName,
channel: "canary",
distTag: "canary",
targetVersion: "2026.427.0-canary.3",
allowCanaryLatest: true,
}),
[],
);
});