Releasing PBVex
PBVex publishes one unscoped npm package, five scoped npm packages, and standalone backend archives from the same SemVer tag:
| Artifact | Purpose |
|---|---|
pbvex | CLI and server-authoring API |
@pbvex/server | Complete GoReleaser backend binary matrix and pbvex-server launcher |
@pbvex/protocol | Shared wire protocol |
@pbvex/client | Browser-neutral client |
@pbvex/react | React bindings |
@pbvex/svelte | Svelte bindings |
| GitHub release archives | Self-contained pbvex backend for Linux, macOS, and Windows |
The package is named pbvex, not @pbvex/pbvex. The @pbvex scope groups the supporting packages.
One-time npm bootstrap
npm requires a package to exist before a trusted publisher can be configured. Perform these steps once from a trusted maintainer workstation. They do not belong in CI.
Create an npm account, enable account-level two-factor authentication, and create or claim the
pbvexnpm organization/scope. Make sure the account can publish both the unscopedpbvexpackage and public packages under@pbvex.Install the repository's declared pnpm version, authenticate interactively with
npm login, and check that every package is still available:bashnpm view pbvex npm view @pbvex/server npm view @pbvex/protocol npm view @pbvex/client npm view @pbvex/react npm view @pbvex/svelteA
404 Not Foundis expected before the first publication. Stop if a name belongs to someone else.Set every publishable
package.jsonto the same initial version, then run:bashpnpm install --frozen-lockfile pnpm lint pnpm test pnpm pack:smoke goreleaser build --snapshot --clean --parallelism 1 node scripts/stage-server-binaries.mjs NPM_CONFIG_PROVENANCE=false pnpm --dir packages/protocol publish --access public --no-git-checks NPM_CONFIG_PROVENANCE=false pnpm --dir packages/server publish --access public --no-git-checks NPM_CONFIG_PROVENANCE=false pnpm --dir packages/pbvex publish --access public --no-git-checks NPM_CONFIG_PROVENANCE=false pnpm --dir packages/client publish --access public --no-git-checks NPM_CONFIG_PROVENANCE=false pnpm --dir packages/react publish --access public --no-git-checks NPM_CONFIG_PROVENANCE=false pnpm --dir packages/svelte publish --access public --no-git-checksEnter the npm one-time password when prompted. Provenance is disabled only for this local bootstrap because npm provenance is generated by supported CI providers.
Open the settings for each of the six packages on npmjs.com and add the same GitHub Actions trusted publisher:
Setting Value Organization or user nathabonfim59Repository pbvexWorkflow filename npm-publish.yamlEnvironment npmAllowed action npm publishThe filename and environment are case-sensitive. Configure every package; a trusted publisher is attached to one package, not an entire scope.
After one trusted tag release succeeds, set npm package access to require two-factor authentication and disallow token-based publishing. Revoke any automation token. PBVex CI does not use
NPM_TOKEN.
First publication of @pbvex/server in an existing PBVex release line
If the other PBVex packages already exist on npm, claim only the new server package from a maintainer workstation. Use a disposable bootstrap prerelease; do not consume the next coordinated release version before trusted publishing is configured:
npm login
BOOTSTRAP_VERSION=0.0.0-bootstrap.0
npm view "@pbvex/server@$BOOTSTRAP_VERSION"
# Continue only when npm reports that this bootstrap version does not exist.
pnpm install --frozen-lockfile
pnpm lint
pnpm test
pnpm pack:smoke
goreleaser build --snapshot --clean --parallelism 1
node scripts/stage-server-binaries.mjs
pnpm --filter @pbvex/server build
BOOTSTRAP_DIR=$(mktemp -d)
cp -R packages/server/. "$BOOTSTRAP_DIR/"
npm --prefix "$BOOTSTRAP_DIR" version "$BOOTSTRAP_VERSION" --no-git-tag-version
NPM_CONFIG_PROVENANCE=false npm publish "$BOOTSTRAP_DIR" --access public --tag bootstrapVerify the public package from a clean temporary installation:
VERIFY_DIR=$(mktemp -d)
npm install --prefix "$VERIFY_DIR" "@pbvex/server@$BOOTSTRAP_VERSION"
"$VERIFY_DIR/node_modules/.bin/pbvex-server" serve --help
npm view "@pbvex/server@$BOOTSTRAP_VERSION" dist.integrityAfter the bootstrap package exists, configure its trusted publisher using the settings above. The normal signed-tag release can then publish the next real version of all six packages together, including the exact @pbvex/server version required by pbvex. Do not publish that coordinated version manually and do not attempt to republish an existing pbvex version merely to add the dependency.
One-time GitHub hardening
Repository settings cannot be fully expressed by workflow files. Configure these controls in GitHub before creating a release tag:
- Under Settings > Actions > General, set the default
GITHUB_TOKENpermission to read-only, require approval for workflows from fork pull requests, and enable the policy that requires actions to be pinned to a full-length commit SHA. - Create an environment named
npm. Allow only tags matchingv*.*.*, add a required maintainer reviewer, prevent self-review when the plan supports it, and disable administrator bypass where practical. Do not add npm secrets. - Create a
github-releaseenvironment with the same tag and reviewer rules. It also needs no stored secret; GoReleaser uses the short-lived repositoryGITHUB_TOKEN. - Add an active tag ruleset for
v*.*.*. Restrict tag creation, updates, and deletion to release maintainers and require signed tags if every authorized maintainer can satisfy that policy. - Protect
master: require pull requests, CODEOWNER review for workflow and release files, resolved conversations, and theCIchecks. Enable immutable GitHub Releases if the repository setting is available. - Keep private vulnerability reporting and secret scanning/push protection enabled. Review Dependabot pull requests like any other dependency change; a passing build is not proof that an update is trustworthy.
The workflows deliberately do not use pull_request_target. Fork pull requests run .github/workflows/ci.yaml with read-only access and no secrets. The npm publish job does not check out or build repository code: it receives the tarballs produced by its unprivileged validation job, verifies their checksums, and only then receives an OIDC identity through the protected npm environment.
Prepare a release
Update the version in all six publishable package manifests. The root workspace is private and its version is not published.
Update the PBVex section of
CHANGELOG.md.Run the complete local gates:
bashpnpm install --frozen-lockfile pnpm lint pnpm build pnpm test pnpm pack:smoke cd backend test -z "$(gofmt -l .)" go vet ./... go test -count=1 ./... CGO_ENABLED=1 go test -race -count=1 ./... cd .. goreleaser check ./scripts/release-validate.sh goreleaser release --snapshot --clean node scripts/stage-server-binaries.mjs VALIDATE_TAG_TEST=1 ./scripts/validate-tag.shMerge the release commit to
master. Create a signed annotated tag whose version exactly matches every package manifest, then push only that tag:bashgit tag -s v0.1.0 -m 'PBVex v0.1.0' git push origin v0.1.0
The tag starts both publishing workflows. Stable versions use npm's latest distribution tag; SemVer prereleases use next. GoReleaser publishes archives for supported Linux, macOS, and Windows targets plus checksums.txt. GitHub also records build-provenance attestations for the archives, while npm trusted publishing automatically records npm provenance. The npm workflow builds the same GoReleaser target matrix, stages it into @pbvex/server, publishes that package before pbvex, and then publishes the client integrations.
Do not move or reuse a release tag. If publication partially fails, determine which immutable package versions already exist, fix the cause, and release a new patch version. npm versions and immutable GitHub releases cannot safely be overwritten.
Verify a release
Download an archive and checksums.txt from the GitHub release, then verify both its checksum and GitHub attestation:
sha256sum --check --ignore-missing checksums.txt
gh attestation verify pbvex_0.1.0_linux_amd64.tar.gz --repo nathabonfim59/pbvexConfirm each npm page shows the expected version and provenance, then test the CLI from a clean directory:
npx --yes pbvex@0.1.0 --help