diff --git a/.github/workflows/build-dsh.yml b/.github/workflows/build-dsh.yml index 541555e..d2e6394 100644 --- a/.github/workflows/build-dsh.yml +++ b/.github/workflows/build-dsh.yml @@ -23,6 +23,8 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 @@ -32,22 +34,38 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + # resolve the current "latest" dsh version once, up front, so the build + # is pinned to a concrete version and the image can be tagged with it + # (otherwise every rebuild silently floats to whatever npm has that day). + - id: dsh_version + run: | + set -euo pipefail + version=$(curl -fsSL https://registry.npmjs.org/@deepseek-ai/dsh | jq -r '."dist-tags".latest') + if [ -z "$version" ] || [ "$version" = "null" ]; then + echo "failed to resolve @deepseek-ai/dsh latest version" >&2 + exit 1 + fi + echo "version=$version" | tee -a "$GITHUB_OUTPUT" + - id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=raw,value=latest,enable={{is_default_branch}} - type=sha,prefix=sha- + type=raw,value=${{ steps.dsh_version.outputs.version }} - uses: docker/build-push-action@v6 with: context: deepseek-harness + platforms: linux/amd64,linux/arm64 push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} # npm mirror default; set NPM_REGISTRY var in repo settings to override + # DSH_VERSION pinned to the version resolved above for reproducibility build-args: | NPM_REGISTRY=${{ vars.NPM_REGISTRY || 'https://registry.npmmirror.com' }} + DSH_VERSION=${{ steps.dsh_version.outputs.version }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/deepseek-harness/Dockerfile b/deepseek-harness/Dockerfile index 5eaaedb..ae24c5c 100644 --- a/deepseek-harness/Dockerfile +++ b/deepseek-harness/Dockerfile @@ -4,11 +4,32 @@ FROM node:22-bookworm-slim ARG NPM_REGISTRY=https://registry.npmmirror.com ENV NPM_REGISTRY=${NPM_REGISTRY} +# dev tools for plugin development: git (clone/pull plugin repos), vim (quick +# in-container edits), build-essential (native module compiles via pnpm). +# Small footprint, kept in the same image so one image serves both plain +# usage and plugin dev — no need to maintain a second image. +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends git vim build-essential ca-certificates; \ + rm -rf /var/lib/apt/lists/* + +# pnpm: dsh forwards `dsh plugin ...` to pnpm inside the profile dir RUN --mount=type=cache,target=/root/.npm \ set -eux; \ printf 'registry=%s\n' "$NPM_REGISTRY" > /usr/local/etc/npmrc; \ npm config set registry "$NPM_REGISTRY"; \ - npm install -g @deepseek-ai/dsh + corepack enable; \ + corepack prepare pnpm@latest --activate; \ + pnpm config set registry "$NPM_REGISTRY" --global + +# dsh version to install: pin to a resolved version for reproducible builds; +# defaults to "latest" for ad-hoc local builds (CI resolves and pins this). +ARG DSH_VERSION=latest + +RUN --mount=type=cache,target=/root/.npm \ + npm install -g "@deepseek-ai/dsh@${DSH_VERSION}" # dsh home: mount here to persist config/plugins/sessions ENV DSH_HOME=/data/dsh diff --git a/deepseek-harness/README.md b/deepseek-harness/README.md index c30cd3f..03b36e8 100644 --- a/deepseek-harness/README.md +++ b/deepseek-harness/README.md @@ -6,7 +6,7 @@ | 文件 | 作用 | |---|---| -| `Dockerfile` | 基于 `node:22-bookworm-slim`,全局安装 `@deepseek-ai/dsh` | +| `Dockerfile` | 基于 `node:22-bookworm-slim`,全局安装 `@deepseek-ai/dsh`;同时带 git/vim/build-essential/pnpm,一个镜像兼顾日常使用和插件开发 | | `run.sh` | 构建/启动/停止/看日志/进容器的辅助脚本 | | `data/dsh/` | 挂载的配置目录(自动创建),容器内路径 `/data/dsh` | @@ -40,6 +40,20 @@ CONFIG_DIR=/srv/dsh ./run.sh run 删除容器后重建,数据仍在。 +## 插件开发 + +镜像里已经装好 git / vim / build-essential / pnpm(`dsh plugin` 子命令会转发给 pnpm),不需要单独一个开发镜像。挂载本地插件源码目录: + +```bash +PLUGIN_DIR=~/code/my-dsh-plugin ./run.sh run +``` + +容器内路径固定为 `/data/dsh/plugin-src`。进容器操作(装依赖、`pnpm link`、跑 `dsh plugin --profile add ...` 等): + +```bash +./run.sh shell +``` + ## 常用命令 ```bash @@ -50,7 +64,7 @@ CONFIG_DIR=/srv/dsh ./run.sh run ./run.sh shell # 进入容器 shell ``` -可调环境变量:`IMAGE_NAME` `IMAGE_TAG` `CONTAINER_NAME` `HOST_PORT` `NPM_REGISTRY` `CONFIG_DIR`。 +可调环境变量:`IMAGE_NAME` `IMAGE_TAG` `CONTAINER_NAME` `HOST_PORT` `NPM_REGISTRY` `CONFIG_DIR` `PLUGIN_DIR`。 ## 手动 docker 命令 diff --git a/deepseek-harness/run.sh b/deepseek-harness/run.sh index bf81cb0..dfb6fdd 100755 --- a/deepseek-harness/run.sh +++ b/deepseek-harness/run.sh @@ -11,9 +11,20 @@ CONTAINER_NAME="${CONTAINER_NAME:-dsh}" NPM_REGISTRY="${NPM_REGISTRY:-https://registry.npmmirror.com}" # config dir mounted into the container (persisted config/plugins/sessions) CONFIG_DIR="${CONFIG_DIR:-$PWD/data/dsh}" +# optional: local plugin source dir for plugin development, mounted into the +# container so you can `dsh plugin --profile add /data/dsh/plugin-src/` +# or pnpm-link it from inside the container (see: run.sh shell). Unset by default. +PLUGIN_DIR="${PLUGIN_DIR:-}" mkdir -p "$CONFIG_DIR" +# extra -v args, appended only when PLUGIN_DIR is set +MOUNT_ARGS=() +if [[ -n "$PLUGIN_DIR" ]]; then + mkdir -p "$PLUGIN_DIR" + MOUNT_ARGS+=(-v "$PLUGIN_DIR:/data/dsh/plugin-src") +fi + case "${1:-run}" in build) docker build --build-arg NPM_REGISTRY="$NPM_REGISTRY" -t "$IMAGE_NAME:$IMAGE_TAG" . @@ -25,11 +36,13 @@ case "${1:-run}" in docker run -d --name "$CONTAINER_NAME" \ --network host \ -v "$CONFIG_DIR":/data/dsh \ + "${MOUNT_ARGS[@]}" \ -e NPM_REGISTRY="$NPM_REGISTRY" \ --restart unless-stopped \ "$IMAGE_NAME:$IMAGE_TAG" echo "dsh web UI: http://127.0.0.1:3080 (host network mode)" echo "config dir: $CONFIG_DIR" + [[ -n "$PLUGIN_DIR" ]] && echo "plugin source dir: $PLUGIN_DIR -> /data/dsh/plugin-src" ;; stop) docker rm -f "$CONTAINER_NAME" @@ -42,7 +55,7 @@ case "${1:-run}" in ;; *) echo "Usage: $0 {build|run|stop|logs|shell}" - echo "Env: IMAGE_NAME IMAGE_TAG CONTAINER_NAME NPM_REGISTRY CONFIG_DIR" + echo "Env: IMAGE_NAME IMAGE_TAG CONTAINER_NAME NPM_REGISTRY CONFIG_DIR PLUGIN_DIR" exit 1 ;; esac