This runbook is the source of truth for shipping Rust crates, GitHub release assets,
and the codewhale npm wrapper.
Current packaging note:
codewhale-tuiis the live runtime crate shipped to users today.codewhale-app-serveris a supporting library crate. The shipped entrypoint iscodewhale app-server; do not add or publish a standalone app-server binary.
- End-user crates:
codewhale-tuicodewhale-cli
- Supporting crates published from this workspace:
codewhale-secretscodewhale-configcodewhale-protocolcodewhale-statecodewhale-agentcodewhale-execpolicycodewhale-hookscodewhale-mcpcodewhale-toolscodewhale-corecodewhale-app-servercodewhale-whaleflow
- Rust crates inherit the shared workspace version from Cargo.toml.
- Internal path dependency versions should match the shared workspace version; stale older pins are release blockers once the workspace version moves.
- The npm wrapper version lives in npm/codewhale/package.json.
codewhaleBinaryVersioncontrols which GitHub release binaries the npm wrapper downloads.- Packaging-only npm releases are allowed:
- bump the npm package version
- leave
codewhaleBinaryVersionpinned to the previously released Rust binaries - rerun
npm packsmoke checks beforenpm publish
Run these from the repository root before cutting a tag:
./scripts/release/check-versions.sh # version drift between workspace, npm, lockfile
cargo fmt --all -- --check
cargo check --workspace --all-targets --locked
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
cargo test --workspace --all-features --locked
cargo publish --dry-run --locked --allow-dirty -p codewhale-tui
./scripts/release/publish-crates.sh dry-runcheck-versions.sh also runs in CI on every push/PR (the versions job in
.github/workflows/ci.yml), so drift between Cargo.toml, the per-crate
manifests, npm/codewhale/package.json, and Cargo.lock is caught before
release time rather than at it.
The source-controlled CNB pipeline mirrors the heavy Linux version/fmt/check/
clippy/test/npm-smoke gates for fix/*, rebrand/*, work/v*, and main.
GitHub Actions keeps the cheap drift/fmt statuses plus macOS and Windows
coverage, while CNB carries the Linux work.
publish-crates.sh dry-run performs a full cargo publish --dry-run for crates
without unpublished workspace dependencies and a packaging preflight for dependent
workspace crates. That avoids false negatives from crates.io not yet containing the
new workspace version while still validating package contents before publish.
For npm wrapper verification, build the two shipped binaries and run the
cross-platform smoke harness. This packs the npm wrapper, installs it into a
clean temporary project, serves local release assets over HTTP, and checks both
the dispatcher-to-TUI path (codewhale doctor --help) and the direct TUI
entrypoint (codewhale-tui --help).
cargo build --release --locked -p codewhale-cli -p codewhale-tui
node scripts/release/npm-wrapper-smoke.jsSet DEEPSEEK_TUI_KEEP_SMOKE_DIR=1 to keep the temporary pack/install
directory for inspection.
To exercise npm run release:check locally as well, regenerate the local asset
directory with a full asset matrix fixture before starting the server:
DEEPSEEK_TUI_PREPARE_ALL_ASSETS=1 node scripts/release/prepare-local-release-assets.js
cd npm/codewhale
DEEPSEEK_TUI_VERSION=X.Y.Z DEEPSEEK_TUI_RELEASE_BASE_URL=http://127.0.0.1:8123/ npm run release:checkSet DEEPSEEK_TUI_VERSION to the npm package version you are verifying for that local run.
The CNB workflow runs the Linux tarball install + delegated-entrypoint smoke test; GitHub Actions keeps macOS and Windows smoke coverage.
After publishing, prove the release is visible in both registries:
./scripts/release/check-published.sh X.Y.ZDo not mark a Rust release complete until that command sees codewhale@X.Y.Z
on npm and every codewhale-* crate at X.Y.Z on crates.io. For a rare
npm packaging-only release, run with --allow-npm-binary-mismatch and keep the
release notes explicit that no new Rust binary version shipped.
After a release or scratch integration branch lands, run the branch hygiene helper before pruning anything:
./scripts/release/branch-hygiene.sh --release-branch codex/vX.Y.ZThe default mode is a dry run. It reports the current checkout branch, main ref,
local and remote release tips, safe local or remote branch deletes, branches
kept for contributor work, and branches that still need a human decision. Review
that report before running --prune --yes, and add --prune-remote only when
you have confirmed the remote branches are safe to delete.
Use --remote upstream when you are working from a fork and the canonical
release refs live on the upstream remote instead of origin.
Verify the helper itself after changing it:
bash scripts/release/branch-hygiene.test.sh
bash scripts/release/ensure-release-on-main.test.shThose scripts are pinned to LF line endings so the same command works from a Windows checkout under Bash.
Crate publishing to crates.io is manual — there is no automated
crates-publish GitHub workflow. Operators run the helpers in
scripts/release/ from a developer workstation that has cargo login
configured.
Release commits must land on main before any vX.Y.Z tag is pushed. Do not
tag a release-only branch. Open the release PR against main, let required
review and CI finish, merge it, then tag the commit that is reachable from
main or let auto-tag.yml create that tag after the version bump reaches
main. This is what lets GitHub process Closes #N lines automatically and
show the release PR as merged. The tag release workflow runs
scripts/release/ensure-release-on-main.sh for tag pushes and manual dispatches,
and fails branch-only release sources before assets are published.
- Write the CHANGELOG entry, then run
./scripts/release/prepare-release.sh X.Y.Z— it bumps every version-bearing file (workspace + crate pins + npm wrapper + README install tags), refreshes the lockfile and generated files, and runscheck-versions.sh. - Run
./scripts/release/publish-crates.sh dry-runlocally; it must be clean. - Merge the release PR into
mainbefore tagging. The normal path is to push the version bump PR tomainand letauto-tag.ymlcreatevX.Y.Zfrom the main commit; see the npm wrapper release section below for theRELEASE_TAG_PATrequirement. - Publish crates in this order with
./scripts/release/publish-crates.sh publish:codewhale-mcpcodewhale-protocolcodewhale-releasecodewhale-secretscodewhale-statecodewhale-whaleflowcodewhale-execpolicycodewhale-hookscodewhale-toolscodewhale-configcodewhale-agentcodewhale-tuicodewhale-corecodewhale-app-servercodewhale-cli
- Wait for each published crate version to appear on crates.io before publishing dependents.
The publish helper is idempotent for reruns: already-published crate versions are skipped.
.github/workflows/release.yml builds these binaries:
codewhale-linux-x64codewhale-macos-x64codewhale-macos-arm64codewhale-windows-x64.execodewhale-tui-linux-x64codewhale-tui-macos-x64codewhale-tui-macos-arm64codewhale-tui-windows-x64.exe
The release job also uploads codewhale-artifacts-sha256.txt. The npm installer and
release verification script both depend on that checksum manifest.
The npm publish step is manual. release.yml no longer runs npm publish
because the npm account requires 2FA OTP on every publish, and an automation
token that bypasses 2FA has not been provisioned. The GitHub Release flow
remains fully automated; only the npm wrapper publish requires a developer
on a workstation with npm login and an authenticator app.
- Set the npm package version in npm/codewhale/package.json to match the workspace
Cargo.toml. CI's version-drift guard will catch mismatches before tag. - Set
codewhaleBinaryVersionto the GitHub release tag that should supply binaries. - Push the version bump to
main.auto-tag.ymlcreates the matchingvX.Y.Ztag, andrelease.ymlbuilds the binary matrix and drafts the GitHub Release. - Wait for the GitHub Release to finalize with all eight signed binaries plus
codewhale-artifacts-sha256.txt. The npmprepublishOnlyhook (scripts/verify-release-assets.js) requires every asset to be present. - From a developer machine, publish the npm wrapper manually:
cd npm/codewhale
npm publish --access public
# (you will be prompted for the npm OTP from your authenticator)release.yml's oldpublish-npmjob usedsecrets.NPM_TOKEN, but npm's 2FA-by-default policy means a publish token must be either an automation token with "Bypass 2FA for token authentication" enabled OR an account-level 2FA-disabled state. We don't have either configured.- The standalone
publish-npm.ymlandcrates-publish.ymlworkflows have been removed; no inert automation plumbing remains. A future move to npm Trusted Publishing (OIDC) would re-introduce a dedicated workflow at that point.
To re-enable automated publish: provision an npm automation token with "Bypass 2FA for token authentication" enabled (or set up npm Trusted Publishing via OIDC), store the corresponding secret on the repo, and re-add a publish-npm job to release.yml (or a dedicated workflow) along with reverting this section's "manual" framing.
Every push to main, fix/*, rebrand/*, work/v*, and every v* tag is mirrored to
cnb.cool/codewhale.net/codewhale via the Sync to CNB workflow
so users behind GitHub-blocking networks can fetch the source and so CNB can
run the heavy Linux CI lane. After a release tag, verify the mirror caught
it before declaring the release shipped:
git ls-remote https://cnb.cool/codewhale.net/codewhale.git refs/tags/vX.Y.ZIf the workflow failed for the release tag, the manual fallback is
documented in docs/CNB_MIRROR.md (one-time git remote add cnb …, then git push cnb vX.Y.Z).
- User-facing rollback:
- npm:
npm install -g codewhale@X.Y.Z - Cargo:
cargo install codewhale-cli --version X.Y.Z --locked --forceandcargo install codewhale-tui --version X.Y.Z --locked --force - manual assets: download binaries or the platform archive plus the matching
codewhale-artifacts-sha256.txtorcodewhale-bundles-sha256.txtmanifest fromhttps://github.com/Hmbown/CodeWhale/releases/tag/vX.Y.Z - workspace files: use
/restore list [N]and/restore <N>for side-git snapshots; this does not change the installed binary version or rewrite conversation history - keep docs/INSTALL.md in sync with these commands
- npm:
- Crates publish partially:
- rerun
./scripts/release/publish-crates.sh publish - already-published crate versions will be skipped
- rerun
- GitHub assets missing or checksum manifest incomplete:
- fix
.github/workflows/release.yml - retag or upload corrected assets before
npm publish
- fix
- npm packaging-only problem:
- bump only the npm package version
- keep
codewhaleBinaryVersionon the last known-good Rust release - repack and republish the wrapper
- A bad npm publish cannot be overwritten:
- publish a new npm version with corrected metadata or install logic
- CNB mirror failed for the release tag:
- check the run via
gh run list --workflow=sync-cnb.yml - retrigger with
gh workflow run sync-cnb.yml, or push the tag manually per docs/CNB_MIRROR.md
- check the run via