设计说明:git 源安装为什么必须带预构建产物(dist 入库)
September 6, 2026 · View on GitHub
关联 issue:#92 git 源安装失败:仓库未提供 dist 预构建产物,且 pnpm 默认拦截构建脚本(allowBuilds) 状态:v0.2.17 起随 dist 入库生效,本文记录决策依据,防止未来被"清理"回去。
问题
从 git 源安装(插件商店展示的形式,如 dsh plugin add github:Tyan66666/billion-context-dsh)在 pnpm 11 上必然失败,报 nothing installable: the plugin(s) need a build step (blocked by default, see allowBuilds) or ship no prebuilt artifacts。而商店条目里该插件的一键安装命令恰恰就是这个 git 形式。
根因(三个,叠加)
dist/未入库——.gitignore排除了dist/,而package.json的main/types/exports/files全指向dist。git clone 下来没有任何可运行产物(只有 npm 发布版的 tarball 里才有)。- 没有任何
prepare脚本——pnpm 对 git 依赖只运行prepare来构建;本仓库只有build脚本,所以即使放行构建也产不出dist。 - pnpm ≥10/11 默认拦截依赖构建脚本——pnpm 11 用
allowBuilds替换了旧的onlyBuiltDependencies系列,依赖的构建脚本默认一律不放行。
为什么"让用户手动放行"救不了
- 对 git 托管包,
allowBuilds的 key 必须是"包名@git+URL#完整 commit hash"的 depPath 形式,按包名的 key(billion-context-dsh: true)不匹配,而且依赖的 commit 一变 key 就得跟着换(pnpm#12367、pnpm#12294)。这条路在 pnpm 11 上基本不可用。 - 就算放行,包里没有
prepare脚本,dist也构建不出来(根因 2)。
决策:提交 dist/ 入库,并且保持零构建脚本
提交 dist/(与 npm pack 产物完全一致的一组文件),git 源安装变成纯文件安装:pnpm 拉下来即含可运行的 dist/,没有任何构建脚本可拦,allowBuilds 机制无从触发。
候选方案的取舍:
| 方案 | 结果 |
|---|---|
提交 dist/(采用) | 任意 git 形式(github:#<tag>、商店条目)开箱即用;不依赖用户配置 |
| GitHub Release 附 tgz、安装指向 tarball URL | 也能开箱即用,但改不了商店里已有的 github: 安装命令;手动 git 安装仍失败 |
加 prepare 脚本 | 适得其反:pnpm 11 会先拦 prepare 再报错,把本来(有 dist 时)能成功的安装搞挂;放行又要维护 commit-hash key |
配套约束(防止契约被无声破坏):
- 永远不要给这个包加
prepare/preinstall/install/postinstall脚本——见上表第三行;tests/package-artifacts.test.ts守护这条契约。 - CI 定期校验
dist/与源码一致(.github/workflows/ci.yml的 "Check committed dist matches the source" 步骤,git status --porcelain -- dist;每日定时 + dist-bot 自身提交后运行,PR 构建不再执行该检查)——防止入库产物过期,git 安装装到旧代码;日常新鲜度由 dist-bot 在每次合并后维护(见下一条)。 dist/由 dist-bot 在合并后自动维护,PR 分支不携带构建产物,开发者完全零接触:.github/workflows/dist-bot.yml监听 main 的 push,在可能改变构建产物的合并(src/**、package.json、package-lock.json、tsup.config.ts)落地后自动重建,产物有变化时把一份新鲜dist/以github-actions[bot]身份直接提交到 main——这需要 main 的分支保护关闭enforce_admins,bot 用 secretDIST_BOT_TOKEN(维护者的 fine-grained PAT,管理员凭证)推送;人类仍一律走 PR。CI 的漂移检查("Check committed dist matches the source")不再是 PR 闸门,改为每日定时 + bot 自身提交后校验 main 产物,兜底 bot 失效;合并到 bot 提交落地之间(约一两分钟)main 上的 dist 为上一版。发布 PR 合并后同理:gh release create打 tag 前等 bot 提交落地,使#<tag>安装与对应 npm 版本完全一致。- 建议用户安装时带
#<tag>;不带 ref 则装默认分支的最新构建(可能与最近一次发布有少量滞后)。