3efa6aad7d
Rename the canonical binaries: - `deepseek` → `codewhale` (CLI dispatcher) - `deepseek-tui` → `codewhale-tui` (TUI runtime) Both legacy names continue to ship as tiny deprecation shims that print a one-line warning to stderr and forward argv to the new binary. The shims are produced by two new `[[bin]]` entries in `crates/cli/Cargo.toml` and `crates/tui/Cargo.toml` pointing at small source files under `src/bin/`. They will be removed in v0.9.0. Touchpoints: - Cargo bin entries + new shim source files. - clap `name`/`bin_name`/usage strings flip to `codewhale`. - Dispatcher's sibling-binary discovery looks for `codewhale-tui` and reports `codewhale` in its error/help prose. `DEEPSEEK_TUI_BIN` env var stays — env vars are explicitly anti-scope. - `update.rs` now downloads `codewhale-*` assets and verifies them against `codewhale-artifacts-sha256.txt`. Legacy `deepseek-*` assets and `deepseek-artifacts-sha256.txt` are still produced by the release matrix so v0.8.40's `deepseek update` keeps working through one transition release. - `ci.yml`, `nightly.yml`, `release.yml` updated to build/upload the new canonical binaries; `release.yml`'s matrix doubles to also ship the legacy shim binaries so v0.8.40 update clients land on the shim. - `scripts/release/crates.sh` and `check-versions.sh` updated for the renamed crate names from R1. Local gates green: `cargo check --workspace --all-targets --locked`, `cargo fmt --all -- --check`, `cargo clippy --workspace --all-targets --all-features --locked -- -D warnings`, `cargo test --workspace --all-features --locked` (3226+ pass, 0 fail), and `cargo build --release` produces all four binaries: - target/release/codewhale (canonical dispatcher) - target/release/codewhale-tui (canonical TUI) - target/release/deepseek (legacy shim, forwards to codewhale) - target/release/deepseek-tui (legacy shim, forwards to codewhale-tui) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
443 lines
17 KiB
YAML
443 lines
17 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags: ['v*']
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'Package/release version to publish to npm, without the leading v'
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
RUSTFLAGS: -Dwarnings
|
|
|
|
jobs:
|
|
parity:
|
|
if: github.event_name == 'push'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
components: clippy, rustfmt
|
|
- name: Install Linux system dependencies
|
|
if: runner.os == 'Linux'
|
|
run: |
|
|
for i in 1 2 3 4 5; do
|
|
sudo apt-get update && break
|
|
echo "apt-get update failed (attempt $i); retrying in 15s"
|
|
sleep 15
|
|
done
|
|
sudo apt-get install -y libdbus-1-dev pkg-config
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
cache-bin: false
|
|
- name: Format check
|
|
run: cargo fmt --all -- --check
|
|
- name: Compile check
|
|
run: cargo check --workspace --all-targets --locked
|
|
- name: Clippy
|
|
run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
|
|
- name: Workspace tests
|
|
run: cargo test --workspace --all-features --locked
|
|
- name: TUI snapshot parity
|
|
run: cargo test -p codewhale-tui-core --test snapshot --locked
|
|
- name: Protocol schema parity
|
|
run: cargo test -p codewhale-protocol --test parity_protocol --locked
|
|
- name: State persistence parity
|
|
run: cargo test -p codewhale-state --test parity_state --locked
|
|
- name: Lockfile drift guard
|
|
run: git diff --exit-code -- Cargo.lock
|
|
|
|
resolve:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
tag: ${{ steps.release.outputs.tag }}
|
|
source_ref: ${{ steps.release.outputs.source_ref }}
|
|
sha: ${{ steps.release.outputs.sha }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
- name: Resolve release source
|
|
id: release
|
|
shell: bash
|
|
run: |
|
|
if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then
|
|
tag="v${{ inputs.version }}"
|
|
git fetch --force origin "refs/tags/${tag}:refs/tags/${tag}"
|
|
sha="$(git rev-list -n 1 "${tag}")"
|
|
source_ref="${tag}"
|
|
else
|
|
tag="${GITHUB_REF_NAME}"
|
|
sha="${GITHUB_SHA}"
|
|
source_ref="${GITHUB_REF_NAME}"
|
|
fi
|
|
|
|
if [ -z "${sha}" ]; then
|
|
echo "Unable to resolve release source for ${tag}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
|
|
echo "source_ref=${source_ref}" >> "$GITHUB_OUTPUT"
|
|
echo "sha=${sha}" >> "$GITHUB_OUTPUT"
|
|
|
|
build:
|
|
needs: [parity, resolve]
|
|
# `parity` is gated to tag-push events. On manual `workflow_dispatch`,
|
|
# parity is skipped, so let `build` proceed when parity either succeeded
|
|
# or was skipped — but never when it actually failed or the run was
|
|
# cancelled. Operators using dispatch are expected to have already run
|
|
# the same gates locally / via ci.yml on `main`.
|
|
if: ${{ !cancelled() && (needs.parity.result == 'success' || needs.parity.result == 'skipped') }}
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
# --- codewhale (cli dispatcher, canonical) ---
|
|
- os: ubuntu-latest
|
|
target: x86_64-unknown-linux-gnu
|
|
binary: codewhale
|
|
artifact_name: codewhale-linux-x64
|
|
- os: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-gnu
|
|
binary: codewhale
|
|
artifact_name: codewhale-linux-arm64
|
|
- os: macos-latest
|
|
target: x86_64-apple-darwin
|
|
binary: codewhale
|
|
artifact_name: codewhale-macos-x64
|
|
- os: macos-latest
|
|
target: aarch64-apple-darwin
|
|
binary: codewhale
|
|
artifact_name: codewhale-macos-arm64
|
|
- os: windows-latest
|
|
target: x86_64-pc-windows-msvc
|
|
binary: codewhale.exe
|
|
artifact_name: codewhale-windows-x64.exe
|
|
# --- codewhale-tui (TUI runtime, canonical) ---
|
|
- os: ubuntu-latest
|
|
target: x86_64-unknown-linux-gnu
|
|
binary: codewhale-tui
|
|
artifact_name: codewhale-tui-linux-x64
|
|
- os: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-gnu
|
|
binary: codewhale-tui
|
|
artifact_name: codewhale-tui-linux-arm64
|
|
- os: macos-latest
|
|
target: x86_64-apple-darwin
|
|
binary: codewhale-tui
|
|
artifact_name: codewhale-tui-macos-x64
|
|
- os: macos-latest
|
|
target: aarch64-apple-darwin
|
|
binary: codewhale-tui
|
|
artifact_name: codewhale-tui-macos-arm64
|
|
- os: windows-latest
|
|
target: x86_64-pc-windows-msvc
|
|
binary: codewhale-tui.exe
|
|
artifact_name: codewhale-tui-windows-x64.exe
|
|
# --- deepseek (legacy dispatcher shim; removed in v0.9.0) ---
|
|
- os: ubuntu-latest
|
|
target: x86_64-unknown-linux-gnu
|
|
binary: deepseek
|
|
artifact_name: deepseek-linux-x64
|
|
- os: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-gnu
|
|
binary: deepseek
|
|
artifact_name: deepseek-linux-arm64
|
|
- os: macos-latest
|
|
target: x86_64-apple-darwin
|
|
binary: deepseek
|
|
artifact_name: deepseek-macos-x64
|
|
- os: macos-latest
|
|
target: aarch64-apple-darwin
|
|
binary: deepseek
|
|
artifact_name: deepseek-macos-arm64
|
|
- os: windows-latest
|
|
target: x86_64-pc-windows-msvc
|
|
binary: deepseek.exe
|
|
artifact_name: deepseek-windows-x64.exe
|
|
# --- deepseek-tui (legacy TUI shim; removed in v0.9.0) ---
|
|
- os: ubuntu-latest
|
|
target: x86_64-unknown-linux-gnu
|
|
binary: deepseek-tui
|
|
artifact_name: deepseek-tui-linux-x64
|
|
- os: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-gnu
|
|
binary: deepseek-tui
|
|
artifact_name: deepseek-tui-linux-arm64
|
|
- os: macos-latest
|
|
target: x86_64-apple-darwin
|
|
binary: deepseek-tui
|
|
artifact_name: deepseek-tui-macos-x64
|
|
- os: macos-latest
|
|
target: aarch64-apple-darwin
|
|
binary: deepseek-tui
|
|
artifact_name: deepseek-tui-macos-arm64
|
|
- os: windows-latest
|
|
target: x86_64-pc-windows-msvc
|
|
binary: deepseek-tui.exe
|
|
artifact_name: deepseek-tui-windows-x64.exe
|
|
runs-on: ${{ matrix.os }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ needs.resolve.outputs.source_ref }}
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: ${{ matrix.target }}
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
cache-bin: false
|
|
- name: Install Linux system dependencies
|
|
if: runner.os == 'Linux'
|
|
run: |
|
|
for i in 1 2 3 4 5; do
|
|
sudo apt-get update && break
|
|
echo "apt-get update failed (attempt $i); retrying in 15s"
|
|
sleep 15
|
|
done
|
|
sudo apt-get install -y libdbus-1-dev pkg-config
|
|
- name: Build
|
|
shell: bash
|
|
env:
|
|
DEEPSEEK_BUILD_SHA: ${{ needs.resolve.outputs.sha }}
|
|
run: cargo build --release --locked --target ${{ matrix.target }}
|
|
- name: Rename binary
|
|
shell: bash
|
|
run: |
|
|
BIN_PATH="target/${{ matrix.target }}/release/${{ matrix.binary }}"
|
|
if [ ! -f "${BIN_PATH}" ]; then
|
|
echo "Binary not at ${BIN_PATH}; searching target/ for ${{ matrix.binary }}:"
|
|
find target -name "${{ matrix.binary }}" -type f
|
|
exit 1
|
|
fi
|
|
cp "${BIN_PATH}" "${{ matrix.artifact_name }}"
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: ${{ matrix.artifact_name }}
|
|
path: ${{ matrix.artifact_name }}
|
|
docker:
|
|
needs: [build, resolve]
|
|
if: ${{ !cancelled() && needs.build.result == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
steps:
|
|
- name: Checkout release source
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ needs.resolve.outputs.source_ref }}
|
|
path: source
|
|
- name: Checkout release infrastructure
|
|
uses: actions/checkout@v4
|
|
with:
|
|
path: infra
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.repository_owner }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
- name: Normalize image name
|
|
id: image
|
|
shell: bash
|
|
run: echo "name=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
|
|
- name: Extract metadata
|
|
id: meta
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: |
|
|
${{ steps.image.outputs.name }}
|
|
tags: |
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=semver,pattern=v{{major}}
|
|
type=ref,event=tag
|
|
type=semver,pattern={{version}},value=${{ needs.resolve.outputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }}
|
|
type=semver,pattern={{major}}.{{minor}},value=${{ needs.resolve.outputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }}
|
|
type=semver,pattern=v{{major}},value=${{ needs.resolve.outputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }}
|
|
type=raw,value=${{ inputs.version }},enable=${{ github.event_name == 'workflow_dispatch' }}
|
|
type=raw,value=v${{ inputs.version }},enable=${{ github.event_name == 'workflow_dispatch' }}
|
|
type=raw,value=latest
|
|
- name: Build and push
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
context: source
|
|
file: infra/Dockerfile
|
|
platforms: linux/amd64,linux/arm64
|
|
push: true
|
|
build-args: |
|
|
DEEPSEEK_BUILD_SHA=${{ needs.resolve.outputs.sha }}
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
|
|
release:
|
|
needs: [build, docker, resolve]
|
|
if: ${{ !cancelled() && needs.build.result == 'success' && needs.docker.result == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
path: artifacts
|
|
# Match both the canonical `codewhale*` artifacts and the legacy
|
|
# `deepseek*` shim artifacts that ship for the transition release.
|
|
pattern: '*'
|
|
- name: List artifacts
|
|
run: find artifacts -type f
|
|
- name: Generate checksum manifest
|
|
shell: bash
|
|
run: |
|
|
mkdir -p artifacts/checksums
|
|
# Canonical manifest used by codewhale's `codewhale update` flow.
|
|
manifest="artifacts/checksums/codewhale-artifacts-sha256.txt"
|
|
: > "${manifest}"
|
|
while IFS= read -r -d '' file; do
|
|
hash="$(sha256sum "${file}" | awk '{print $1}')"
|
|
base="$(basename "${file}")"
|
|
printf '%s %s\n' "${hash}" "${base}" >> "${manifest}"
|
|
done < <(find artifacts -type f ! -path 'artifacts/checksums/*' -print0 | sort -z)
|
|
# Legacy alias manifest so v0.8.40 `deepseek update` clients can
|
|
# still find a manifest by their hardcoded name. Same content; will
|
|
# be removed once the legacy shim binaries are retired in v0.9.0.
|
|
cp "${manifest}" "artifacts/checksums/deepseek-artifacts-sha256.txt"
|
|
cat "${manifest}"
|
|
- uses: softprops/action-gh-release@v1
|
|
with:
|
|
tag_name: ${{ needs.resolve.outputs.tag }}
|
|
files: artifacts/*/*
|
|
prerelease: false
|
|
body: |
|
|
> This release renames the project to **codewhale**. The legacy
|
|
> `deepseek` and `deepseek-tui` binaries continue to ship as
|
|
> deprecation shims for one release cycle; they print a one-line
|
|
> warning and forward to `codewhale` / `codewhale-tui`. They will
|
|
> be removed in v0.9.0. See `docs/REBRAND.md` for the full
|
|
> migration story.
|
|
|
|
## Install
|
|
|
|
### Recommended — npm (one command, both binaries)
|
|
|
|
```bash
|
|
npm install -g codewhale
|
|
```
|
|
|
|
The wrapper downloads both binaries from this Release and places them in the same directory.
|
|
|
|
### Docker / GHCR
|
|
|
|
```bash
|
|
docker run --rm -it \
|
|
-e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
|
|
-v ~/.deepseek:/home/deepseek/.deepseek \
|
|
ghcr.io/hmbown/deepseek-tui:${{ needs.resolve.outputs.tag }}
|
|
```
|
|
|
|
The image ships the `codewhale` dispatcher and `codewhale-tui` runtime (plus the legacy `deepseek` / `deepseek-tui` shims during the transition). The `latest` tag is also updated on release.
|
|
|
|
### Cargo (Linux / macOS)
|
|
|
|
```bash
|
|
cargo install codewhale-cli codewhale-tui --locked
|
|
```
|
|
|
|
Both crates are required — `codewhale-cli` produces the `codewhale` dispatcher and `codewhale-tui` produces the interactive runtime that the dispatcher delegates to. Installing only one binary will fail at runtime with a `MISSING_COMPANION_BINARY` error.
|
|
|
|
### Manual download
|
|
|
|
**Both** binaries below must be downloaded for your platform and dropped into the same directory (e.g. `~/.local/bin/`):
|
|
|
|
| Platform | Dispatcher | TUI runtime |
|
|
|---|---|---|
|
|
| Linux x64 | `codewhale-linux-x64` | `codewhale-tui-linux-x64` |
|
|
| Linux ARM64 | `codewhale-linux-arm64` | `codewhale-tui-linux-arm64` |
|
|
| macOS x64 | `codewhale-macos-x64` | `codewhale-tui-macos-x64` |
|
|
| macOS ARM | `codewhale-macos-arm64` | `codewhale-tui-macos-arm64` |
|
|
| Windows x64 | `codewhale-windows-x64.exe` | `codewhale-tui-windows-x64.exe` |
|
|
|
|
Then `chmod +x` both (Unix) and run `./codewhale`.
|
|
|
|
Legacy `deepseek-*` and `deepseek-tui-*` assets are also attached for one release cycle so that existing `deepseek update` invocations on v0.8.40 keep working; they install the deprecation shims, which forward to the canonical binaries.
|
|
|
|
### Verify (recommended)
|
|
|
|
Download `codewhale-artifacts-sha256.txt` from this Release and verify:
|
|
|
|
```bash
|
|
# Linux
|
|
sha256sum -c codewhale-artifacts-sha256.txt
|
|
|
|
# macOS
|
|
shasum -a 256 -c codewhale-artifacts-sha256.txt
|
|
```
|
|
|
|
The legacy `deepseek-artifacts-sha256.txt` is also attached for backward compatibility and contains the same hashes.
|
|
|
|
## Changelog
|
|
|
|
See [CHANGELOG.md](https://github.com/Hmbown/DeepSeek-TUI/blob/main/CHANGELOG.md) for the full notes for this release.
|
|
|
|
homebrew:
|
|
needs: [release, resolve]
|
|
if: ${{ !cancelled() && needs.release.result == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- name: Check Homebrew tap token
|
|
id: homebrew-token
|
|
env:
|
|
TOKEN: ${{ secrets.HOMEBREW_TAP_PAT || secrets.RELEASE_TAG_PAT }}
|
|
run: |
|
|
if [ -z "${TOKEN:-}" ]; then
|
|
echo "No Homebrew tap token configured; skipping tap update."
|
|
echo "available=false" >> "${GITHUB_OUTPUT}"
|
|
else
|
|
echo "available=true" >> "${GITHUB_OUTPUT}"
|
|
fi
|
|
# Checkout main (not the tag) so the release-infra script is always
|
|
# available, even for tags created before this workflow was added.
|
|
- uses: actions/checkout@v4
|
|
if: steps.homebrew-token.outputs.available == 'true'
|
|
with:
|
|
ref: main
|
|
- name: Download checksum manifest
|
|
if: steps.homebrew-token.outputs.available == 'true'
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh release download ${{ needs.resolve.outputs.tag }} \
|
|
--repo ${{ github.repository }} \
|
|
--pattern 'deepseek-artifacts-sha256.txt' \
|
|
--dir /tmp
|
|
- name: Update Homebrew tap
|
|
if: steps.homebrew-token.outputs.available == 'true'
|
|
env:
|
|
TAG: ${{ needs.resolve.outputs.tag }}
|
|
MANIFEST: /tmp/deepseek-artifacts-sha256.txt
|
|
TAP_REPO: Hmbown/homebrew-deepseek-tui
|
|
TOKEN: ${{ secrets.HOMEBREW_TAP_PAT || secrets.RELEASE_TAG_PAT }}
|
|
run: bash .github/scripts/update-homebrew-tap.sh
|
|
|
|
# npm publish is intentionally not automated. The npm account requires 2FA OTP
|
|
# on every publish, and a granular automation token that bypasses 2FA has not
|
|
# been provisioned. Release the npm wrapper manually from a developer machine
|
|
# after the GitHub Release has been created — see CLAUDE.md "Releases" for the
|
|
# exact commands.
|