脚本和 CI 里怎么跑 dsh plugin 命令?DeepSeek Harness 非交互、退出码与幂等
把 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 命令的成败契约是退出码,不是输出文案:输出会随版本与语言环境变化,退出码不会(来源)。 写法上直接让失败中止:
set -euo pipefail
dsh plugin --profile web add "<包名>"
dsh --profile web --dump-config | grep -q "# =="
第二行才是真正的验收:add 成功只说明依赖装上了,--dump-config 里出现 # == <包名> 才说明配置层进了组合树(来源)。把这两步分开,日志里就能一眼区分「装不上」和「装了没生效」——后者多半是包没声明 dsh.bundle。
需要分支处理时用 if 而不是解析文本:
if dsh plugin --profile web list | grep -q "<包名>"; then
echo "already installed"
else
dsh plugin --profile web add "<包名>"
fi
别把 grep "error" 当成失败判据:pnpm 的警告文案里也可能出现这个词,而真正的失败已经被非零退出码表达了。
幂等重跑:dsh plugin 已装再装怎么办
「按期望状态重跑」比「按动作重跑」更可靠:先读当前状态,再决定要不要动手(来源)。 两条实践:
- 把状态查询作为脚本的第一步:
dsh plugin --profile web list
它的输出由 profile 目录里的 pnpm 给出,能看到包名与版本。用它做判断,脚本逻辑就是显式的,不必依赖「重复 add 会怎样」这种隐含行为。
- 需要固定版本时,把版本写进目标。脚本里最忌讳写「最新」——今天跑通、明天可能因为上游发了新版而失败。把目标钉在具体版本或具体 commit 上,重跑结果才稳定:
# 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 可写;满足之后一条命令就能跑(来源)。 最小可用片段:
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 "# =="
要点逐条:
CI=true让 pnpm 走非交互分支,遇到本该弹确认的环节直接按非交互处理;--config.confirm-modules-purge=false关掉删除node_modules前的确认,避免清缓存时卡住;pnpm必须在 PATH 里——容器镜像里没装 pnpm 时,命令会以pnpm failed in profile directory这类形态失败,排查方法见《dsh plugin 报 pnpm failed in profile directory 怎么修?》;- registry 要在受限网络里指向内网源,否则构建阶段会卡在拉包上,做法见《内网、离线环境怎么装 dsh?》;
- git 依赖提前授权:把 pnpm 打印的确切包键写进该 profile 的
pnpm-workspace.yaml:
allowBuilds:
<包名>: true
这一步等于提前放行安装期代码执行,只对源码可信的包使用(来源)。
DSH plugin 脚本化的注意事项
一句话总结:命令要选能退出的、成败要看退出码、确认要提前给答案、版本要钉死。 七条提醒:
- 常驻命令不进流水线:
dsh web这类进程会占住终端,只适合做后台服务; - 验收用
--dump-config:add的退出码只证明装了依赖,配置层要在组合树里确认; - 不要解析输出文案:中英文措辞与版本都可能变,退出码才是稳定契约;
- 版本一定钉住:写
<包名>@<版本>或#<sha>,避免重跑结果随上游漂移; - 确认环节提前给定:purge 用参数关掉、构建脚本用
allowBuilds授权,别让 CI 等一个回车; $DSH_HOME要可写:容器里默认路径可能落在只读层,必要时显式指向可写卷,且要在步骤间保持一致;- 升级前先备份 profile:
cp -r "$DSH_HOME/profiles/<名字>" "$DSH_HOME/profiles/<名字>.bak",脚本改坏时能整目录还原。
按非交互场景把命令编好之后,剩下的就是参数细节;如果你想要的是按功能或字母序排列的完整命令对照,那属于另一类文章,本篇只负责「能无人值守地跑起来」。

常见问题
dsh plugin 里适合进脚本的是「一次性退出」的命令:dsh plugin --profile <名字> add / remove / list 与 dsh --profile <名字> --dump-config 都属于这类,跑完即退出。不适合的是常驻命令——dsh web 会占住终端不返回,只能用于后台服务,不能等在流水线里。
判断 dsh plugin 在 CI 里的成败要靠退出码:CLI 对无效命令、参数错用、配置错误与启动失败都返回非零状态,脚本里用 set -e 或 if ! cmd 就能直接兜住。靠 grep 输出里的关键词很脆弱——输出文案会随版本变,而且中文与英文环境下的措辞不同,退出码才是稳定契约。
dsh plugin add 对已存在的包不会因为「装过了」而失败,它等同于覆盖安装,所以「按期望状态重跑」天然就比较幂等。要更保险的话,先跑 dsh plugin --profile <名字> list 读一下当前状态,用输出判断该不该执行 add,把决策显式写进脚本,而不是依赖 pnpm 的默认行为。
在 CI 或容器里跑 dsh plugin 卡在提示上,通常是 pnpm 在等交互确认。常见的两处是删除 node_modules 前的 purge 确认与 git 依赖的构建脚本授权:前者用 --config.confirm-modules-purge=false 或设 CI=true 让它走非交互分支,后者把 pnpm 打印的包键写进该 profile 的 pnpm-workspace.yaml 的 allowBuilds,让授权成为配置而不是问答。
容器里跑 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 官方文档 - 打包与安装插件
来源
- dsh CLI README· deepseek-ai
- DeepSeek Harness 官方文档 - 打包与安装插件· deepseek-harness
- dsh CLI README(中文)· deepseek-ai