首页
/ OpenHands Agent Canvas 的 release-please 自动化发布流程全解:从 Conventional Commits 到 npm 与 GHCR 双通道发布

OpenHands Agent Canvas 的 release-please 自动化发布流程全解:从 Conventional Commits 到 npm 与 GHCR 双通道发布

2026-09-04 18:15:39作者:凌朦慧Richard

本篇指南基于 OpenHands 仓库中 Agent Canvas(@openhands/agent-canvas)的官方发布技能文档 .agents/skills/release.md,完整还原该仓库以 release-please 为核心的 trunk-based 自动化发布体系:如何由 Conventional Commit 推导版本号、如何审查并"切割"(cut)一个 release PR、tag 推送后如何同时触发 npm 与 Docker 发布,以及发布失败时的四类典型排障手段。读完后你可以独立完成一次完整的版本发布,并理解仓库内 release-please-config.json.release-please-manifest.json.github/release.yml 及各发布 workflow 之间的联动关系。

一、总体机制:release-please 驱动的五步发布链路

Agent Canvas 的发布是 trunk-based(主干开发)且完全自动化 的,核心由组织级共享的可复用 workflow(OpenHands/release-actions 仓库)承载。整条链路可以概括为五个环节:

  1. PR 合入 main 必须遵循 Conventional Commit 标题featfixperfdocschorebuildcirefactorstyletestrevert)。.github/workflows/pr.yml 会对 PR 标题做 lint,并打上对应的 type: 标签;squash merge 时 PR 标题即 commit message。该 workflow 特意使用 pull_request_target(而非 pull_request)以保证 fork 来源的 PR 也能被处理,且故意不传 secrets: inherit——因为可复用 workflow 只读 payload 中的标题、不检出 PR 代码,这样做可以让 App token 保持在作用域之外(见该文件头部的安全注释)。
  2. 每次向 main 推送都会触发 release-please.github/workflows/release.yml),维护一个标题为 chore(main): release X.Y.Zdraft release PR,累积自上一个版本以来合入的所有变更。
  3. release PR 预先写好全部版本号变更package.jsonpackage-lock.jsonconfig/defaults.json 中的 versions.agentCanvas,以及 README.md / README.windows.md 中用 x-release-please-version 注释标注的 Docker 镜像 pin。
  4. 将 release PR 标记为 "Ready for review" 是显式的"切割发布"信号.github/workflows/release-ready.yml 监听 ready_for_review 事件,向 Slack 频道 #proj-agent-canvas 发通知,并给 PR 打上 release: ready 标签。
  5. 合并 release PR 后,release-please 推送 vX.Y.Z tag(使用组织 release App token)并创建 GitHub Release。同一个 tag 推送随即触发 npm-publish.yml(发布到 npm)与 docker.yml(构建多架构 GHCR 镜像)。

版本号如何推导

下一版本号由上次发布以来合入的 Conventional Commit 类型决定:

提交类型 版本影响
fix patch(X.Y.Z → X.Y.(Z+1))
feat minor(X.(Y+1).0)
任意类型带 ! 后缀,或提交信息带 BREAKING CHANGE footer major((X+1).0.0)
docs / chore / refactor 等其余类型 仅出现在 release notes 中,单独不产生 release PR

Release notes 由 .github/release.ymltype: 标签分组,配置为六个类别:Features(type: feat)、Bug Fixes(type: fix)、Performance(type: perf)、Documentation(type: docs)、Maintenance(type: chore/build/ci/refactor/style/test/revert)、Other Changes(其余标签兜底)。注意配置里 skip-changelog: true,即不维护 CHANGELOG.md,GitHub Releases 本身就是 changelog(仓库中现存的 CHANGELOG.md 只保留了最早的 1.0.0-alpha.2 历史条目)。

版本面(version surface)清单定义在 release-please-config.json 中,当前 .release-please-manifest.json 记录的已发布版本为 1.16.0,与 package.jsonversion 字段及 config/defaults.jsonversions.agentCanvas 完全一致。

铁律:永远不要手工往 release PR 分支上提交任何东西 —— 该分支由 release-please 独占,且它会在每次 main 有推送时 force-push 该分支。

二、核心配置文件逐项解读

release-please-config.json

{
  "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
  "include-component-in-tag": false,
  "include-v-in-tag": true,
  "draft-pull-request": true,
  "packages": {
    ".": {
      "release-type": "node",
      "changelog-type": "github",
      "skip-changelog": true,
      "extra-files": [
        {
          "type": "json",
          "path": "config/defaults.json",
          "jsonpath": "$.versions.agentCanvas"
        },
        {
          "type": "generic",
          "path": "helm/agent-canvas/Chart.yaml"
        },
        "README.md",
        "README.windows.md"
      ]
    }
  }
}

各字段的作用:

  • include-v-in-tag: true:tag 带 v 前缀(如 v1.16.0),这正是 npm-publish.ymltags: ['v*'] 触发条件的来源。
  • draft-pull-request: true:版本 bump 以 draft PR 形式存在,"标记 ready" 才成为显式发布信号(下节详述)。
  • release-type: node:按 Node.js 语义(SemVer)从 conventional commits 推导版本号。
  • skip-changelog: true + changelog-type: github:变更说明直接写入 GitHub Release,不生成 changelog 文件。
  • extra-files 是关键细节:
    • config/defaults.jsonJSON jsonpath 更新($.versions.agentCanvas)。该文件是全仓库"版本 pin 的单一事实来源",被 scripts/dev-safe.mjsscripts/dev-with-automation.mjsdocker/entrypoint.sh(经生成的 defaults.env)和 .github/workflows/docker.yml 共同读取——所以它必须和 npm 包版本同步 bump,否则 Docker 安装路径会 pin 到旧版。
    • helm/agent-canvas/Chart.yaml 必须用 generic(注释驱动)类型。配置文件中的注释解释得很清楚:如果写成裸路径字符串,release-please 会解析为 CompositeUpdater,覆盖 chart 级 version 并在 js-yaml 重新序列化时剥离文件内所有注释
    • README.mdREADME.windows.md 是纯文本,靠行内注释 # x-release-please-version 定位替换。当前仓库中可确认这三处标注:README.md 第 99 行(docker-compose 中的 ghcr.io/openhands/agent-canvas:1.16.0)和 README.windows.md 第 15、24 行。

.release-please-manifest.json

{ ".": "1.16.0" }

记录当前已发布版本,release-please 每次运行据此对比、决定是否需要开/更新 release PR。

.github/workflows/release.yml:带"CI 守门"的 release-please 调用方

.github/workflows/release.yml 的触发分支为 mainrelease/**(维护分支,发布作用域限定在该分支),包含两个 job:

  • await-required-checks:只有当推送的 commit 标题匹配 chore(*): release *(即 release PR 的 squash 合并 commit)时才等待——因为只有这次合并会真正打 tag。它轮询 test-and-build (ubuntu) 这个 check run 直到 success(最长 40 分钟,20 秒一次轮询)。原因是仓库的 "Release Tag" ruleset 要求被 tag 的 commit 上该检查必须为绿,否则 release-please 会与 CI 竞争、tag 推送会被 pre-receive 规则拒绝。非 release commit 的推送直接跳过等待。
  • release-pleaseneeds: await-required-checks,以 secrets: inherit 调用共享的 release-please.yml 可复用 workflow。文件注释强调这是必需而非优化:release-please 必须使用组织 App token(RELEASE_APP_ID / RELEASE_APP_PRIVATE_KEY)创建 release,因为只有它触发的 release 才会发出 release: published 事件;若退化为 GITHUB_TOKEN,事件会被抑制,后续发布链路断掉。

三、Step 1:找到 Release PR

gh pr list --state open --label "autorelease: pending"

如果没有任何 release PR 打开,只有两种可能:上次发布以来没有可发布内容(无 feat/fix/breaking 合入),或者 release.ymlmain 上正在失败:

gh run list --workflow=release.yml --limit=3

四、Step 2:审查 Release PR

打开 release PR,确认两件事:

  1. 标题中的版本号符合预期。它由合入 commit 的类型计算得出——如果看起来不对,回查上次发布以来各 PR 标题的 conventional 类型。想强制指定版本,向 main 合入一个 commit message 带 Release-As: X.Y.Z footer 的提交即可(release-please 官方支持的覆盖机制)。
  2. 暂存的 bump 覆盖所有版本面package.jsonpackage-lock.jsonconfig/defaults.jsonREADME.mdREADME.windows.md(以及 helm/agent-canvas/Chart.yaml),且 release notes 列出了预期变更。

五、Step 3:切割发布(需先与用户确认)

在此停下来,先向用户确认再执行——把 PR 标记 ready 并合并后就会真实发布到 npm 和 GHCR。

gh pr ready <release-pr-number>

这一步触发 release-ready 门禁(release-ready.yml):Slack 通知落到 #proj-agent-canvas 频道,PR 被贴上 release: ready 标签。然后以 squash 方式(与其他 PR 相同)合并该 release PR。注意 SLACK_BOT_TOKEN 是可选的——没有它时门禁仍会打标签,发布照常进行。

六、Step 4:观察发布流水线

合并 release PR 会再次触发 main 上的 release.yml:等待所需检查通过后推送 vX.Y.Z tag、创建 GitHub Release;tag 推送随即点火两条发布流水线。监控命令:

gh run list --workflow=release.yml --limit=3
gh run list --workflow=npm-publish.yml --limit=3
gh run list --workflow=docker.yml --limit=3

npm-publish.yml 的实现可以看到 tag 推送后的完整动作:Node 24 环境安装依赖 → 跑全量测试 → 分别构建 app 与 library(npm run build / npm run build:lib,构建期注入 VITE_POSTHOG_API_KEY)→ 把生产遥测默认值烘进打包产物 → npm pack --dry-run 校验包内容 → 校验 package.json 版本与 tag 版本严格一致(不一致即失败,这是"tag 与包版本不符"的防线)→ 解析 dist-tag → 以 OIDC trusted publishing 发布:

npm publish --access public --provenance --tag ${{ dist_tag }}

dist-tag 的解析策略(源码注释中有完整说明):在第一个稳定版发布之前,所有版本都用 latest,保证 npm install @openhands/agent-canvas 始终解析到最新构建;一旦存在稳定版,预发布版本退回各自的 alpha / beta / rc 标签,只有稳定版保留 latest。之所以把 dist-tag 直接写在 publish 命令里而不是事后 npm dist-tag add,是因为 OIDC 的 trusted-publishing token 只覆盖 npm publish 这一次调用,额外的 dist-tag 请求会返回 E401。

docker.yml 同样监听 v* tag,负责构建并发布多架构的 ghcr.io/openhands/agent-canvas 镜像(main 分支与 PR 上则跑构建冒烟)。

七、Step 5:验证发布

# GitHub release
gh release view v<version>

# npm(发布传播约需 2 分钟)
npm view @openhands/agent-canvas@<version>
npm view @openhands/agent-canvas dist-tags   # 稳定版应持有 latest

# Docker
docker pull ghcr.io/openhands/agent-canvas:<version>

三点交叉验证:GitHub Release 存在、npm 上可查询到该版本(且 dist-tag 符合预期)、GHCR 镜像可按版本号拉取。

八、Troubleshooting:四类典型故障

1. 合入 main 后没有 release PR 出现

只有 featfix 和 breaking 变更会产生 release PR。同时检查 main 上的 release.yml 运行记录——该 workflow 在设计上会故意失败当组织 secrets RELEASE_APP_ID / RELEASE_APP_PRIVATE_KEY 不可用时(GITHUB_TOKEN 兜底会打出一个永远不会触发发布流水线的 tag,因此宁可显式失败)。

2. 提议的版本号不对

版本来自上次发布以来的 conventional commit 历史。修复方式向前看:向 main 合入一个带 Release-As: X.Y.Z footer 的 commit 来固定下一个版本。

3. PR 标记 ready 后没有 Slack 消息

SLACK_BOT_TOKEN 按设计是可选的——门禁仍会打上 release: ready 标签,发布流程正常推进,只是没有通知。

4. package.json 版本与 tag 不一致

正常流程下不可能发生:release-please 在 release PR 里 bump package.json,tag 打在合并 commit 上,而 npm-publish.yml 会强制校验两者一致。如果真遇到了,说明有人手工推了 tag——解决办法是删除该 tag,把打 tag 的职责交还给 release-please。

九、关键实践要点总结

  • Conventional Commit 标题即发布契约:每个 PR 标题的类型前缀同时决定 type: 标签(release notes 分组)和版本号推导,改标题要慎重。
  • "标记 ready"是发布闸门:draft PR + ready_for_review 事件监听构成了一个显式的人工确认点,避免了"合入即发布"的误操作;发布前必须走人工审查(版本号 + bump 覆盖面)。
  • 版本面必须整体同步:npm 包、config/defaults.json(Docker/dev 脚本的 pin 源)、Helm Chart、README 镜像 pin 由 release-please 在一个 PR 中原子更新,任何一处遗漏都会造成 npm 与 Docker 安装路径版本漂移。
  • 发布链的信任根是组织 App tokenrelease: published 事件驱动后续流程,App token 缺失时 workflow 故意失败而不是静默降级。
  • 规则式防线:Release Tag ruleset 要求 tag commit 上 test-and-build (ubuntu) 为绿,release.ymlawait-required-checks job 与 npm-publish.yml 的版本一致性校验共同保证"未经 CI 验证的 commit 打不出 tag、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
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384