| 1 | # DeepSeek TUI Release Runbook |
| 2 | |
| 3 | This runbook is the source of truth for shipping Rust crates, GitHub release assets, |
| 4 | and the `deepseek-tui` npm wrapper. |
| 5 | |
| 6 | Current packaging note: |
| 7 | - `deepseek-tui` is the live runtime and TUI package shipped to users today. |
| 8 | - `deepseek-tui-core` is a supporting workspace crate for the extraction/parity effort, not a replacement for the shipping runtime. |
| 9 | |
| 10 | ## Canonical Publish Targets |
| 11 | |
| 12 | - End-user crates: |
| 13 | - `deepseek-tui` |
| 14 | - `deepseek-tui-cli` |
| 15 | - Supporting crates published from this workspace: |
| 16 | - `deepseek-secrets` |
| 17 | - `deepseek-config` |
| 18 | - `deepseek-protocol` |
| 19 | - `deepseek-state` |
| 20 | - `deepseek-agent` |
| 21 | - `deepseek-execpolicy` |
| 22 | - `deepseek-hooks` |
| 23 | - `deepseek-mcp` |
| 24 | - `deepseek-tools` |
| 25 | - `deepseek-core` |
| 26 | - `deepseek-app-server` |
| 27 | - `deepseek-tui-core` |
| 28 | - `deepseek-cli` on crates.io is an unrelated crate and is not part of this release flow. |
| 29 | |
| 30 | ## Version Coordination |
| 31 | |
| 32 | - Rust crates inherit the shared workspace version from [Cargo.toml](../Cargo.toml). |
| 33 | - Internal path dependency versions should match the shared workspace version; stale older pins are release blockers once the workspace version moves. |
| 34 | - The npm wrapper version lives in [npm/deepseek-tui/package.json](../npm/deepseek-tui/package.json). |
| 35 | - `deepseekBinaryVersion` controls which GitHub release binaries the npm wrapper downloads. |
| 36 | - Packaging-only npm releases are allowed: |
| 37 | - bump the npm package version |
| 38 | - leave `deepseekBinaryVersion` pinned to the previously released Rust binaries |
| 39 | - rerun `npm pack` smoke checks before `npm publish` |
| 40 | |
| 41 | ## Preflight |
| 42 | |
| 43 | Run these from the repository root before cutting a tag: |
| 44 | |
| 45 | ```bash |
| 46 | ./scripts/release/check-versions.sh # version drift between workspace, npm, lockfile |
| 47 | cargo fmt --all -- --check |
| 48 | cargo check --workspace --all-targets --locked |
| 49 | cargo clippy --workspace --all-targets --all-features --locked -- -D warnings |
| 50 | cargo test --workspace --all-features --locked |
| 51 | cargo publish --dry-run --locked --allow-dirty -p deepseek-tui |
| 52 | ./scripts/release/publish-crates.sh dry-run |
| 53 | ``` |
| 54 | |
| 55 | `check-versions.sh` also runs in CI on every push/PR (the `versions` job in |
| 56 | `.github/workflows/ci.yml`), so drift between `Cargo.toml`, the per-crate |
| 57 | manifests, `npm/deepseek-tui/package.json`, and `Cargo.lock` is caught before |
| 58 | release time rather than at it. |
| 59 | |
| 60 | `publish-crates.sh dry-run` performs a full `cargo publish --dry-run` for crates |
| 61 | without unpublished workspace dependencies and a packaging preflight for dependent |
| 62 | workspace crates. That avoids false negatives from crates.io not yet containing the |
| 63 | new workspace version while still validating package contents before publish. |
| 64 | |
| 65 | For npm wrapper verification, build the two shipped binaries and run the |
| 66 | cross-platform smoke harness. This packs the npm wrapper, installs it into a |
| 67 | clean temporary project, serves local release assets over HTTP, and checks both |
| 68 | the dispatcher-to-TUI path (`deepseek doctor --help`) and the direct TUI |
| 69 | entrypoint (`deepseek-tui --help`). |
| 70 | |
| 71 | ```bash |
| 72 | cargo build --release --locked -p deepseek-tui-cli -p deepseek-tui |
| 73 | node scripts/release/npm-wrapper-smoke.js |
| 74 | ``` |
| 75 | |
| 76 | Set `DEEPSEEK_TUI_KEEP_SMOKE_DIR=1` to keep the temporary pack/install |
| 77 | directory for inspection. |
| 78 | |
| 79 | To exercise `npm run release:check` locally as well, regenerate the local asset |
| 80 | directory with a full asset matrix fixture before starting the server: |
| 81 | |
| 82 | ```bash |
| 83 | DEEPSEEK_TUI_PREPARE_ALL_ASSETS=1 node scripts/release/prepare-local-release-assets.js |
| 84 | cd npm/deepseek-tui |
| 85 | DEEPSEEK_TUI_VERSION=X.Y.Z DEEPSEEK_TUI_RELEASE_BASE_URL=http://127.0.0.1:8123/ npm run release:check |
| 86 | ``` |
| 87 | |
| 88 | Set `DEEPSEEK_TUI_VERSION` to the npm package version you are verifying for that local run. |
| 89 | |
| 90 | The CI workflow runs the same tarball install + delegated-entrypoint smoke test |
| 91 | on Linux, macOS, and Windows. |
| 92 | |
| 93 | After publishing, prove the release is visible in both registries: |
| 94 | |
| 95 | ```bash |
| 96 | ./scripts/release/check-published.sh X.Y.Z |
| 97 | ``` |
| 98 | |
| 99 | Do not mark a Rust release complete until that command sees `deepseek-tui@X.Y.Z` |
| 100 | on npm and every `deepseek-*` crate at `X.Y.Z` on crates.io. For a rare |
| 101 | npm packaging-only release, run with `--allow-npm-binary-mismatch` and keep the |
| 102 | release notes explicit that no new Rust binary version shipped. |
| 103 | |
| 104 | ## Rust Crates Release |
| 105 | |
| 106 | Crate publishing to crates.io is **manual** — there is no automated |
| 107 | `crates-publish` GitHub workflow. Operators run the helpers in |
| 108 | `scripts/release/` from a developer workstation that has `cargo login` |
| 109 | configured. |
| 110 | |
| 111 | 1. Update the workspace version in [Cargo.toml](../Cargo.toml). |
| 112 | 2. Run `./scripts/release/check-versions.sh` and |
| 113 | `./scripts/release/publish-crates.sh dry-run` locally; both must be clean. |
| 114 | 3. Tag the release as `vX.Y.Z` (typically by pushing the version bump to |
| 115 | `main` and letting `auto-tag.yml` create the tag — see the npm wrapper |
| 116 | release section below for the `RELEASE_TAG_PAT` requirement). |
| 117 | 4. Publish crates in this order with `./scripts/release/publish-crates.sh publish`: |
| 118 | - `deepseek-secrets` |
| 119 | - `deepseek-config` |
| 120 | - `deepseek-protocol` |
| 121 | - `deepseek-state` |
| 122 | - `deepseek-agent` |
| 123 | - `deepseek-execpolicy` |
| 124 | - `deepseek-hooks` |
| 125 | - `deepseek-mcp` |
| 126 | - `deepseek-tools` |
| 127 | - `deepseek-core` |
| 128 | - `deepseek-app-server` |
| 129 | - `deepseek-tui-core` |
| 130 | - `deepseek-tui-cli` |
| 131 | - `deepseek-tui` |
| 132 | 5. Wait for each published crate version to appear on crates.io before publishing dependents. |
| 133 | |
| 134 | The publish helper is idempotent for reruns: already-published crate versions are skipped. |
| 135 | |
| 136 | ## GitHub Release Assets |
| 137 | |
| 138 | `.github/workflows/release.yml` builds these binaries: |
| 139 | |
| 140 | - `deepseek-linux-x64` |
| 141 | - `deepseek-macos-x64` |
| 142 | - `deepseek-macos-arm64` |
| 143 | - `deepseek-windows-x64.exe` |
| 144 | - `deepseek-tui-linux-x64` |
| 145 | - `deepseek-tui-macos-x64` |
| 146 | - `deepseek-tui-macos-arm64` |
| 147 | - `deepseek-tui-windows-x64.exe` |
| 148 | |
| 149 | The release job also uploads `deepseek-artifacts-sha256.txt`. The npm installer and |
| 150 | release verification script both depend on that checksum manifest. |
| 151 | |
| 152 | ## npm Wrapper Release |
| 153 | |
| 154 | **The npm publish step is manual.** `release.yml` no longer runs `npm publish` |
| 155 | because the npm account requires 2FA OTP on every publish, and an automation |
| 156 | token that bypasses 2FA has not been provisioned. The GitHub Release flow |
| 157 | remains fully automated; only the npm wrapper publish requires a developer |
| 158 | on a workstation with `npm login` and an authenticator app. |
| 159 | |
| 160 | ### Steps |
| 161 | |
| 162 | 1. Set the npm package version in [npm/deepseek-tui/package.json](../npm/deepseek-tui/package.json) to match the workspace `Cargo.toml`. CI's version-drift guard will catch mismatches before tag. |
| 163 | 2. Set `deepseekBinaryVersion` to the GitHub release tag that should supply binaries. |
| 164 | 3. Push the version bump to `main`. `auto-tag.yml` creates the matching `vX.Y.Z` tag, and `release.yml` builds the binary matrix and drafts the GitHub Release. |
| 165 | 4. **Wait for the GitHub Release to finalize** with all eight signed binaries plus `deepseek-artifacts-sha256.txt`. The npm `prepublishOnly` hook (`scripts/verify-release-assets.js`) requires every asset to be present. |
| 166 | 5. From a developer machine, publish the npm wrapper manually: |
| 167 | |
| 168 | ```bash |
| 169 | cd npm/deepseek-tui |
| 170 | npm publish --access public |
| 171 | # (you will be prompted for the npm OTP from your authenticator) |
| 172 | ``` |
| 173 | |
| 174 | ### Why not automated? |
| 175 | |
| 176 | - `release.yml`'s old `publish-npm` job used `secrets.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. |
| 177 | - The standalone `publish-npm.yml` and `crates-publish.yml` workflows 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. |
| 178 | |
| 179 | ### If you fix the token later |
| 180 | |
| 181 | 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. |
| 182 | |
| 183 | ## Recovery and Rollback |
| 184 | |
| 185 | - Crates publish partially: |
| 186 | - rerun `./scripts/release/publish-crates.sh publish` |
| 187 | - already-published crate versions will be skipped |
| 188 | - GitHub assets missing or checksum manifest incomplete: |
| 189 | - fix `.github/workflows/release.yml` |
| 190 | - retag or upload corrected assets before `npm publish` |
| 191 | - npm packaging-only problem: |
| 192 | - bump only the npm package version |
| 193 | - keep `deepseekBinaryVersion` on the last known-good Rust release |
| 194 | - repack and republish the wrapper |
| 195 | - A bad npm publish cannot be overwritten: |
| 196 | - publish a new npm version with corrected metadata or install logic |
| 197 |