c0dd43993c
Adds riscv64 to build pipelines so CodeWhale ships prebuilt binaries and npm wrappers for 64-bit RISC-V Linux (glibc) systems. Changes: **CI / build** - release.yml: +2 build matrix entries (codewhale + codewhale-tui for riscv64gc-unknown-linux-gnu), cross-compilation toolchain step using a dedicated DEB822-format apt source for ports.ubuntu.com, bundle step, and release-notes table row. - nightly.yml: +2 matrix entries, matching cross-compilation setup. - resolve job: handle workflow_dispatch when the target tag does not yet exist (fall back to HEAD SHA). **Packaging** - npm/codewhale/scripts/artifacts.js: add riscv64 to ASSET_MATRIX under linux so npm install -g codewhale resolves on RISC-V. **Docs** - docs/INSTALL.md: add riscv64 row to supported platforms table; replace with clearer 'other architectures' wording. Build strategy: cross-compile from ubuntu-latest (x86_64) using gcc-riscv64-linux-gnu. The dbus runtime dependency (from the keyring crate's secret-service backend) is satisfied via ports.ubuntu.com. PKG_CONFIG_ALLOW_CROSS and a cross-target libdir are set so the keyring crate finds dbus-1 during cross-compilation. Docker support for linux/riscv64 is intentionally not added here: GitHub Actions does not yet provide the infrastructure to build or emulate riscv64 containers. The Dockerfile changes will follow when the hosted CI surface supports it.
607 lines
24 KiB
YAML
607 lines
24 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 }}"
|
|
if git rev-parse "refs/tags/${tag}" >/dev/null 2>&1; then
|
|
sha="$(git rev-list -n 1 "${tag}")"
|
|
source_ref="${tag}"
|
|
else
|
|
# Tag doesn't exist yet — build from HEAD
|
|
sha="${GITHUB_SHA}"
|
|
source_ref="${GITHUB_REF_NAME}"
|
|
echo "Tag ${tag} not found; building from ${source_ref} @ ${sha}"
|
|
fi
|
|
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: ubuntu-latest
|
|
target: riscv64gc-unknown-linux-gnu
|
|
binary: codewhale
|
|
artifact_name: codewhale-linux-riscv64
|
|
- 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: ubuntu-latest
|
|
target: riscv64gc-unknown-linux-gnu
|
|
binary: codewhale-tui
|
|
artifact_name: codewhale-tui-linux-riscv64
|
|
- 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: Install RISC-V cross-compilation toolchain
|
|
if: matrix.target == 'riscv64gc-unknown-linux-gnu'
|
|
run: |
|
|
# Install cross-compiler (available in standard repos)
|
|
sudo apt-get update
|
|
sudo apt-get install -y gcc-riscv64-linux-gnu libc6-dev-riscv64-cross
|
|
|
|
# Add Ubuntu ports for riscv64 packages
|
|
. /etc/os-release
|
|
sudo tee /etc/apt/sources.list.d/riscv64.sources <<SRC
|
|
Types: deb
|
|
URIs: http://ports.ubuntu.com/
|
|
Suites: ${UBUNTU_CODENAME} ${UBUNTU_CODENAME}-updates
|
|
Components: main universe
|
|
Architectures: riscv64
|
|
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
|
|
SRC
|
|
sudo dpkg --add-architecture riscv64
|
|
sudo apt-get update -o Dir::Etc::sourcelist=/etc/apt/sources.list.d/riscv64.sources -o Dir::Etc::sourceparts=- -o APT::Get::List-Cleanup=0
|
|
sudo apt-get install -y libdbus-1-dev:riscv64
|
|
- name: Build
|
|
shell: bash
|
|
env:
|
|
DEEPSEEK_BUILD_SHA: ${{ needs.resolve.outputs.sha }}
|
|
CC_riscv64gc_unknown_linux_gnu: riscv64-linux-gnu-gcc
|
|
CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER: riscv64-linux-gnu-gcc
|
|
PKG_CONFIG_ALLOW_CROSS: 1
|
|
PKG_CONFIG_LIBDIR_riscv64gc_unknown_linux_gnu: /usr/lib/riscv64-linux-gnu/pkgconfig
|
|
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 }}
|
|
|
|
bundle:
|
|
needs: [build, resolve]
|
|
if: ${{ !cancelled() && needs.build.result == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ needs.resolve.outputs.source_ref }}
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
path: artifacts
|
|
pattern: 'codewhale*'
|
|
- name: Create platform archives
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p bundles/checksums
|
|
MANIFEST="bundles/checksums/codewhale-bundles-sha256.txt"
|
|
: > "$MANIFEST"
|
|
|
|
bundle() {
|
|
local platform="$1" # linux-x64, linux-arm64, macos-x64, macos-arm64, windows-x64
|
|
local cli_src="$2" # artifact name for codewhale binary
|
|
local tui_src="$3" # artifact name for codewhale-tui binary
|
|
local ext="$4" # tar.gz or zip
|
|
local variant="$5" # '' (standard) or 'portable' (Windows only, no install script)
|
|
shift 5
|
|
|
|
local dir="bundles/codewhale-${platform}${variant:+-}${variant}"
|
|
mkdir -p "$dir"
|
|
|
|
# Copy binaries, stripping platform suffixes
|
|
local cli_dst="codewhale"
|
|
local tui_dst="codewhale-tui"
|
|
if [[ "$platform" == windows-* ]]; then
|
|
cli_dst="codewhale.exe"
|
|
tui_dst="codewhale-tui.exe"
|
|
fi
|
|
cp "artifacts/${cli_src}/${cli_src}" "$dir/${cli_dst}"
|
|
cp "artifacts/${tui_src}/${tui_src}" "$dir/${tui_dst}"
|
|
|
|
# Add install script (standard variant only)
|
|
if [[ "$variant" != "portable" ]]; then
|
|
if [[ "$platform" == windows-* ]]; then
|
|
cp scripts/release/install.bat "$dir/"
|
|
# Convert line endings to CRLF for Windows
|
|
sed -i 's/$/\r/' "$dir/install.bat" 2>/dev/null || true
|
|
else
|
|
cp scripts/release/install.sh "$dir/"
|
|
chmod +x "$dir/install.sh"
|
|
fi
|
|
fi
|
|
|
|
if [[ "$ext" == "zip" ]]; then
|
|
(cd bundles && zip -r "codewhale-${platform}${variant:+-}${variant}.zip" "codewhale-${platform}${variant:+-}${variant}/")
|
|
else
|
|
tar -czf "bundles/codewhale-${platform}${variant:+-}${variant}.tar.gz" -C bundles "codewhale-${platform}${variant:+-}${variant}/"
|
|
fi
|
|
|
|
local archive="codewhale-${platform}${variant:+-}${variant}.${ext}"
|
|
sha256sum "bundles/${archive}" | awk '{printf "%s %s\n", $1, $2}' >> "$MANIFEST"
|
|
echo " Created bundles/${archive}"
|
|
}
|
|
|
|
# Platform: linux-x64
|
|
bundle linux-x64 \
|
|
codewhale-linux-x64 codewhale-tui-linux-x64 tar.gz ""
|
|
|
|
# Platform: linux-arm64
|
|
bundle linux-arm64 \
|
|
codewhale-linux-arm64 codewhale-tui-linux-arm64 tar.gz ""
|
|
|
|
# Platform: linux-riscv64
|
|
bundle linux-riscv64 \
|
|
codewhale-linux-riscv64 codewhale-tui-linux-riscv64 tar.gz ""
|
|
|
|
# Platform: macos-x64
|
|
bundle macos-x64 \
|
|
codewhale-macos-x64 codewhale-tui-macos-x64 tar.gz ""
|
|
|
|
# Platform: macos-arm64
|
|
bundle macos-arm64 \
|
|
codewhale-macos-arm64 codewhale-tui-macos-arm64 tar.gz ""
|
|
|
|
# Platform: windows-x64 (standard + portable)
|
|
bundle windows-x64 \
|
|
codewhale-windows-x64.exe codewhale-tui-windows-x64.exe zip ""
|
|
bundle windows-x64 \
|
|
codewhale-windows-x64.exe codewhale-tui-windows-x64.exe zip "portable"
|
|
|
|
echo ""
|
|
echo "=== Archive checksums ==="
|
|
cat "$MANIFEST"
|
|
|
|
- name: Upload bundle artifacts
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: codewhale-bundles
|
|
path: bundles/*
|
|
if-no-files-found: error
|
|
|
|
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
|
|
env:
|
|
# The build record is useful in CI, but it is uploaded as a
|
|
# `.dockerbuild` artifact. The release job intentionally downloads
|
|
# all binary artifacts, so suppress the extra record artifact there.
|
|
DOCKER_BUILD_RECORD_UPLOAD: false
|
|
DOCKER_BUILD_SUMMARY: false
|
|
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, bundle, docker, resolve]
|
|
if: ${{ !cancelled() && needs.build.result == 'success' && needs.bundle.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/codewhale/.deepseek \
|
|
ghcr.io/hmbown/codewhale:${{ 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 — platform archives (recommended)
|
|
|
|
Each archive below contains **both** the `codewhale` dispatcher and `codewhale-tui` runtime, plus an install script:
|
|
|
|
| Platform | Archive | Install script |
|
|
|---|---|---|
|
|
| Linux x64 | `codewhale-linux-x64.tar.gz` | `install.sh` |
|
|
| Linux ARM64 | `codewhale-linux-arm64.tar.gz` | `install.sh` |
|
|
| Linux RISC-V | `codewhale-linux-riscv64.tar.gz` | `install.sh` |
|
|
| macOS x64 | `codewhale-macos-x64.tar.gz` | `install.sh` |
|
|
| macOS ARM | `codewhale-macos-arm64.tar.gz` | `install.sh` |
|
|
| Windows x64 | `codewhale-windows-x64.zip` | `install.bat` |
|
|
| Windows x64 (portable) | `codewhale-windows-x64-portable.zip` | — |
|
|
|
|
**Unix (Linux / macOS):**
|
|
```bash
|
|
tar xzf codewhale-<platform>.tar.gz
|
|
cd codewhale-<platform>
|
|
./install.sh
|
|
```
|
|
|
|
**Windows:**
|
|
- Extract `codewhale-windows-x64.zip`
|
|
- Run `install.bat` (copies to `%USERPROFILE%\bin`)
|
|
- Add `%USERPROFILE%\bin` to your PATH
|
|
|
|
The **portable** Windows archive skips the install script — extract and run from any directory.
|
|
|
|
Individual binaries are also attached below for scripting and the npm wrapper. Legacy `deepseek-*` and `deepseek-tui-*` assets ship 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 the checksum manifests from this Release and verify:
|
|
|
|
```bash
|
|
# Linux — archive bundles
|
|
sha256sum -c codewhale-bundles-sha256.txt
|
|
|
|
# Linux — individual binaries
|
|
sha256sum -c codewhale-artifacts-sha256.txt
|
|
|
|
# macOS
|
|
shasum -a 256 -c codewhale-bundles-sha256.txt
|
|
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 as the canonical manifest.
|
|
|
|
## Changelog
|
|
|
|
See [CHANGELOG.md](https://github.com/Hmbown/CodeWhale/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.
|