1763261503
* docs: v0.8.46 CHANGELOG — platform archives, palette, sub-agents, sandbox, web install, search fixes Closes #2188 * feat(v0.8.46): quick fixes — palette, model picker Esc, sub-agent sidebar, shell chip, model name casing, CVE bump (#2212) * fix: bump qs to >=6.15.2 for CVE-2026-8723 Add qs override in feishu-bridge package.json to force transitive dependency resolution to >=6.15.2, addressing CVE-2026-8723. Refs: #2198 * fix: Esc in model picker applies last-highlighted choice Previously Esc reverted to the initial model when the user hadn't moved the selection. Now Esc always applies the currently highlighted model and thinking-effort tier, making Esc consistent with Enter. Also updates the picker footer hint from 'Esc cancel' to 'Esc apply'. Refs: #2196 * feat: show '⏳ shell running' chip in TUI footer Adds a footer_shell_chip function that displays a '⏳ shell running' status chip in the footer's right cluster whenever a foreground shell command is active via exec_shell. The chip is always visible regardless of user-configured status items. Refs: #2194 * feat: auto-collapse finished sub-agents in sidebar When a sub-agent completes (status = 'done'), its detail lines (id, steps, duration, progress) are now hidden in the sidebar agents panel. Only the summary label line is shown, keeping the sidebar compact. Running agents still show full detail. Refs: #2195 * feat: refresh Whale dark palette for better contrast Improve contrast and layer separation in the Whale dark theme: - Deepen base background for more depth (10,17,32) - Lighten panel (22,34,56) for clearer distinction from bg - Lighten elevated surface (36,52,78) for better elevation - Lighten selection (48,68,100) for clearer selected state - Boost text hint (138,150,174) and dim (118,130,156) readability - Brighter border (52,88,145) for better edge definition - Update tool surface colors for consistency Refs: #2197 * fix: preserve model name casing in normalize_model_name_for_provider When the user enters a model name like 'DeepSeek-V4-Flash', the normalizer was lowercasing it to 'deepseek-v4-flash' via the canonical_official_deepseek_model_id function. Now the normalizer preserves the caller's casing when the input already matches a known model id case-insensitively. Compact aliases like 'deepseek-v4pro' are still rewritten to 'deepseek-v4-pro'. Refs: #2109 * feat(web): install download tile with arch detection, SHA256, China mirrors + companion binary fix (#2213) * fix(web): download both codewhale and codewhale-tui binaries in install snippets The SNIPPETS map only fetched one binary per platform, causing the dispatcher to fail with MISSING_COMPANION_BINARY. Every arch now downloads both codewhale AND codewhale-tui side-by-side. - macOS/Linux: added second curl + combined chmod/xattr/mv for tui - Windows: added second Invoke-WebRequest for codewhale-tui.exe - VERIFY: PowerShell now hashes both binaries; Unix --ignore-missing covers all present binaries in a single sha256sum pass * feat(web): add install download tile with arch detection, SHA256, and China mirrors (#2192) * feat(sandbox/linux): process hardening — PR_SET_DUMPABLE, NO_NEW_PRIVS, RLIMIT_CORE (#2214) * feat(sandbox/linux): add process hardening module — PR_SET_DUMPABLE, NO_NEW_PRIVS, RLIMIT_CORE (#2183) * feat(sandbox/linux): seccomp filter + bwrap passthrough - seccomp: BPF filter whitelisting safe syscalls, denying ptrace/mount/kexec and other dangerous syscalls. Uses raw BPF instructions via libc prctl to avoid external dependencies (#2182). - bwrap: optional bubblewrap passthrough when /usr/bin/bwrap is present and [sandbox] prefer_bwrap=true in config. Creates read-only rootfs with write access limited to the working directory (#2184). - landlock detect_denial extended to recognize seccomp SIGSYS/"Bad system call" patterns alongside existing Landlock EACCES/EPERM detection. - SandboxManager gains prefer_bwrap field; set_prefer_bwrap on ShellManager. - EngineConfig gains prefer_bwrap field, wired through main/ui/runtime_threads. - Diagnostics now reports bwrap_available and cgroup_version. - config.example.toml documents the prefer_bwrap key. Pre-existing clippy fixes picked up in the same build: - collapsible_if in ui.rs version-check - cmp_owned in goal.rs test - consecutive str::replace in normalize_auth_mode Closes #2182, closes #2184 * docs: add cross-links to issue and PR templates in CONTRIBUTING.md (#2215) - Link .github/ISSUE_TEMPLATE/bug_report.md and feature_request.md from the Reporting Issues section - Link .github/PULL_REQUEST_TEMPLATE.md from the Pull Request Guidelines section * feat(release): bundle platform archives with install scripts (#2216) - Add bundle job to release workflow that creates per-platform archives (tar.gz for Linux/macOS, .zip for Windows) containing both codewhale and codewhale-tui binaries plus install scripts - Create install.bat (Windows) — copies binaries to %USERPROFILE%\bin - Create install.sh (Unix) — copies binaries to ~/.local/bin - Windows gets a portable .zip variant without install script - Release notes updated to promote archives as primary download method - Individual binaries retained for npm wrapper and scripting Closes #2193 * fix(web_search): fall back to DuckDuckGo when Bing returns zero results (#2130) When the configured search provider is Bing and the query returns zero results (common for technical/compound queries), fall through to the DuckDuckGo path instead of reporting empty. A provenance message is surfaced: "Bing returned no results; used DuckDuckGo fallback". Also adds Security and Code of Conduct cross-links to CONTRIBUTING.md per the sub-agent renovation (#2203). * docs: SANDBOX.md threat model + RFCs for persistence and MCP + SandboxExecutor trait - docs/SANDBOX.md: complete threat model describing each platform's sandbox (Seatbelt, Landlock, seccomp, process hardening, bwrap, Windows v1). Covers defense-in-depth layering, config keys, denial detection, limitations. - docs/rfcs/2189-persistence-sqlite.md: RFC for SQLite migration (drafted by sub-agent) - docs/rfcs/2190-mcp-modularization.md: RFC for MCP crate split into protocol/client/server with OAuth support - crates/tui/src/sandbox/policy.rs: SandboxExecutor trait definition and SafetyLevel→SandboxPolicyBehavior mapping function with tests Closes #2180, closes #2186, closes #2189, closes #2190 * feat: sandbox parity tests + remove sub-agent 100-turn cap - Add sandbox parity tests covering platform detection, denial patterns, bwrap preference, and policy consistency across modes (#2187) - Remove arbitrary 100-turn sub-agent cap: DEFAULT_MAX_STEPS changed from 100 to u32::MAX. Sub-agents now run until they produce a final text response, are cancelled by the parent, or hit a configured explicit budget (#2034) Closes #2187, closes #2034
564 lines
22 KiB
YAML
564 lines
22 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 }}
|
|
|
|
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: 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` |
|
|
| 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.
|