feat(npm): publish as codewhale; keep deepseek-tui as deprecation shim

Rename the npm wrapper directory and package from `deepseek-tui` to
`codewhale`. Move under `npm/codewhale/`:
  - `package.json` renamed (name, bin, internal field) — keeps a
    `deepseekBinaryVersion` fallback so old metadata still works.
  - Bin entry points renamed to `bin/codewhale.js` and
    `bin/codewhale-tui.js`; they spawn the corresponding canonical
    binaries via the wrapper.
  - `scripts/artifacts.js` switches to the canonical asset-name matrix
    (`codewhale-*`, `codewhale-tui-*`) and `codewhale-artifacts-sha256.txt`.
  - `scripts/run.js` exports `runCodewhale` and `runCodewhaleTui`; the
    legacy `runDeepseek` exports are gone since nothing else inside the
    package depended on them.
  - `scripts/install.js`, `verify-release-assets.js`, `preflight-glibc.js`
    update brand-mention strings + User-Agent headers. Env vars
    (`DEEPSEEK_TUI_*`, `DEEPSEEK_*`) are explicitly anti-scope and are
    left in place.
  - Tests retargeted at the canonical asset names; all 19 still pass.
  - README rewritten with the new install command and a deprecation
    note about the old package.

Create a one-release deprecation shim at `npm/deepseek-tui/`:
  - `package.json` with no `bin`, just a postinstall script that
    prints a clear message telling the user to install `codewhale`
    instead.
  - `README.md` with the same migration note.
  - Will be removed in v0.9.0 (or whenever Hunter retires the shims).

Release-side scripts in `scripts/release/` follow the rename:
  - `prepare-local-release-assets.js` now requires `npm/codewhale/...`
    and copies the canonical `codewhale*` binaries.
  - `npm-wrapper-smoke.js` smokes the renamed package.
  - `check-versions.sh` reads `npm/codewhale/package.json` for the
    primary check and additionally pins the legacy shim package to
    the same version.
  - `check-published.sh` queries `codewhale@<version>` (with
    `codewhaleBinaryVersion` lookup that falls back to the legacy
    `deepseekBinaryVersion` field).
  - `.github/workflows/auto-tag.yml` watches both `npm/codewhale/` and
    `npm/deepseek-tui/` package.json for auto-tag triggers.

Verified:
  - `npm test` inside `npm/codewhale/` passes 19/19.
  - `npm install --dry-run --ignore-scripts` succeeds for both
    `npm/codewhale/` and `npm/deepseek-tui/`.
  - `scripts/release/check-versions.sh` reports OK.
  - Rust gates re-run: `cargo check`, `cargo fmt --check`,
    `cargo clippy -- -D warnings`, all clean.

No `npm publish` is run from this change — Hunter publishes manually
when the rebrand is ready to ship.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hunter Bown
2026-05-23 11:11:50 -05:00
parent 8da9fb7d52
commit 23daefbe24
24 changed files with 327 additions and 219 deletions
+29 -10
View File
@@ -56,29 +56,48 @@ fail=0
echo "Checking published release ${version}..."
if npm_version="$(npm view "deepseek-tui@${version}" version 2>/dev/null)"; then
echo "npm deepseek-tui@${npm_version} is published."
# Canonical post-rebrand npm package.
if npm_version="$(npm view "codewhale@${version}" version 2>/dev/null)"; then
echo "npm codewhale@${npm_version} is published."
else
echo "npm deepseek-tui@${version} is not published." >&2
echo "npm codewhale@${version} is not published." >&2
fail=1
fi
if npm_binary_version="$(npm view "deepseek-tui@${version}" deepseekBinaryVersion 2>/dev/null)"; then
# `codewhaleBinaryVersion` is the new internal version-pin field. Fall back
# to the legacy `deepseekBinaryVersion` field for old/transition packages.
binary_field=""
npm_binary_version=""
if value="$(npm view "codewhale@${version}" codewhaleBinaryVersion 2>/dev/null)" && [[ -n "${value}" ]]; then
binary_field="codewhaleBinaryVersion"
npm_binary_version="${value}"
elif value="$(npm view "codewhale@${version}" deepseekBinaryVersion 2>/dev/null)" && [[ -n "${value}" ]]; then
binary_field="deepseekBinaryVersion"
npm_binary_version="${value}"
fi
if [[ -n "${binary_field}" ]]; then
if [[ "${npm_binary_version}" == "${version}" ]]; then
echo "npm deepseekBinaryVersion=${npm_binary_version}."
echo "npm ${binary_field}=${npm_binary_version}."
elif [[ "${allow_npm_binary_mismatch}" == "1" ]]; then
echo "npm deepseekBinaryVersion=${npm_binary_version} (allowed packaging-only mismatch)."
echo "npm ${binary_field}=${npm_binary_version} (allowed packaging-only mismatch)."
else
echo "npm deepseekBinaryVersion=${npm_binary_version}, expected ${version}." >&2
echo "npm ${binary_field}=${npm_binary_version}, expected ${version}." >&2
fail=1
fi
elif [[ "${allow_npm_binary_mismatch}" == "1" ]]; then
echo "npm deepseekBinaryVersion is absent (allowed packaging-only mismatch)."
echo "npm codewhaleBinaryVersion is absent (allowed packaging-only mismatch)."
else
echo "npm deepseekBinaryVersion is absent for deepseek-tui@${version}." >&2
echo "npm codewhaleBinaryVersion is absent for codewhale@${version}." >&2
fail=1
fi
# Legacy `deepseek-tui` deprecation shim package. Best-effort check —
# absence after the transition release is expected and not fatal.
if legacy_version="$(npm view "deepseek-tui@${version}" version 2>/dev/null)"; then
echo "npm deepseek-tui@${legacy_version} (deprecation shim) is published."
fi
for crate in "${release_crates[@]}"; do
if curl -fsSL "https://crates.io/api/v1/crates/${crate}/${version}" >/dev/null 2>&1; then
echo "crates.io ${crate}@${version} is published."
@@ -89,7 +108,7 @@ for crate in "${release_crates[@]}"; do
done
if [[ "${fail}" == "0" ]]; then
echo "Published release OK: npm deepseek-tui@${version} and ${#release_crates[@]} crates are visible."
echo "Published release OK: npm codewhale@${version} and ${#release_crates[@]} crates are visible."
fi
exit "${fail}"
+15 -6
View File
@@ -5,10 +5,10 @@
# Checks performed:
# 1. No `crates/*/Cargo.toml` carries a literal `version = "x.y.z"`; every
# crate must inherit `version.workspace = true`.
# 2. `npm/deepseek-tui/package.json` `version` matches the workspace
# `version` in the root `Cargo.toml`. (The npm wrapper directory is
# renamed to `npm/codewhale/` in a follow-up phase; this script will
# be updated then.)
# 2. `npm/codewhale/package.json` `version` matches the workspace
# `version` in the root `Cargo.toml`. (`npm/deepseek-tui/` still
# exists during the transition as a deprecation shim package; its
# version is also checked.)
# 3. Internal `codewhale-*` path dependency pins match the workspace version.
# 4. The TUI crate's packaged changelog copy matches root `CHANGELOG.md`.
# 5. The current release has a dated Keep a Changelog entry and compare link.
@@ -32,11 +32,20 @@ fi
# 2) Workspace ↔ npm package.json.
workspace_version="$(grep -E '^version = "' Cargo.toml | head -n1 | sed -E 's/^version = "([^"]+)".*/\1/')"
npm_version="$(node -p "require('./npm/deepseek-tui/package.json').version")"
npm_version="$(node -p "require('./npm/codewhale/package.json').version")"
if [[ "${workspace_version}" != "${npm_version}" ]]; then
echo "::error::npm/deepseek-tui/package.json version (${npm_version}) does not match workspace Cargo.toml (${workspace_version})." >&2
echo "::error::npm/codewhale/package.json version (${npm_version}) does not match workspace Cargo.toml (${workspace_version})." >&2
fail=1
fi
# Also pin the legacy deprecation shim package to the same workspace version
# so a stale `deepseek-tui` doesn't ship pointing at a different release.
if [[ -f npm/deepseek-tui/package.json ]]; then
legacy_npm_version="$(node -p "require('./npm/deepseek-tui/package.json').version")"
if [[ "${workspace_version}" != "${legacy_npm_version}" ]]; then
echo "::error::npm/deepseek-tui/package.json version (${legacy_npm_version}) does not match workspace Cargo.toml (${workspace_version})." >&2
fail=1
fi
fi
# 3) Internal path dependency pins.
internal_dep_drift="$(
+4 -4
View File
@@ -8,7 +8,7 @@ const path = require("path");
const { spawn } = require("child_process");
const repoRoot = path.resolve(__dirname, "..", "..");
const packageDir = path.join(repoRoot, "npm", "deepseek-tui");
const packageDir = path.join(repoRoot, "npm", "codewhale");
const prepareAssetsScript = path.join(
repoRoot,
"scripts",
@@ -133,7 +133,7 @@ function parsePackJson(stdout) {
}
async function main() {
const tempRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "deepseek-npm-smoke-"));
const tempRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "codewhale-npm-smoke-"));
const releaseAssetsDir = path.join(tempRoot, "release-assets");
const packDir = path.join(tempRoot, "pack");
const installDir = path.join(tempRoot, "install");
@@ -165,11 +165,11 @@ async function main() {
await runCommand("npm", ["init", "-y"], { cwd: installDir });
await runCommand("npm", ["install", tarball], { cwd: installDir, env });
await runCommand("npx", ["--no-install", "deepseek", "doctor", "--help"], {
await runCommand("npx", ["--no-install", "codewhale", "doctor", "--help"], {
cwd: installDir,
env,
});
await runCommand("npx", ["--no-install", "deepseek-tui", "--help"], {
await runCommand("npx", ["--no-install", "codewhale-tui", "--help"], {
cwd: installDir,
env,
});
@@ -8,7 +8,7 @@ const {
allAssetNames,
CHECKSUM_MANIFEST,
detectBinaryNames,
} = require("../../npm/deepseek-tui/scripts/artifacts");
} = require("../../npm/codewhale/scripts/artifacts");
async function sha256(filePath) {
const content = await fs.readFile(filePath);
@@ -25,16 +25,16 @@ async function main() {
const buildDir = path.resolve(
process.argv[3] || path.join("target", "release"),
);
const { deepseek, tui } = detectBinaryNames();
const { codewhale, tui } = detectBinaryNames();
const isWindows = process.platform === "win32";
const assets = [
{
source: path.join(buildDir, isWindows ? "deepseek.exe" : "deepseek"),
target: deepseek,
source: path.join(buildDir, isWindows ? "codewhale.exe" : "codewhale"),
target: codewhale,
},
{
source: path.join(buildDir, isWindows ? "deepseek-tui.exe" : "deepseek-tui"),
source: path.join(buildDir, isWindows ? "codewhale-tui.exe" : "codewhale-tui"),
target: tui,
},
];
@@ -45,9 +45,9 @@ async function main() {
continue;
}
assets.push({
source: assetName.startsWith("deepseek-tui")
? path.join(buildDir, isWindows ? "deepseek-tui.exe" : "deepseek-tui")
: path.join(buildDir, isWindows ? "deepseek.exe" : "deepseek"),
source: assetName.startsWith("codewhale-tui")
? path.join(buildDir, isWindows ? "codewhale-tui.exe" : "codewhale-tui")
: path.join(buildDir, isWindows ? "codewhale.exe" : "codewhale"),
target: assetName,
});
}