How to cut and publish a release of one or more @did-btcr2/* packages.
Releases happen on demand. There’s no automated cadence: a release is cut whenever:
A release can be a single package or a batch (every package, or a subset). Batched releases are common because most changes to a downstream package (e.g., method) cascade requirements onto its dependents (api, cli).
The project follows semantic versioning with one caveat: pre-1.0 packages treat MINOR as breaking under strict semver. Currently the only post-1.0 package is common. Everything else is pre-1.0.
| Bump | Pre-1.0 (most packages) | Post-1.0 (common) |
|---|---|---|
| PATCH | Bug fix, internal refactor, build tooling change with no public API impact | Same |
| MINOR | New feature OR breaking change | New feature, backward-compatible |
| MAJOR | (rarely used pre-1.0) | Breaking change |
When in doubt, bump higher rather than lower. A spurious MAJOR bump is harmless; a missed one breaks consumers silently.
git checkout main
git pull origin main
git status
The working tree should be empty. If it isn’t, stash or commit before proceeding.
pnpm clean
pnpm install
pnpm build
pnpm build:tests && pnpm test
pnpm lint
All five must pass. If any step fails, do not release: fix main first, get the fix merged, then return to step 1.
Bump every package that has changed since its last published version. Edit each package’s package.json directly: there’s no automated bump tool currently in use.
For a small release, the relevant section of each package.json:
{
"name": "@did-btcr2/<pkg>",
"version": "X.Y.Z", // bump this
...
}
If a package has not changed since its last release, do not bump it. Republishing identical content with a new version number wastes consumer time and pollutes the registry.
git add packages/*/package.json
git commit -m "chore: bump <package(s)> to <version(s)>"
For batched releases, list each package + version in the commit body:
chore: bump patch versions across all packages
- common 8.0.1 to 8.0.2
- keypair 0.11.3 to 0.11.4
- ...
pnpm install # ensure pnpm-lock.yaml updates if any internal deps shifted
pnpm build
pnpm build:tests && pnpm test
If you bumped versions on a release branch, open a PR and merge per the PR Workflow. If you bumped directly on main (only for trivial PATCH releases on a clean tree), push directly:
git push origin main
Make sure you’re logged in:
npm whoami # should print your npm username
# if not logged in: npm login
Have your 2FA token ready if your npm account requires it.
Recommended: workspace publish (publishes all bumped packages in topological order automatically):
# Dry run first: shows what would be published without actually doing it
pnpm -r publish --access public --dry-run
# If the dry run looks correct:
pnpm -r publish --access public
pnpm -r publish only publishes packages whose version field is newer than what’s on the registry. It also rewrites workspace:^ references to concrete semver in the published tarballs.
Alternative: manual publish in dependency order: useful when you want to verify each landing on npm before proceeding to the next:
cd packages/common && pnpm publish --access public && cd -
cd packages/keypair && pnpm publish --access public && cd -
cd packages/smt && pnpm publish --access public && cd -
cd packages/cryptosuite && pnpm publish --access public && cd -
cd packages/bitcoin && pnpm publish --access public && cd -
cd packages/key-manager && pnpm publish --access public && cd -
cd packages/method && pnpm publish --access public && cd -
cd packages/api && pnpm publish --access public && cd -
cd packages/cli && pnpm publish --access public && cd -
The dependency order matters: a package can’t be installed by its dependents until it’s on the registry.
Verify the published tarballs work via a fresh consumer in a scratch directory:
mkdir /tmp/btcr2-release-check && cd /tmp/btcr2-release-check
pnpm init -y
pnpm add @did-btcr2/method@<just-published-version>
# ESM consumer
node --input-type=module -e "
const { DidBtcr2 } = await import('@did-btcr2/method');
console.log('ESM:', typeof DidBtcr2);
"
# CJS consumer
node -e "
const { DidBtcr2 } = require('@did-btcr2/method');
console.log('CJS:', typeof DidBtcr2);
"
Both should print function. If either fails, the published tarball is broken: yank it (npm unpublish @did-btcr2/method@X.Y.Z) within the 72-hour window and investigate.
For major releases or whenever you want a git anchor:
git tag v<version> -m "Release v<version>"
git push origin v<version>
For batched releases, use the highest version bumped or a date-based tag.
You bumped a version but pnpm/npm doesn’t think the local version is newer. Verify by checking npm:
npm view @did-btcr2/<pkg> version
If the registry has a newer version than your local file, your bump didn’t actually take effect or you’re on a stale branch. Check git log and your working tree.
Run npm login and re-authenticate. If you have 2FA, you’ll need to provide a token.
Don’t panic. The packages that already published are fine. Investigate the failure (often a permissions issue, a stale 2FA token, or a transient registry error). Re-run pnpm -r publish --access public: pnpm will skip packages whose published version already matches the local version, and only retry the ones that didn’t make it.
If it’s within 72 hours of publishing, you can yank with npm unpublish @did-btcr2/<pkg>@<version>. After 72 hours, npm policy prevents unpublishing (you’d have to publish a new patch version with the fix instead).
Test fixes locally before publishing again. The post-publish smoke test in step 8 exists specifically to catch this kind of thing before consumers hit it.
There is currently no automated CHANGELOG generation. Significant releases should have a one-paragraph summary in the GitHub release notes (created via gh release create v<version> --notes "...") describing:
For small PATCH releases, the GitHub release entry is optional: the commit history is sufficient.