Skip to content

Latest commit

 

History

History
140 lines (115 loc) · 7.62 KB

File metadata and controls

140 lines (115 loc) · 7.62 KB

Releasing ic402

ic402 has two independent version numbers. Most releases move only the first.

Version What it is Bumps How
Package version (e.g. 2.2.4) the mops/npm release number every release scripts/version.sh
STABLE_SCHEMA_VERSION (e.g. 1) the library's stable-state contract (Ic402.STABLE_SCHEMA_VERSION in src/ic402/lib.mo) only when a Stable*State type changes incompatibly hand-edit lib.mo + check-stable-compat.sh --update

What gets published

  • mops: ic402 (source of truth: mops.toml; ships src/ic402/**/*.mo).
  • npm: @ic402/client (packages/client) and @ic402/mcp (integrations/mcp).
  • The root ic402 and @ic402/example-client packages are private — they carry the version for consistency but are not published.

scripts/version.sh syncs the version across 9 files (mops.toml, the four package.jsons, example/zk-verifier/Cargo.toml + Cargo.lock, and the runtime version literals in integrations/mcp/src/index.ts + example/client/src/index.ts). It does not touch integrations/mcp/README.md or CHANGELOG.md — those are edited by hand each release.


A. Standard release (no breaking stable change)

This is almost every release. The stable-compat CI job passes on its own, so there's nothing extra to do for the stable contract.

  1. Land your changes on master and confirm CI is green on the commit you're about to tag.
  2. Bump the version:
    ./scripts/version.sh 2.2.5          # or: patch | minor | major  (also: pnpm version:bump 2.2.5)
  3. By hand (the two files version.sh doesn't touch):
    • integrations/mcp/README.md — update both 2.2.x references (the version: line + the new McpServer({ … version: '2.2.x' }) prose).
    • CHANGELOG.md — prepend a ## v2.2.5 — YYYY-MM-DD entry (most-recent-first), with a release-type summary line + categorized ### subsections. State whether there are any wire/HTTP or @ic402/client breaking changes.
  4. Commit + tag (annotated tag, ic402 vX.Y.Z (…) subject — matches the existing tags):
    git commit -am "release: v2.2.5"
    git tag -a v2.2.5 -m "ic402 v2.2.5 (short description)"
  5. Push (your action):
    git push origin master && git push origin v2.2.5
  6. npm auth (one-time). The two npm packages publish with a per-repo token in a gitignored .npmrc, so ic402 and sibling repos (e.g. engramx) each publish with their own token — no shared global ~/.npmrc token. pnpm won't expand env vars in a project .npmrc, so the token is a literal value (safe: .npmrc is gitignored, never committed). Create it once:
    pnpm config set "//registry.npmjs.org/:_authToken" npm_xxxxxxxx --location project
    (or cp .npmrc.example .npmrc and paste your automation token). mops publish uses separate mops auth, not this token.
  7. Publish (after CI on the pushed tag is green):
    pnpm build:client && pnpm build:demo     # ensure dist/ is fresh
    mops publish                              # publishes the `ic402` mops package
    (cd packages/client && npm publish)       # @ic402/client   (first ever publish: --access public)
    (cd integrations/mcp && npm publish)      # @ic402/mcp       (first ever publish: --access public)
    Publishing is optional for a release with no consumer-facing code change (e.g. a docs/CI-only patch) — the git tag still marks it.

PATH gotcha: mops publish and npm publish run directly in your shell (not through a project script that sanitizes PATH). A sibling repo's node_modules/.bin ahead on PATH can shadow mops with a broken copy (Cannot find package '@dfinity/identity'). If you hit that, run the project's toolchain explicitly (e.g. /opt/homebrew/bin/mops publish) or drop the offending entry from PATH.

Failed to replace env in config: ${NPM_TOKEN} warning: this comes from the global ~/.npmrc (_authToken=${NPM_TOKEN}) when NPM_TOKEN is unset. It's harmless (install/build from the public registry needs no auth). Now that each repo carries its own gitignored .npmrc, the global token line is redundant — remove it from ~/.npmrc (or export NPM_TOKEN=…) to silence the warning. A project .npmrc with a literal token does not warn.


B. Release with a breaking stable-state change

A "breaking" change to one of the four library Stable*State types is a removed or retyped field, or a new field on an existing stable record (breaking because consumers hold these in mutable stable vars, whose type is invariant across upgrade). Adding a new variant case or a brand-new ?optional stable variable is not breaking.

You'll know you have one because CI's stable-compat job fails with BREAKING ic402 stable change with NO version bump. To release it safely:

  1. Bump Ic402.STABLE_SCHEMA_VERSION in src/ic402/lib.mo (12).
  2. Provide a migration. Wire the #migrate branch of the checkSchemaVersion guard (see the pattern in example/main.mo and docs/upgrade-safety.md), or document a state-dropping fresh deploy in the CHANGELOG.
  3. Advance the baseline:
    ./scripts/check-stable-compat.sh --update     # refuses unless step 1 happened; re-stamps the baseline
  4. Commit the bumped lib.mo + the new test/stable-anchor.most + your migration, then do the standard release (A). Call out the STABLE_SCHEMA_VERSION change and the migration prominently in the CHANGELOG.

Don't hand-edit test/stable-anchor.most — it's generated, and advancing it without a version bump defeats the gate (it carries a "do not hand-edit" header). Always use --update.

For an additive stable change you skip section B entirely: the gate stays green and you don't bump STABLE_SCHEMA_VERSION or advance the baseline. Leaving the baseline at an older signature is fine — it keeps proving "anyone from that version can still upgrade."


The stable-compat gate (what it's protecting)

scripts/check-stable-compat.sh (CI job stable-compat) compiles test/stable-anchor.mo — a fixture that persists exactly the four library Stable*State types — to a .most stable signature and compares it to the committed baseline with moc's own --stable-compatible oracle. It fails the build on a stable change that is upgrade-incompatible and doesn't bump STABLE_SCHEMA_VERSION, so a downstream consumer can never silently trap in loadStable on a live, fund-holding canister. A --self-test run proves the gate still discriminates. Full design + the consumer-side pattern: docs/upgrade-safety.md.

Pre-release checklist

CI runs all of this on push; to check locally before tagging:

mops test                                   # Motoko unit suites
pnpm test:client                            # @ic402/client
pnpm exec vitest run                         # MCP guards/security + integration (skips w/o a replica)
pnpm build:client && pnpm build:demo         # type-check + build the published TS
bash scripts/build-example.sh /tmp/ex.wasm   # B0: example stays installable (wasm locals budget)
./scripts/check-stable-compat.sh             # stable contract gate
./scripts/check-stable-compat.sh --self-test # gate self-test
bash scripts/gen-did.sh && git diff --exit-code example/example.did   # Candid in sync
pnpm format:check && pnpm lint

See CONTRIBUTING.md for the dev setup and docs/upgrade-safety.md for how consumers handle an ic402 bump.