首页
/ Caveman CLI 发布流程全解:@caveman-ai/cli 的双轨版本线、签名校验与 npm 发布顺序

Caveman CLI 发布流程全解:@caveman-ai/cli 的双轨版本线、签名校验与 npm 发布顺序

2026-09-04 09:59:10作者:宣利权Counsellor

本文基于 packages/cli/PUBLISHING.md 详解 Caveman CLI 的完整发布规范:cli-v*bin-v* 双发布列车(release train)如何分工、二进制工件如何用密钥签名并做本地校验、以及发布顺序为何是"承重墙"(load-bearing)。读完你可以掌握:从 BINARY_RELEASE 打钉(pin)到 npm 发布再到跨平台冒烟测试的全链路操作,以及 tarball 内容边界、npm --prefix 陷阱等源码级细节。

发布前提与包名约定

仓库要求创始人(founder)签核后方可发布,且必须从经过审查的仓库检出(reviewed repository checkout)发布,绝不允许从一个解包的 tarball 发布——这是防止"改完再发"污染构建链的第一道闸。

包名与入口约定(见 package.json):

  • @caveman-ai/cli 发布,并始终带 npm publish --access public
  • 永远不要用裸名 caveman 发布——npm 上的该名字属于无关项目;
  • bin 字段同时安装两个可执行入口:cavemancave,两者都指向同一入口 dist/index.js
  • 包的运行环境要求 node >= 22.13engines.node),且 dependencies 为空对象——这是一个零运行时依赖的 JS CLI。

双发布列车:cli-v* 与 bin-v*

Caveman 的产物分为两种,走两条互不干扰的版本线:

Tag 负责内容
cli-v* 手动发布本零运行时依赖 JS CLI 到 npm
bin-v* 通过 release-binaries.yml 发布签名的 Go 伴生二进制

两条线的关键点:

  • CLI 的 semver 不隐含二进制版本。当前仓库中,packages/cli/BINARY_RELEASE 的内容是 bin-v1.1.4,而 package.json 的 CLI 版本是 1.3.1——两者独立演进。这个文件独立钉住(independently pin)CLI 构建所消费的二进制发布版本。
  • Caveman Cloud 的服务端镜像从 Caveman Cloud 仓库发布,不在本仓库范围内。
  • 二进制工作流把编译产物和签名清单发布到本仓库的 Releases(https://github.com/JuliusBrussee/caveman/releases/download 前缀)。

二进制钉住是如何进入 CLI 代码的

钉住不是"文档约定",而是被编译进 CLI 产物的。构建脚本 scripts/gen-binaries.mjs 会:

  1. 读取 packages/cli/BINARY_RELEASE,用正则 ^bin-v\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$ 校验其必须是 bin-v semver tag,否则直接抛错;
  2. 读取 packages/cli/BINARY_SIGNING_PUBKEY.pub,校验其为换行终止的单 PEM 公钥块;
  3. 生成 src/binaries.generated.ts,导出三个常量:BINARY_RELEASEBINARY_RELEASE_BASE_DEFAULT(releases download 前缀)、BINARY_SIGNING_PUBKEY(公钥全文,以字符串常量形式进入 JS 包)。

该脚本还支持 --check 模式:若生成的常量与已提交的 binaries.generated.ts 不一致,报错"binary release constants are stale"——即 BINARY_RELEASE 改钉后必须重新生成并提交,CI 可以借此拦截过期常量。

签名与校验:密钥体系全链路

仓库环境 binary-release 只需要一个创始人控制的 secret:CAVEMAN_BINARY_SIGNING_PRIVATE_KEY_PEM,它必须与仓库中提交的公钥(BINARY_SIGNING_PUBKEY.pub)匹配。上传使用工作流自身的 GITHUB_TOKEN 并配 job 级 contents: write 权限,不存在跨仓库 PAT。Windows 工件使用 win32/{arm64,amd64} 目录命名,且以 .exe 文件名安装。

这条信任链在代码中是闭环的:

发布端scripts/sign-binary-checksums.mjs):

  • 从环境变量读取私钥,先做公钥归一化比对normalizePublicKey(privateKeyPEM) 与提交的公钥做 SPKI PEM 规范化比较,不一致则拒绝签名——防止配错密钥;
  • 生成 sigstore 格式的签名 bundle(mediaType 为 application/vnd.dev.sigstore.bundle.v0.3+json,含 SHA-256 消息摘要与 RSA 签名),写入 checksums.txt.keysig,文件权限 0o600
  • 写盘前先对生成的 bundle 本地自验,自验失败即中止。

消费端verifyChecksumSignatureBundle,同文件):校验 mediaType、摘要算法 SHA2_256、digest 相等,最后用公钥 verify("sha256", ...) 验证签名。

用户端caveman setup --install 用编译进 CLI 的公钥校验 checksum manifest 签名,再对流式下载的每个工件逐个验证 SHA-256,之后才执行原子安装。这个本地检查覆盖了"公钥签名的 manifest 链",即 manifest 被公钥签名、工件哈希在 manifest 内、工件内容对得上 manifest。

release-binaries.yml 还叠加了发布侧的防篡改闸门:

  • 触发条件为 bin-v* tag,job 绑定 binary-release 环境(人工审批);
  • 第一步用 gh api 确认 tag 必须是 annotated 且 GitHub 验证 verification.verified == true,且 tag 目标是 origin/main 的祖先;
  • 强制 packages/cli/BINARY_RELEASE 与当前 tag 名严格相等tr -d '\r\n' 后比较),保证"钉住的版本就是正在发布的版本";
  • 构建后要求完整的 36 工件矩阵build-release-binaries.mjs --list 的输出必须与 dist/binaries/ 下实际文件逐一相等;
  • 发布前检查同名 Release 不存在,拒绝覆盖已有 Release。

发布顺序:为什么资产必须先于 npm 包存在

PUBLISHING.md 明确写道:"Ordering is load-bearing: assets must exist before npm package naming them."(顺序是承重的:资产必须先于引用它们的 npm 包存在。)完整顺序如下:

1. 钉住即将发布的二进制 tag

packages/cli/BINARY_RELEASE 设置为即将切出的二进制 tag,运行 CLI 测试并提交生成的常量(即 binaries.generated.ts)。同时要求最新一次 profile-contract Actions 运行是绿的——该工作流(profiles.yml)会拿真实压缩能力去验证随包发布的 agent profiles(校验 profile 注册表、要求生成物与 PR diff 一致、跑 fake-harness 矩阵)。

2. 在本仓库切出二进制 tag

annotated、签名、位于 main 上。release-binaries.ymlbinary-release 环境审批通过后完成构建、签名、发布到本仓库 Releases。

3. 匿名 HTTP 验证

没有 GITHUB_TOKENGH_TOKEN~/.netrc 或未认证 gh 的环境下,验证全部 36 个二进制文件和 2 个 manifest 文件均返回匿名 HTTP 200。这一步模拟的正是终端用户的真实下载路径——任何登录态可见但匿名不可见的资产都会在此暴露。

4. 版本号 bump、切 CLI tag、发布 npm

node agents/compile.mjs
pnpm --dir packages/cli test
node agents/probe-installed.mjs --all --json
(cd packages/cli && npm pack --dry-run)
(cd packages/cli && npm publish --access public)

其中 probe-installed --all发布机证明(release-machine proof):缺失、损坏或版本漂移的二进制都会使闸门失败;CI 会独立安装每个钉住的 profile,因此本地全局安装无法掩盖 profile 漂移。

5. 干净机器冒烟测试

在 macOS ARM、Linux x64、Windows x64 三平台上,钉死到确切的 npm 版本号执行:安装 CLI → caveman setup --installcaveman --versioncaveman wrap --off → 一次压缩加字节级精确的取回(byte-exact retrieval)→ 卸载且不损伤用户配置。

失败后的回退策略:冒烟失败则对该 npm 版本执行 deprecate、回滚 quickstart 安装文案,调查清楚后再谈下一次发布。永远不要覆盖已发布的版本或删除普通二进制 Release;只有在存在活跃安全风险时才移除资产。

包内容边界:tarball 里有什么、不能有什么

npm tarball 保持"纯 JS":只包含 dist/README.mdLICENSE 和包元数据(对应 package.jsonfiles 字段:["dist", "README.md", "LICENSE"])。

关键设计决策:

  • 不存在 postinstall 网络拉取。用户显式运行 caveman setup --install 来获取 Go 伴生二进制——因为包管理器的脚本策略(ignore-scripts 等)可能会静默拦截运行时安装脚本,显式命令则不受此影响。
  • Go 二进制本身不进 npm 包,而是通过 Releases 资产 + 签名 manifest + 本地 SHA-256 校验的通道交付。

发布前检查清单与 tarball 审查

node agents/compile.mjs
pnpm --dir packages/cli test
node agents/probe-installed.mjs --all --json
(cd packages/cli && npm pack --dry-run)

打包后人工审查 tarball,它必须包含:编译进 dist/index.js 的二进制发布常量(BINARY_RELEASEBINARY_RELEASE_BASE_DEFAULTBINARY_SIGNING_PUBKEY,由 gen-binaries.mjs 生成);它不得包含:私钥、源码树、Go 二进制、任何凭据。

一个真实的 npm 陷阱

PUBLISHING.md 特别警告:不要使用 npm --prefix packages/cli pack 的写法。npm 11 在 pack/publish 场景下可能忽略 --prefix,转而打包仓库根目录的 package——结果就是把错误的包发出去。所以正确姿势是子 shell 内执行 (cd packages/cli && npm pack ...),与上面命令块中的写法一致。

小结:这套发布规范的三个设计支柱

  1. 双轨解耦cli-v*(npm,手动)与 bin-v*(Releases,自动化工作流)各自带版本,靠 BINARY_RELEASE 单文件钉住,且钉住值被正则校验并编译进 CLI 二进制常量;
  2. 端到端可验证的信任链:创始人私钥 → 工作流环境审批 → 签名 manifest(sigstore bundle)→ 编译进 CLI 的公钥 → 用户端 setup --install 的 manifest 签名校验 + 逐工件 SHA-256;每一环都有对应的代码实现(sign-binary-checksums.mjsgen-binaries.mjsrelease-binaries.yml);
  3. 不可逆发布纪律:匿名 200 验证先于 npm 发布、三平台钉版本冒烟、失败只 deprecate 不覆盖、tag 必须 annotated 且 GitHub 签名验证通过——共同构成"发出去的东西必须可追责"的底线。

对维护者而言,这篇规范配合 packages/cli/tests 下的 release-binaries.test.mjsbinary-installer-platform.test.mjs 等安装器测试,覆盖了从 tag 切出到用户落盘的每个关键断言点。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
981
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384