feat: publish to npm, over trusted publishing - #135
Conversation
Closes #134. behold carried `private: true`, so "open your own project" started with "clone this repo and run it from the checkout". kubemicrovm-ops ships BEHOLD_DIR handling and a bespoke error message for exactly that, and every other consumer would invent its own. `private` goes, `publishConfig` sets `access: public` (a scoped package's first publish is otherwise restricted) and `provenance: true`. ## Trusted publishing rather than a token release.yml publishes over OIDC — npm mints a short-lived credential for this repository and this workflow filename, so there is no NPM_TOKEN to leak, rotate or scope wrongly. That needs `id-token: write`, which the job declares, and a Trusted Publisher configured on npmjs.com naming this repo and `release.yml`. Until that exists the publish step fails rather than falling back to anything, which is the behaviour you want. npm is upgraded explicitly: trusted publishing needs 11.5.1 or newer, and while Node 24 ships an npm 11 the exact patch moves with the runner image. Pinning the floor here beats discovering it on a tag. ## What the workflow refuses to publish Everything that can say no runs first — typecheck, tests, build — then two release-specific gates: The tarball has to carry bin, dist and web. `files` includes web because it is the SPA; a tarball missing it publishes a server with no UI, which would look fine in CI and break on install. The tag has to match package.json's version, so a mistyped tag cannot ship a version nobody asked for. workflow_dispatch rehearses all of it and publishes nothing. ## Verified against a real install `npm pack`, installed into a scratch project, and run from node_modules/.bin: the bin resolves dist/cli.js, the server resolves web/ (it is dirname(module)/../web, which lands the same from src/ in dev and dist/ when installed), and it served the SPA and a real project's graph on a port. Note: the package has no `license` field and the repo has no LICENSE file. npm will warn on publish. Choosing one is not my call, so it is left as is — worth settling before the first publish rather than after. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TCV5kwB1jdnWH9if9ZbNXq
54e440f to
9db546e
Compare
|
Restacked onto This branch carried #133's commit underneath the npm change, so my #139 (which closes the same issue, #131) put it in conflict. That was my doing — I implemented #131 without checking for an open PR on it first. The npm work itself was never in conflict: The zoom-note work it was stacked on is handled separately — #139 landed the bulk, and #133's Original tip
|
Closes #134.
private: truemeant "open your own project" started with "clone this repo and run it from the checkout". kubemicrovm-ops shipsBEHOLD_DIRhandling and a bespoke error message for exactly that, and every other consumer would invent its own.The package
privateremoved.publishConfigsetsaccess: public— a scoped package's first publish is restricted otherwise — andprovenance: true.Nothing else needed changing:
bin,files: [bin, dist, web]andprepublishOnlywere already right.Trusted publishing, not a token
release.ymlpublishes over OIDC. npm mints a short-lived credential for this repository and this workflow filename, so there is noNPM_TOKENto leak, rotate, or scope wrongly.Two things have to line up outside this PR:
release.yml. Until that exists the publish step fails rather than falling back to anything, which is the behaviour you want.id-token: writeon the job — declared here. Neither alone is enough.npm is upgraded explicitly: trusted publishing needs 11.5.1+, and while Node 24 ships an npm 11 the exact patch moves with the runner image. Pinning the floor beats discovering it on a tag.
What it refuses to publish
Typecheck, tests and build run first, then two release-specific gates:
bin,distandweb.webis the SPA — a tarball missing it publishes a server with no UI, which looks fine in CI and breaks on install.package.json's version, so a mistyped tag cannot ship a version nobody asked for.workflow_dispatchrehearses all of it and publishes nothing.Verified against a real install, not just a dry run
That exercises the thing most likely to break on packaging:
webRootisdirname(module)/../web, which resolves fromsrc/in dev and fromdist/when installed, becausefilesships both at the package root. It served the SPA and a real project's graph.npm run tscclean, 433 tests passing.One thing to settle first
There is no
licensefield and no LICENSE file. npm will warn on publish, and a public package without one is ambiguous for anyone who installs it. Picking a license is not my call, so I have left it alone — but it is worth settling before the first publish rather than after.Follow-up, not in this PR
Once published, kubemicrovm-ops's
just viewcan drop its checkout-finding logic fornpx @intentius/behold serve .. Deliberately held back: that change is wrong until the package actually exists.