chore(release): publish @robrain/shared to npm; bump to 2.4.9 - #13
chore(release): publish @robrain/shared to npm; bump to 2.4.9#13radubusuioc wants to merge 1 commit into
Conversation
adelinamart
left a comment
There was a problem hiding this comment.
Reviewed against the repo rather than the diff — the packaging reasoning is sound and most of it checks out, but two things would bite on the first real release.
🔴 The test exclusion doesn't actually take effect
tsc doesn't clean its output directory. I applied the tsconfig change locally and rebuilt:
$ pnpm --filter @robrain/shared build
$ ls packages/shared/dist | grep -c '\.test\.'
28
db.test.d.ts db.test.d.ts.map db.test.js db.test.js.map …
Excluding src/**/*.test.ts stops new test files being emitted; every existing one stays. Since dist/ is already populated on the release machine, the published tarball would still contain compiled tests — the thing this change is meant to prevent. The npm pack --dry-run preflight would surface it, but it shouldn't depend on catching it by eye.
- "prepack": "tsc",
+ "prepack": "rm -rf dist && tsc",🔴 Publishing shared before the release guard can strand a version
publish:npm publishes @robrain/shared first, and the guard only runs later in the CLI's prepublishOnly. If it fails (missing tag or GHCR image — the 2.3.6/2.3.7 case it was built for), @robrain/shared@2.4.8 is already public with no matching CLI. npm blocks reusing that version for 24h after an unpublish, so the fix is bumping everything to 2.4.9 — leaving an orphan 2.4.8 the cloud could pin, which breaks the lockstep invariant the other 13 files maintain.
The stated goal (a cloud repin shouldn't wait on a CLI publish failure) still holds if the guard runs first — it works standalone and already has ROBRAIN_SKIP_RELEASE_GUARD=1 for emergencies:
-"publish:npm": "pnpm --filter @robrain/shared publish --access public --no-git-checks && …"
+"publish:npm": "pnpm --filter robrain build && node packages/cli/scripts/verify-release-artifacts.mjs && pnpm --filter @robrain/shared publish --access public --no-git-checks && …"(The build has to come first — the guard imports dist/lib/release-guard.js.)
✅ Verified, no action needed
- No runtime dependencies is correct. Everything under
packages/shared/srcimports onlynode:builtins, anddb.tsdeliberately uses a structuralPoolLike"to avoid a pg dependency in shared". Nothing to declare. exportsalready has a"."entry (the diff hides it in context), soimport { … } from '@robrain/shared'resolves for registry consumers.- The exclusion is genuinely needed —
dist/really does carry 28 test artifacts today. - No CI publishes recursively (only
publish-perception-image.yml), so droppingprivate: truecan't cause an accidental publish elsewhere. prepare+prepackboth runningtscis redundant but right:preparekeeps the git-pin path working,prepackcovers registry publishes.
🟡 Worth folding in
- Broken sourcemaps.
declarationMap/sourceMapare on andfilesomitssrc, so consumers get.js.map/.d.ts.mappointing at a nonexistent../src. Either add"src"tofilesor emit the published build without maps. LICENSEisn't in the tarball. The CLI lists it infiles; shared doesn't. Apache-2.0 §4(a) asks that recipients get a copy, and it's inconsistent between our two published packages.- No README → blank npm page. A few lines (install, "part of the robrain monorepo", link back) would do.
Coordination
- Don't merge until the
@robrainorg exists —publish:npmnow leads with the shared publish, so without the scope the first command fails and the release stops half-done. (Scope is clear on the registry side: no@robrain/*packages published, androbrain's maintainer ismart.adelina, so that's the account that needs to create it.) - Decide who publishes. The org-creation steps skip inviting members, which is fine if I cut the releases. If you're going to publish
@robrain/shared, you need to be an org member with publish rights — and note the ownership will be asymmetric: shared belongs to the org, the unscopedrobrainCLI stays on my personal account unless we transfer it. - This is inert until the cloud repo swaps its git-pin for
"@robrain/shared": "2.4.8"— worth landing both in the same window so the version doesn't sit unused. - It touches
packages/cli/src/commands/install.tsandsrc/index.ts, which have in-flight changes onmain(plan-required install messaging,robrain statusregistration state). Expect a version-string conflict; whichever lands second rebases. - Two OTP prompts with 2FA now, one per package.
Make `@robrain/shared` a publishable package so consumers (the cloud repo) can install a prebuilt tarball from the registry instead of the git-pin `github:adelinamart/robrain#vX.Y.Z&path:packages/shared`, which forces a full-monorepo download plus a TypeScript compile at install time. packages/shared/package.json: - drop `private: true`; add license/repository/homepage/bugs mirroring the CLI - `files: ["dist", "schema.sql", "README.md", "LICENSE"]` — schema.sql stays at package root because the cloud mounts and resolves it from there - `publishConfig.access: "public"` — scoped packages default to restricted, which the free `@robrain` org cannot host - export `./schema.sql` and `./package.json` so consumers can resolve the schema directly; both additions are backwards compatible Build hygiene. Excluding `src/**/*.test.ts` in tsconfig.json is not enough on its own: `tsc` never cleans its output directory, so a release machine with a populated `dist/` would keep every previously-compiled `*.test.js` and ship it. `prepare` and `prepack` are therefore `rm -rf dist && tsc -p tsconfig.publish.json`. Both hooks are needed because npm runs `prepare` after `prepack`, so `prepare` determines what actually lands in the tarball. tsconfig.publish.json also disables sourceMap/declarationMap: `files` does not ship `src/`, so published `.js.map` / `.d.ts.map` would point at a `../src` that is not in the tarball. Plain `build` keeps maps for local dev, and the new tsconfig.typecheck.json re-includes tests so `pnpm typecheck` still covers them. Root package.json: `publish:npm` builds the CLI and runs verify-release-artifacts.mjs *before* publishing @robrain/shared. Publishing shared first is deliberate — it has no coupling to the Docker image, so a cloud repin should not wait on a CLI publish failure — but the CLI's own prepublishOnly guard runs last in the chain. Without the up-front check, a guard failure there (the 2.3.6/2.3.7 case) would leave @robrain/shared already public with no matching CLI, and npm blocks reusing a version for 24h after an unpublish. The only fix would be bumping to the next patch, orphaning a version the cloud could pin and breaking the lockstep invariant. docs/release.md: document the two-package npm step, the one-time @robrain org creation, the pack preflight, and why the guard runs up front. Version 2.4.9 across the 13 lockstep files — 2.4.8 is already tagged and published from main, so this packaging change takes the next patch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bbc87d5 to
6f25325
Compare
What
Makes
@robrain/sharedpublishable to npm. Rebased ontomainand renumbered 2.4.8 → 2.4.9:robrain@2.4.8is already published andv2.4.8is tagged on origin, so the original version was dead.Today the cloud repo consumes shared as a git-pin, which forces every consumer install to download the full monorepo tarball and compile TypeScript at install time. Publishing a prebuilt package removes that.
Review fixes folded in
🔴 Test exclusion didn't take effect. Correct —
tscnever cleansoutDir, so a release machine's populateddist/keeps every previously-compiled*.test.js. Reproduced: seededdist/with the old config (28 test artifacts), rebuilt with the exclusion, all 28 survived.Fixed, but the suggested
prepack: "rm -rf dist && tsc"alone isn't sufficient — npm runsprepareafterprepack, soprepare: "tsc"re-emitted everything the clean had just removed. My first attempt at this shipped maps for exactly that reason. Both hooks now runrm -rf dist && tsc -p tsconfig.publish.json.🔴 Publishing shared before the guard can strand a version. Correct, and the consequence chain (24h unpublish block → bump to next patch → orphan version → broken lockstep invariant) is right. Applied your fix, build-first as you noted. Verified it actually gates:
Under the old ordering
@robrain/shared@2.4.9would already be public at this point.🟡 Broken sourcemaps. Took the "emit without maps" option — shipping
srcwould also drag in test sources.tsconfig.publish.jsondisablessourceMap/declarationMap; plainbuildkeeps them, so local dev and the git-pin path are unaffected. Tarball dropped 41.7 kB → 31.3 kB.🟡 LICENSE — added
packages/shared/LICENSE(copy of the CLI's) and listed it infiles.🟡 README — added, with install, usage, the
schema.sqlresolution snippet, and links back.Verification
pnpm -r build,pnpm -r typecheck(7/7) cleanpnpm test159/159 ·bun test src127/127dist/dirty (28 test artifacts, 34 maps), then packed: 24 files, 0 tests, 0 maps —dist/*.js,dist/*.d.ts,schema.sql,README.md,LICENSE,package.jsonimport('@robrain/shared')→ 40 exports;import('@robrain/shared/load-env')works;require.resolve('@robrain/shared/schema.sql')works;LICENSEpresentMerge conflict
Rebase hit one conflict,
packages/shared/package.json(mainbumped it to 2.4.8 while keepingprivate: true). Resolved in favour of this branch.install.tsandindex.tsauto-merged cleanly despite the in-flight install-messaging androbrain statuswork.Coordination
@robrainorg existing.publish:npmleads with the shared publish, so without the scope the release stops after the guard passes. Registry side is clear: no@robrain/*packages exist."@robrain/shared": "2.4.9"— worth landing both in the same window.robrainCLI stays on the personal account unless transferred.🤖 Generated with Claude Code