dsh-publish-gate
Verified@bushizhanghan/dsh-publish-gate · v0.1.4 · MIT
Helps DSH plugin AUTHORS publish their plugins: a one-command gate that turns every silent failure in the publish path into a deterministic check — package.json completeness, BOM, YAML/JSON parseability, junction and patch insert correctness, npm scope pe
Install
dsh plugin add @bushizhanghan/dsh-publish-gate Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Readme
dsh-publish-gate — 帮你发布 DSH 插件的一键三连门禁
这个插件是给插件作者用的:你写完一个 DSH 插件,想发 GitHub + npm + 插件市场,它帮你把发布前该查的全部查一遍,再给你照抄的命令清单。
发布一个 DSH 插件有七步,每一步都有一个直到你付出代价才说话的坑:
files忘了lib/于是发出去一个空包、PowerShell 写下的 BOM 让JSON.parse报一个不提 BOM 的错、patch 的 insert 行缩进错了于是插件装上了但永远不加载、裸包名让 granular token 收到一个既不说 token 也不说 scope 的 403。这个插件把整条链路的每个静默坑变成一条确定性检查。
六个 agent 工具:全量门禁、patch 专项、市场条目校验、版本推进、多插件体检、发布命令清单。
典型用法(写完插件后)
我写好了一个新插件 X,帮我发出去
→ agent 先调 publish_gate(X) 查所有静默坑
→ 有 blocker 就修,修完再跑
→ 全绿后调 publish_gate_commands(X, {version}) 拿 GitHub/npm/市场三步的命令,照抄执行
安装
dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
重启 DSH 后生效。只读:这个插件从不写你的插件目录。
Agent 工具
| 工具 | 作用 |
|---|---|
publish_gate |
全量门禁:静态检查 + 网络检查,返回 safeToPublish |
publish_gate_patch |
只查 cordis.patch.yml——「插件装了但工具没出现」的第一个怀疑对象 |
publish_gate_market |
校验市场条目 YAML(url/name/category/description 双语) |
publish_gate_next_version |
当前版本、是否已被 registry 占用、建议 bump 到多少 |
publish_gate_doctor |
扫整个目录,按 blocker 数排序,看哪个插件还不能发 |
publish_gate_commands |
按顺序列出发布命令,并标出这台机器已经走到哪一步 |
检查清单
会真的挡住发布的(error)
| 规则 | 抓什么 |
|---|---|
utf8-bom |
BOM 让 JSON.parse 抛「Unexpected token」,错误信息里没有 BOM 三个字 |
json-parse |
package.json 语法错误 |
missing-field |
name/version/description/main/license/repository/author 缺失 |
invalid-name / invalid-version |
npm 拒绝的形状 |
files-omits-lib |
发出去是空包——npm 不警告 |
files-omits-patch |
dsh.bundle.patch 指的文件不在 files 里,装上不挂载 |
no-exports |
缺 exports["."],宿主解析不到入口 |
client-without-export |
声明了 client 平台却没导出 ./client,宿主半边在、侧栏永不出现 |
not-esm |
没 "type": "module",import 在加载时炸 |
no-insert-row |
patch 里只有 override,永远不挂载(页面报的仍然是这条——顶层 - id: 是 patch 不是挂载) |
patch-not-mount |
info:这些顶层 - id: 是在覆盖已有条目,合法,但不是挂载 |
insert-missing-name / insert-name-mismatch |
挂载名缺失或与 package.json 不一致 |
no-patch-file |
有 lib/index.js 但没有 cordis.patch.yml |
no-name-export / no-apply-export |
入口协议不完整 |
require-in-esm |
ESM 里 require() → ERR_AMBIGUOUS_MODULE_SYNTAX,报错不带行号 |
no-license |
缺 LICENSE |
version-taken |
这个版本已经在 registry 上,再发是 403 |
registry-unreachable |
registry 回了 500/429 —— 版本检查没跑,所以别把它当干净 |
值得在发布前知道的(warn / info)
unscoped-name(granular token 的 403)、repo-missing、patch-name-unresolvable(junction 放错层级)、no-readme / readme-no-install / readme-no-profile、license-mismatch、no-tests / no-test-files / no-test-script、no-engines、bad-repo-url、no-git / no-gitignore,以及市场条目的 market-* 全系列。
一个真实的例子
对一个刚从别人那儿接手、准备发布的插件跑 publish_gate:
✗ package.json starts with a UTF-8 BOM [utf8-bom]
A byte-order mark makes JSON.parse throw `Unexpected token` and npm publish
fail with a parse error that never names the BOM.
✗ files does not include "lib" [files-omits-lib]
The published tarball will contain no code.
✗ cordis.patch.yml has no insert row [no-insert-row]
A patch with only overrides never mounts your plugin. This file has 1
top-level `- id:` row — those patch entries that already exist in the tree,
they do not add one.
✗ lib/index.js calls require() inside an ESM module [require-in-esm]
4 blocker(s) — fix before publishing
这四条里有两条是发布四个插件时真实踩到的。
设计取舍
- 静态检查不需要网络:
publish_gate默认带网络检查(版本占用、repo 存在),但skipNetwork让它在离线和 CI 里同样可用;网络失败一律降级成「未知」,不让门禁本身变成障碍。 - 只读:修不修由你定,每条 finding 都带
fix文案。门禁的职责是把话说清楚,不是替你做决定。 - 确定性:全部规则是文件内容或 registry/GitHub API 检查,没有 LLM 调用——一条规则答错,你可以直接看规则。
- 自举:这个插件的测试把四个已发布插件当 fixture,断言它们在门禁下零 blocker;规则如果有任何误报倾向,测试先炸。
开发
node --test # 24 个测试(含 4 个真实插件作 fixture)
License
MIT