a0f5cbffd7
## 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
129 lines
3.3 KiB
JavaScript
129 lines
3.3 KiB
JavaScript
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,
|
|
}),
|
|
[],
|
|
);
|
|
});
|