Composio CLI 发布工作流全指南:从自动 Beta、稳定版提升到失败恢复的 GitHub Releases 实战
导读
本文基于 Composio 仓库中的 CLI Release Workflow 手册(配属 .agents/skills/cli-release/SKILL.md 技能),完整讲解独立 composio 命令行二进制与安装器所依托的 GitHub Release 发布体系:如何区分自动 Beta、手动 Beta、稳定版提升与失败恢复四条路径,如何用 gh CLI 预检候选 Beta、核对资产与安装测试,以及发布被中断或失败时如何安全恢复。读完本文,你将掌握一套可直接执行的发布操作手册,并理解 build-cli-binaries.yml、resolve-release-target.sh、verify-assets.sh 等底层脚本的判定逻辑与设计动机。
Sources Of Truth:谁在真正掌控 CLI 发布
CLI 二进制发布不依赖 npm 或 Changesets,而是由一组 GitHub Actions 工作流与脚本共同构成唯一事实来源(sources of truth):
| 事实来源 | 职责 |
|---|---|
| build-cli-binaries.yml | 拥有 Beta 与 Stable 两类 GitHub Release 的构建与发布 |
| resolve-release-target.sh | 决定发布 tag 与源 commit(三种模式:push 滚动 Beta、build-beta 派发、promote-stable 提升) |
| verify-assets.sh | 定义并要求六个规范资产全部 uploaded |
| cli.test-installation.yml | 发布后跨平台验证安装器与 shell 集成 |
| .changeset/config.json | 将 @composio/cli 与 @composio/cli-local-tools 加入 ignore 列表,使其脱离 Changesets 发布轨道 |
特别要注意:ts.release.yml 是 TypeScript SDK/npm 的发布列车,不是 CLI 二进制的常规发布路径。CLI 包在 ts/packages/cli/package.json 中被标记为 private(版本号为开发哨兵值 0.0.0-development),永远不通过 Changesets 发布到 npm;二进制资产只挂在 GitHub Release 上,install.sh 与 composio upgrade 从 Releases 下载,composio upgrade --beta 则解析最新的 CLI 预发布版本。
Choose The Path:先归类需求,再选择发布路径
动手前必须先判断本次请求属于哪一类,因为不同目标的入口、结果与验证方式完全不同:
| 目标 | 路径 | 结果 |
|---|---|---|
| 发布一个普通 CLI 变更 | 将已评审的 PR 合并到 next |
push 自动构建滚动 Beta(rolling beta) |
| 从某分支构建 Beta | 在该分支派发 build-beta |
从该分支 commit 构建预发布版本 |
| 发布稳定版 CLI | 在已有且经过测试的 Beta tag 上派发提升(promotion) | Beta 的源 commit 被重建并以稳定 tag 发布 |
| 恢复失败的提升 | 检查 draft 后重跑或重新派发同一个 Beta | 未发布的 draft 可被恢复并替换资产 |
build-cli-binaries.yml 的 push 触发条件限定了 CLI 相关路径(ts/packages/cli/**、ts/packages/cli-local-tools/**、install.sh、install/**、mise.toml 等),也就是说只有真正改动 CLI 的合并才会触发自动 Beta。手动派发则通过 workflow_dispatch 的 action 输入(build-beta 或 promote-stable)与可选 version 输入来控制。
一个关键约束来自 resolve-release-target.sh:私有 CLI 的 package.json 使用开发哨兵版本号,永远不会被用来选择二进制版本。如果发布负责人需要一次有意的 minor 或 major 版本,正确做法是:派发一个显式指定版本号的 Beta → 完整验证 → 再提升那个确切的 Beta。
Changeset Rule:永远不要为被忽略的 CLI 包创建 Changeset
@composio/cli 和 @composio/cli-local-tools 位于 .changeset/config.json 的 ignore 列表中,因此严禁为它们创建 .changeset/*.md 条目。
一旦有人创建了指向被忽略包的 Changeset,会触发一个非常隐蔽的连锁故障:
changesets/action进入 version-PR 模式;- 但
changeset version对被忽略的包不会产生任何 commit; - action 最终以
No commits between next and changeset-release/next失败,并阻塞无关的 SDK 发布。
这一点在 ts/scripts/validate-changesets.mjs 的实现中有直接印证:findIgnoredChangesetReleases 会扫描所有待处理的 Changeset,凡命中 config.ignore 中包名的,直接抛出异常,错误信息明确说明“被忽略包的 Changeset 会让 release job 在打开空 PR 时失败”。
正确的替代做法是:如果 CLI 变更需要面向用户的说明,直接编辑 ts/packages/cli/CHANGELOG.md。交接前必须运行守卫命令:
pnpm validate:changesets
该命令定义在根 package.json 中,对应 ts/scripts/validate-changesets.mjs;同时 test/release-workflow.test.ts 会校验 .changeset/config.json 的 ignore 列表、工作流与脚本之间的一致性,防止这些约束悄然漂移。
Inspect Candidates:基于实时 GitHub 状态挑选 Beta
选择候选 Beta 时必须使用 GitHub 的实时状态,绝不从本地 tag 或记忆中的版本挑选,因为发布状态随时可能变化。
先列出最近 100 个 Release,过滤出已发布的 @composio/cli@ 预发布版本:
REPOSITORY=ComposioHQ/composio
gh release list \
--repo "$REPOSITORY" \
--limit 100 \
--json tagName,isPrerelease,isDraft,publishedAt \
--jq '.[] | select(.tagName | startswith("@composio/cli@")) | select(.isPrerelease and (.isDraft | not))'
对选中的候选,要求它必须是已发布的预发布版本(而非 draft),然后检查其 commit 与资产:
BETA_TAG='@composio/cli@0.0.0-beta.000'
gh release view "$BETA_TAG" \
--repo "$REPOSITORY" \
--json tagName,isDraft,isPrerelease,publishedAt,targetCommitish,assets \
--jq '{tagName,isDraft,isPrerelease,publishedAt,targetCommitish,assets:[.assets[] | {name,state}]}'
合格的 Beta 必须满足 isDraft: false、isPrerelease: true,且以下六个规范资产全部处于 uploaded 状态(这六项与 verify-assets.sh 中的 expected 列表完全一一对应,并与构建矩阵的四个平台保持一致):
composio-linux-x64.zipcomposio-linux-aarch64.zipcomposio-darwin-x64.zipcomposio-darwin-aarch64.zipcomposio-skill.zip(随版本打包的 skills 包)checksums.txt(由 generate-checksums.ts 对dist/binaries下所有 zip 生成的校验和)
资产状态为何如此重要?verify-assets.sh 的注释点明了原因:一个资产可以出现在列表里但仍在处理中(state != "uploaded"),这正是发布后出现 404 的典型成因。所以该脚本采用有界重试(默认 VERIFY_ATTEMPTS=10 次、每次间隔 VERIFY_SLEEP_SECONDS=15 秒),且单次快照同时查询名称与状态,避免“检查与使用之间的时间窗口”。
找到 Beta 后,按其目标 commit 定位工作流运行记录,并要求其全绿(包括可复用的安装测试作业):
TARGET_COMMIT='replace-with-targetCommitish'
gh run list \
--repo "$REPOSITORY" \
--workflow build-cli-binaries.yml \
--commit "$TARGET_COMMIT" \
--limit 10
权限确认点:如果用户只要求“发布稳定版”但没有点名具体 Beta tag,那么你应该先展示解析出的候选,并在派发前停下征得明确确认——稳定版提升是一次生产环境的写入操作。
Build A Manual Beta:显式版本与非版本化 Beta
只有当用户明确要求构建 Beta 时才走这条路。选中的 ref 同时提供工作流定义与源 commit(这正是“该 ref 上定义的工作流构建该 ref 的代码”的原因)。
常规的 next-patch Beta(省略 version):
SOURCE_BRANCH='replace-with-branch'
gh workflow run build-cli-binaries.yml \
--repo "$REPOSITORY" \
--ref "$SOURCE_BRANCH" \
--raw-field action=build-beta
有意的 minor 或 major 版本(必须比最新稳定版更新,例如 0.3.0):
gh workflow run build-cli-binaries.yml \
--repo "$REPOSITORY" \
--ref "$SOURCE_BRANCH" \
--raw-field action=build-beta \
--raw-field version=0.3.0
版本校验逻辑在 resolve-release-target.sh 中:version 必须匹配 <major>.<minor>.<patch>,且必须大于最新稳定版(version_is_greater 按数值比较,而非字典序——注释明确解释了为何字典序会在 patch 超过 9 时出错)。不传版本时,next_beta_base_version 取最新稳定版并 patch + 1 作为基础版本,最终 tag 形如 @composio/cli@<version>-beta.<RUN_NUMBER>(RUN_NUMBER 保证唯一性)。
派发后要持续观察运行直到发布与安装测试结束。请牢记:Beta 不是稳定版,它不触发 /releases/latest 重定向,也不能被匿名用户通过 install.sh 无参数安装获取(除非显式传 tag)。
Promote A Beta To Stable:从测试过的 Beta 提升稳定版
稳定版 tag 通过去除 Beta 后缀推导(${BETA_TAG%%-beta.*}),例如 @composio/cli@0.3.0-beta.123 → @composio/cli@0.3.0。先确认目标稳定 tag 的现状:
STABLE_TAG="${BETA_TAG%%-beta.*}"
gh release view "$STABLE_TAG" --repo "$REPOSITORY" --json tagName,isDraft,isPrerelease,publishedAt
按结果分三种情况:
- 稳定 tag 不存在:可以继续提升;
- 稳定 tag 是 draft:可以恢复该 draft(见下文失败恢复);
- 稳定 tag 已发布:立即停止,绝不覆盖已发布的 Release。
然后在 Beta tag 上派发工作流——选中的 ref 提供不可变的源 commit,工作流会先验证它与该 Beta Release 的目标 commit 一致,再重新构建:
gh workflow run build-cli-binaries.yml \
--repo "$REPOSITORY" \
--ref "$BETA_TAG" \
--raw-field action=promote-stable
此处 --ref 传入的是 tag 而非分支。若命令返回了 URL 则直接使用;否则通过以下方式定位新派发,核对其创建时间与 actor 后再持续观察:
gh run list \
--repo "$REPOSITORY" \
--workflow build-cli-binaries.yml \
--event workflow_dispatch \
--commit "$TARGET_COMMIT" \
--limit 5
gh run watch RUN_ID --repo "$REPOSITORY" --compact --exit-status
promote-stable 的底层防御在 resolve-release-target.sh 中层层把关:
- 必须派发在 tag 上(
REF_TYPE == "tag"),否则报错退出; - 该 tag 必须匹配
@composio/cli@<version>-beta.<number>格式; - 通过 GitHub API 校验该 tag 的 Release 确实是 prerelease;
- 若稳定 tag 已是 draft 则允许恢复(
gh release view能按名称解析 draft),若已发布则拒绝(REST 的/releases/tags/{tag}对 draft 返回 404,因此用gh release view判断isDraft); - 校验
target_commitish与当前COMMIT_SHA完全一致——这保证了“稳定版对应的二进制就是被测试过的那个 Beta 的源码构建产物”。
Verify Completion:发布完成的五重验证
不要在任何一步缺失时宣布发布完成,全部满足才算完成:
Build CLI Binaries工作流成功结束;- 稳定 Release 已发布,
isDraft: false且isPrerelease: false; - 六个规范资产全部存在且
uploaded; - 工作流的安装测试矩阵通过;
- (隐含)提升来源的 Beta 本身是通过全部验证的。
用一条命令核对最终状态:
gh release view "$STABLE_TAG" \
--repo "$REPOSITORY" \
--json tagName,isDraft,isPrerelease,publishedAt,targetCommitish,assets \
--jq '{tagName,isDraft,isPrerelease,publishedAt,targetCommitish,assets:[.assets[] | {name,state}]}'
汇报内容应包含:稳定 tag、被提升的 Beta、目标 commit、工作流 URL、资产数量与状态、安装测试结果。
值得展开的是发布管线内部的“先 draft 后 publish”设计(见 build-cli-binaries.yml 与 create-or-resume-draft.sh):Release 先以 draft 形式创建并附带全部资产——draft 不会触发 release: published 事件,也不会被 /releases/latest 重定向命中,因此任何匿名消费者(install.sh、重定向)都不可能在任何资产挂载并验证完成之前观察到这个发布;随后 verify-assets.sh 作为“响亮失败门”把关,最后一步才用 gh release edit --draft=false --latest=... 将其翻转为已发布。此外,release 作业对同一个 tag 使用 job 级并发组(cli-release-${{ needs.prepare.outputs.release_tag }})做串行化,防止两次快速 push 或重跑交错上传资产。
发布后的安装验证由 cli.test-installation.yml 承担:它作为可复用工作流被 build-cli-binaries.yml 以 workflow_call 方式调用,在多平台矩阵(Ubuntu x64 / Ubuntu ARM64、macOS Intel / Apple Silicon,覆盖 bash 与 zsh)上真实执行 install.sh,验证 bundle 与入口符号链接、composio --version 执行、shell 启动文件中的托管 PATH 块(要求恰好一个 managed block,保证幂等)、自定义安装目录、卸载与错误处理等。这也是为什么发布负责人必须等到安装测试矩阵通过才能收工。
Failure Recovery:发布失败的分场景恢复手册
| 失败场景 | 处理方式 |
|---|---|
| 构建矩阵失败 | 任何 Release 都不应发布。修复源码,产出新的 Beta,再提升该候选 |
| 存在 draft 但发布未完成 | 检查失败原因后,重跑或重新派发同一个 Beta。draft 的资产可用 --clobber 安全替换(见 create-or-resume-draft.sh 的幂等恢复逻辑) |
| 重复运行提示已发布 | 这是有意的安全失败。核实已发布的 Release 后停止重复运行。其机制是:按 tag 串行化后,先运行的已发布,后运行的撞上 already published 守卫并响亮地报错,而不是悄悄覆盖线上 Release |
| 发布后安装失败 | 不要改动已发布的 tag。通过新的 Beta 与下一个稳定 patch 向前修复 |
| TS 发布提示 release PR 无提交 | 删除指向被忽略 CLI 包的待处理 Changeset,将其说明保留在 CLI 的 CHANGELOG 中,运行 pnpm validate:changesets,让下一次 push 重试 SDK 发布列车 |
恢复操作的核心原则与源码守卫完全一致:draft 是“可恢复的中间状态”,已发布 tag 是“不可变的事实”。resolve-release-target.sh 的 promote-stable 分支允许恢复 draft(打印 resuming (assets will be re-uploaded)),但遇到已发布 tag 直接 exit 1;create-or-resume-draft.sh 同样只有两种情况会安全继续——draft 存在(clobber 重传)或 Release 完全不存在(新建),其余一律报错。理解这层设计后,遇到“红色 ❌”不应盲目重试,而应先确认 tag 的真实状态。
小结:一次发布的生命周期
把整套流程串起来,一次规范的 CLI 发布是这样的闭环:
- 合并 PR 到
next(仅 CLI 相关路径)→ 自动构建滚动 Beta; - 基于 GitHub 实时状态挑选并验证候选 Beta(六个资产
uploaded、工作流与安装测试全绿); - 若需要显式版本,派发
build-beta并携带version输入; - 在通过验证的 Beta tag 上派发
promote-stable,由脚本守卫完成 commit 一致性校验与“不覆盖已发布 tag”的保护; - 先 draft 建 Release、校验资产、再翻转为发布,最后等待安装测试矩阵通过;
- 汇报稳定 tag、来源 Beta、目标 commit、工作流 URL、资产状态与安装结果;
- 若任何环节失败,按失败场景表恢复,且永远“向前修复”而不是改动已发布的 tag。
需要再次强调的是,这套 CLI 二进制发布体系与 TypeScript SDK 的 Changesets 发布是两套并行轨道:CLI 包被 .changeset/config.json 显式 ignore,其版本号由 GitHub Release tag(@composio/cli@<version>[-beta.<n>])唯一权威决定,而不是由 package.json 或 Changeset 决定。理解这条边界,是安全操作 Composio CLI 发布的起点。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00