脚本和 CI 里怎么跑 dsh plugin 命令?DeepSeek Harness 非交互、退出码与幂等

配置与使用发布于 2026-10-01作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH pluginCI自动化脚本dsh plugin add
把 dsh plugin 命令放进脚本与 CI:哪些命令适合非交互执行、哪些会挂在确认提示上,怎么用退出码而不是 grep 输出判断成败,已装再装怎么做幂等,以及在容器里跑的最小可用写法与前置条件。

把 dsh plugin 命令放进脚本与 CI,只需要抓住三点:用「跑完就退出」的命令、用退出码而不是输出判断成败、把需要确认的环节提前用参数或配置文件给定答案。 dsh plugin --profile <名字> add / remove / list 与 dsh --profile <名字> --dump-config 都属于一次性退出的命令;CLI 对无效命令、参数错用、配置错误与启动失败一律返回非零,所以脚本里直接用 set -e 就能兜住绝大多数失败(来源)。

哪些 dsh plugin 命令适合进脚本,哪些会挂住

分界线是「跑完是否退出」:一次性退出的命令能进流水线,常驻进程不能(来源)。 按这个标准过一遍常用命令:

命令能否进脚本说明
dsh plugin --profile <名字> add <目标>可以装完即退出,退出码表达结果
dsh plugin --profile <名字> remove <包名>可以卸完即退出,可幂等重跑
dsh plugin --profile <名字> list可以一次性输出,适合做状态断言
dsh --profile <名字> --dump-config可以不启动应用,打印组合后的配置树
dsh web不可以常驻占住终端,属于服务进程

「挂住」还有另一种形态:命令没挂,但在等一个确认。 pnpm 在两种情况下会停下来要答案——删除 node_modules 前的 purge 确认,以及 git 依赖的构建脚本授权。在本地你会顺手敲个回车,在 CI 里就是等到超时。这两种确认都必须改由参数或配置文件提前给定答案,见下文的非交互写法。

用退出码判断 dsh plugin 成败,而不是 grep 输出

dsh plugin 命令的成败契约是退出码,不是输出文案:输出会随版本与语言环境变化,退出码不会(来源)。 写法上直接让失败中止:

bash
set -euo pipefail

dsh plugin --profile web add "<包名>"
dsh --profile web --dump-config | grep -q "# =="

第二行才是真正的验收:add 成功只说明依赖装上了,--dump-config 里出现 # == <包名> 才说明配置层进了组合树(来源)。把这两步分开,日志里就能一眼区分「装不上」和「装了没生效」——后者多半是包没声明 dsh.bundle。

需要分支处理时用 if 而不是解析文本:

bash
if dsh plugin --profile web list | grep -q "<包名>"; then
  echo "already installed"
else
  dsh plugin --profile web add "<包名>"
fi

别把 grep "error" 当成失败判据:pnpm 的警告文案里也可能出现这个词,而真正的失败已经被非零退出码表达了。

幂等重跑:dsh plugin 已装再装怎么办

「按期望状态重跑」比「按动作重跑」更可靠:先读当前状态,再决定要不要动手(来源)。 两条实践:

  1. 把状态查询作为脚本的第一步:
bash
dsh plugin --profile web list

它的输出由 profile 目录里的 pnpm 给出,能看到包名与版本。用它做判断,脚本逻辑就是显式的,不必依赖「重复 add 会怎样」这种隐含行为。

  1. 需要固定版本时,把版本写进目标。脚本里最忌讳写「最新」——今天跑通、明天可能因为上游发了新版而失败。把目标钉在具体版本或具体 commit 上,重跑结果才稳定:
bash
# npm 包:带版本
dsh plugin --profile web add "<包名>@<版本>"

# GitHub 源:锁 commit
dsh plugin --profile web add github:owner/repo#<sha>

先本地手动装一遍再写脚本:同一个包在 DSH Plugin Hub 里点安装就能验证「这个插件本身装得上、装完确实生效」,确认没问题之后再把等价命令搬进脚本。跳过这一步,最后很难分清是插件的问题还是脚本的问题。

在 CI 与容器里跑 dsh plugin 的最小可用写法

容器里的三个前置条件是:Node 版本够新、pnpm 在 PATH、$DSH_HOME 可写;满足之后一条命令就能跑(来源)。 最小可用片段:

bash
npm install -g pnpm

export CI=true
export DSH_HOME="${HOME}/.dsh"

dsh plugin --profile web add --config.confirm-modules-purge=false "<包名>"
dsh --profile web --dump-config | grep -q "# =="

要点逐条:

  1. CI=true 让 pnpm 走非交互分支,遇到本该弹确认的环节直接按非交互处理;
  2. --config.confirm-modules-purge=false 关掉删除 node_modules 前的确认,避免清缓存时卡住;
  3. pnpm 必须在 PATH 里——容器镜像里没装 pnpm 时,命令会以 pnpm failed in profile directory 这类形态失败,排查方法见《dsh plugin 报 pnpm failed in profile directory 怎么修?》;
  4. registry 要在受限网络里指向内网源,否则构建阶段会卡在拉包上,做法见《内网、离线环境怎么装 dsh?》;
  5. git 依赖提前授权:把 pnpm 打印的确切包键写进该 profile 的 pnpm-workspace.yaml:
yaml
allowBuilds:
  <包名>: true

这一步等于提前放行安装期代码执行,只对源码可信的包使用(来源)。

DSH plugin 脚本化的注意事项

一句话总结:命令要选能退出的、成败要看退出码、确认要提前给答案、版本要钉死。 七条提醒:

  1. 常驻命令不进流水线:dsh web 这类进程会占住终端,只适合做后台服务;
  2. 验收用 --dump-config:add 的退出码只证明装了依赖,配置层要在组合树里确认;
  3. 不要解析输出文案:中英文措辞与版本都可能变,退出码才是稳定契约;
  4. 版本一定钉住:写 <包名>@<版本> 或 #<sha>,避免重跑结果随上游漂移;
  5. 确认环节提前给定:purge 用参数关掉、构建脚本用 allowBuilds 授权,别让 CI 等一个回车;
  6. $DSH_HOME 要可写:容器里默认路径可能落在只读层,必要时显式指向可写卷,且要在步骤间保持一致;
  7. 升级前先备份 profile:cp -r "$DSH_HOME/profiles/<名字>" "$DSH_HOME/profiles/<名字>.bak",脚本改坏时能整目录还原。

按非交互场景把命令编好之后,剩下的就是参数细节;如果你想要的是按功能或字母序排列的完整命令对照,那属于另一类文章,本篇只负责「能无人值守地跑起来」。

插件市场

来源:dsh CLI README、DeepSeek Harness 官方文档 - 打包与安装插件

常见问题

哪些 dsh plugin 命令可以放心放进脚本和 CI 非交互执行?

dsh plugin 里适合进脚本的是「一次性退出」的命令:dsh plugin --profile <名字> add / remove / list 与 dsh --profile <名字> --dump-config 都属于这类,跑完即退出。不适合的是常驻命令——dsh web 会占住终端不返回,只能用于后台服务,不能等在流水线里。

CI 里怎么判断 dsh plugin 命令是成功还是失败?靠看输出行不行?

判断 dsh plugin 在 CI 里的成败要靠退出码:CLI 对无效命令、参数错用、配置错误与启动失败都返回非零状态,脚本里用 set -e 或 if ! cmd 就能直接兜住。靠 grep 输出里的关键词很脆弱——输出文案会随版本变,而且中文与英文环境下的措辞不同,退出码才是稳定契约。

脚本重跑时插件已经装过了,dsh plugin add 会报错吗?怎么做幂等?

dsh plugin add 对已存在的包不会因为「装过了」而失败,它等同于覆盖安装,所以「按期望状态重跑」天然就比较幂等。要更保险的话,先跑 dsh plugin --profile <名字> list 读一下当前状态,用输出判断该不该执行 add,把决策显式写进脚本,而不是依赖 pnpm 的默认行为。

在 CI 或容器里跑 dsh plugin 命令经常卡在确认提示上,怎么关掉?

在 CI 或容器里跑 dsh plugin 卡在提示上,通常是 pnpm 在等交互确认。常见的两处是删除 node_modules 前的 purge 确认与 git 依赖的构建脚本授权:前者用 --config.confirm-modules-purge=false 或设 CI=true 让它走非交互分支,后者把 pnpm 打印的包键写进该 profile 的 pnpm-workspace.yaml 的 allowBuilds,让授权成为配置而不是问答。

容器里跑 dsh plugin 前置条件有哪些?最小可用写法是什么?

容器里跑 dsh plugin 的三个前置条件是:Node 版本够新、pnpm 在 PATH 里、$DSH_HOME 可写。最小可用写法是一行 CI=true dsh plugin --profile web add <包名>,前面用 npm install -g pnpm 保证 pnpm 存在,必要时把 registry 指向内网源;跑完再执行 dsh --profile web --dump-config 断言配置层已生成。

相关术语

非交互执行(non-interactive execution)
非交互执行指命令在没有人守着终端的情况下跑完:不等待输入、不弹确认、只以退出码表达结果。它是把 dsh plugin 命令放进脚本与 CI 的前提,遇到需要确认的环节必须改由参数或配置文件提前给定答案。— dsh CLI README
退出码(exit code)
退出码是进程结束时返回给调用方的整数状态,0 表示成功、非零表示失败。CLI 对无效命令、参数错用、配置错误与启动失败都返回非零,因此它是脚本里判断 dsh plugin 命令成败的稳定依据。— dsh CLI README
幂等重跑(idempotent rerun)
幂等重跑指同一条命令重复执行多次,结果与执行一次相同。对 dsh plugin 而言,可靠做法是先用 list 读取当前状态、再决定要不要执行 add,让「期望状态」而不是「命令是否跑过」成为脚本的判定依据。— dsh CLI README
allowBuilds
allowBuilds 是写在该 profile 的 pnpm-workspace.yaml 里的构建脚本白名单,pnpm 默认拒绝运行 git 依赖的构建脚本,把包键写进 allowBuilds 等于提前授权,避免安装过程停下来等你确认。— DeepSeek Harness 官方文档 - 打包与安装插件

来源