首页
/ Cline 稳定版发布全流程解析:从 release 命令手册到 ext-vscode-publish-stable 工作流

Cline 稳定版发布全流程解析:从 release 命令手册到 ext-vscode-publish-stable 工作流

2026-09-06 12:35:21作者:范靓好Udolf

本篇围绕 Cline 仓库中的发布操作手册 .claude/commands/release.md 展开,系统讲解稳定版从 main 分支出发、经版本确认、CHANGELOG 整理、提交打 tag、触发 CI 发布工作流,到更新 GitHub Release Notes 的完整链路;并结合 ext-vscode-publish-stable.yml 等 CI 源码,剖析其中每一步自动校验(tag 与版本号一致、Changelog 首条必须匹配当前版本等)的实现细节,帮助维护者既会用这套流程,也懂它为什么这样设计。

发布流程总览:六步走完全程

release 命令是一个写给 Agent/维护者执行的发布剧本,其自述目标(引自 release.md 的 Overview)是帮助完成以下六件事:

  1. 选择/确认目标版本号;
  2. 面向终端用户人工整理 CHANGELOG.md 条目;
  3. 确保 package.json 的版本号与 Changelog 一致;
  4. 创建并推送发布提交 + tag;
  5. 触发发布工作流(publish workflow);
  6. 更新 GitHub Release Notes 并给出最终总结。

这套流程的前提是:发布直接从 main 分支进行,而不是从 release 分支切出——这与同目录下的 hotfix-release.md 形成互补(hotfix 才涉及从旧 tag 上 cherry-pick)。

第一步:同步主干并确定版本

发布的第一动作是把 main 同步到最新,然后查看当前版本:

git checkout main
git pull origin main
cat package.json | grep '"version"'

需要与 maintainer 确认本次发布的版本级别:patch / minor / major。

这里有一个仓库层面的细节值得说明:Cline 是 monorepo,根 package.json@cline/packages(private,不含 version 字段),真正携带扩展版本号的是 VS Code 扩展包 apps/vscode/package.json,例如当前为 "version": "4.1.17"。发布校验(见下文)正是以该文件里的版本为准。同时,仓库当前使用 Bun workspace,锁文件是根目录的 bun.lock 而非 package-lock.jsonhotfix-release.md 中明确写道:单纯的 CHANGELOG + version 变更不会改动任何依赖,且 bun.lock 并不锁定 workspace 包版本,因此不需要bun install 重新生成锁文件——发布工作流里的 bun install --frozen-lockfile 会在锁文件不同步时直接失败,这是刻意设置的防错闸门。

第二步:整理 Changelog 并同步版本号

这一步是纯人工的“面向用户写作”,操作手册的要求有三条:

  • 用面向用户(human-friendly)的发布说明编辑 CHANGELOG.md 中目标版本的条目;
  • 版本标题必须使用方括号格式,例如 ## [3.66.1]
  • package.json 的版本号更新为同一个值。

对照仓库里的 CHANGELOG.md 可以看到这一约定的实际形态:最新条目是 ## [4.1.17],其下用 ### Added / ### Fixed / ### Changed 分节组织条目,且每条都是“用户能感知的行为变化”式的描述(例如“Fixed the background Hub process ballooning in memory during long sessions”),而不是“fix NPE in Foo.ts”这类内部口吻。

CI 对这一格式有硬性校验。 ext-vscode-publish-stable.yml 中的 Verify Changelog Entry 步骤会执行:

EXPECTED_HEADING="## [${{ steps.get_version.outputs.version }}]"
FIRST_HEADING=$(grep -m 1 '^## \[' CHANGELOG.md || true)
if [[ "$FIRST_HEADING" != "$EXPECTED_HEADING" ]]; then
  echo "Error: CHANGELOG.md must start with '$EXPECTED_HEADING' before publishing."
  exit 1
fi

也就是说,发布前 CHANGELOG.md第一条版本标题必须恰好等于 ## [<当前 package 版本>]。这意味着新版本条目必须插在文件最顶端,且版本号要与 apps/vscode/package.json 完全一致——这正是操作手册第 3 步“Ensure package.json version matches the changelog”的自动化兜底。

第三步:提交并打 tag

手册给出的标准提交与打 tag 序列:

git add CHANGELOG.md package.json package-lock.json
git commit -m "v<version> Release Notes"
git push origin main
git tag v<version>
git push origin v<version>

要点:

  • 提交信息固定为 v<version> Release Notes
  • tag 命名格式为 v<version>(如 v4.1.17),先推 main 再推 tag;
  • 结合上一步的锁文件讨论,当前仓库的等价操作是把变更文件(CHANGELOG.mdapps/vscode/package.json)提交推送即可,bun.lock 仅在真实改动依赖时随 bun install 一起更新。

tag 并非只是给人类看的标记,CI 会对其做严格解析。工作流中 Resolve Release Tag 步骤(ext-vscode-publish-stable.yml)包含以下校验逻辑:

if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$ ]]; then
  echo "Error: tag must match vX.Y.Z (optionally with -suffix or .suffix)"
  exit 1
fi

即 tag 必须匹配 vX.Y.Z,并允许 -suffix.suffix 形式的预发布后缀。随后工作流还会执行第二次版本一致性检查 Verify Tag Matches Package Version

TAG="${{ steps.resolve_tag.outputs.tag }}"
VERSION="v${{ steps.get_version.outputs.version }}"
if [[ "$TAG" != "$VERSION" ]]; then
  echo "Error: tag '$TAG' does not match package version '$VERSION'"
  exit 1
fi

两处校验(changelog 首标题、tag 与 package 版本)共同保证“三处版本一致”:CHANGELOG.mdpackage.json、git tag,任何一处不一致都会让发布在打包之前失败。

第四步:触发 publish 工作流

手册第 4 步是让 maintainer 手动触发发布工作流,并以 v<version> 作为 release tag。对应的仓库定义在 .github/workflows/ext-vscode-publish-stable.yml,它通过 workflow_dispatch 手动触发,提供三个输入:

输入 说明 默认值
release-type 选择 release(正式)或 pre-release(预发布) release
auto_create_tag_from_main 是否自动从“被测 main commit”创建并推送 tag(推荐) true
tag 要发布的 tag(两种模式下都必填,如 v3.1.2

自动建 tag 与手动建 tag 两种模式

auto_create_tag_from_main=true(推荐)时,工作流的行为是(源码见 ext-vscode-publish-stable.yml):

  1. 要求必须从 main ref 触发(WORKFLOW_REF == refs/heads/main);
  2. 校验被测 SHA 必须位于 origin/main 祖先链上;
  3. 若 tag 已存在,其指向的 commit 必须恰好等于本次被测 SHA,否则报错;
  4. 若 tag 不存在,则 git tag <TAG> <TESTED_SHA> 并以 github-actions[bot] 身份推送到远端。

auto_create_tag_from_main=false 时,则要求 tag 必须已存在且恰好指向本次被测 SHA(即操作手册第三步手动 git tag + git push 的路径)。两条路径殊途同归:发布内容永远锚定在一个“测试通过的 main commit”上,最后执行 git checkout --detach <TAG>^{commit} 让后续打包基于该 commit 的快照进行。

先测试,后发布

工作流分为两个 job(ext-vscode-publish-stable.yml):

jobs:
    test:
        uses: ./.github/workflows/ext-vscode-test.yml
    publish:
        needs: test
        ...
        environment: publish

test job 复用 ext-vscode-test.yml 这个可复用工作流(含变更检测、质量检查等 job),publish job 通过 needs: test 强制“测试通过才发布”,并且挂在名为 publish 的 GitHub Environment 下(可用环境保护规则做发布审批)。concurrency 组按 tag 命名(cancel-in-progress: false),防止同一 tag 并发发布。

publish job 内部关键步骤

按顺序看 ext-vscode-publish-stable.yml 中 publish job 的步骤:

  1. Checkoutref: mainfetch-tags: truelfs: true
  2. Resolve Release Tag:如上所述;
  3. Setup Bun / Node:Bun 1.3.14 + Node 22。源码注释解释了为何仍需 Node——发布脚本以 node scripts/publish-*.mjs 运行,且 npx ovsx 需要 npm;Node 22 是刻意 pin 住的,因为更新的 LTS(Node 24 / npm 11)会让 vsce 的 npm list 检测在打包时报 ELSPROBLEMS;
  4. Install workspace dependencies:在仓库根执行 bun install --frozen-lockfile,一次解析整个 Bun workspace;
  5. Build SDK packagesbun run build:sdk,把 @cline/* 本地 workspace 包构建出 dist/(扩展对这些包的依赖是源码符号链接,必须先构建);
  6. Assert better-sqlite3 native binary present:检查 node_modules/better-sqlite3/build/Release/better_sqlite3.node 存在,防止 trustedDependencies 的 postinstall 未执行导致原生模块缺失;
  7. Install Publishing Toolsnpm install -g @vscode/vsce ovsx
  8. Get Version / Verify Tag Matches / Verify Changelog Entry / Verify Marketplace Tokens:如前所述的版本一致性双校验,外加检查 VSCE_PATOVSX_PAT 两个 secrets 非空;
  9. Get Previous Tag + Get Changelog Entry:用 git tag --merged 找上一个版本 tag 生成 compare 链接;用 awk 截取 CHANGELOG.md## [<version>] 到下一个 ## [ 标题之间的内容作为 Release body。源码里还有一段值得注意的“事故驱动”注释:Slack section block 拒绝超过 3000 字符的文本,且 Slack action 丢弃超长消息时不会让步骤失败,所以工作流向 Slack 推送的是截断版并附“Read the full release notes”链接,GitHub Release body 则保留全文;
  10. Package and Publish Extension
node scripts/marketplace-readme.mjs swap-in
trap 'node scripts/marketplace-readme.mjs restore' EXIT

vsce package --no-dependencies --allow-package-secrets sendgrid \
  --out "cline-${VERSION}.vsix"

if [ "$RELEASE_TYPE" = "pre-release" ]; then
  bun run publish:marketplace:prerelease
else
  bun run publish:marketplace
fi

--no-dependencies 的原因在 publish-marketplace.mjs 的注释中写得很清楚:扩展已被 esbuild 完整打包成 dist/extension.js,若不禁止,vsce 会遍历 node_modules,把指向包外 monorepo 的 @cline/* 符号链接整个拖进 VSIX。README 换入(swap-in)同样是因为 vsce 只能读取扩展根目录的 README.md,发布脚本会先把 README.marketplace.md 换进来,退出时 restore 原文件,helper 是幂等的,CI 外层再包一层也不会互相破坏。

从源码结构看,publish:marketplace 最终调用 apps/vscode/scripts/publish-marketplace.mjs:先 vsce publish --no-dependencies --allow-package-secrets sendgrid(预发布时追加 --pre-release),再 npx ovsx publish --no-dependencies,即同时发布到 VS Code Marketplace 与 Open VSX 两个渠道。

  1. Create GitHub Release:使用 softprops/action-gh-release@v1tag_name 为解析出的 tag,附件为 apps/vscode/*.vsix,body 为截取的 Changelog 内容加 prev_tag...tag 的 Full Changelog compare 链接,prerelease 标志取自 release-type 输入;
  2. Post release to Slack:通过 Slack 机器人向频道推送版本号与(可能截断的)发布说明。

第五步:更新 GitHub Release Notes

发布完成后,操作手册建议人工润色 GitHub Release 的 body:

gh release view v<version> --json body --jq '.body'
gh release edit v<version> --notes "<final curated release notes>"

因为 CI 自动生成的 Release body 是机械截取 CHANGELOG 段落的产物,人工这一步负责把措辞调整到最终对外的口径,再回写。

第六步:最终总结

发布收尾时要向团队给出三样东西:

  • 已发布的版本/tag;
  • 发布页面链接;
  • 面向终端用户的核心变更摘要。

延伸阅读:仓库内与发布相关的其他通道

为完整起见,以下两条与稳定版发布并行的通道都可用仓库内文件直接查证(不属于本文主线,仅作索引):

  • Hotfix 流程.claude/commands/hotfix-release.md 描述了从 main 挑选指定 commit、先推 release notes 提交、再 cherry-pick 到最新 tag 之上、以 patch 版本号(如 3.40.0 → 3.40.1)打新 tag 的流程。它明确“不创建 release 分支,只用 tag”,cherry-pick 冲突需人工解决后继续,最后同样手动触发 ext-vscode-publish-stable 工作流。
  • SDK / CLI 发布:根 package.jsonrelease 脚本指向 sdk/scripts/release.ts,支持 bun release sdk [version](按 shared → llms → agents → core → sdk 依赖顺序发布 @cline/* 包,latest 渠道创建 sdk-vX.Y.Z 标签)与 bun release cli [version](要求 apps/cli/package.json 版本与 cli-vX.Y.Z tag 指向当前 HEAD 且已推远端)。这与 VS Code 扩展的 vX.Y.Z tag 体系是相互独立的命名空间。

关键校验点速查

把整条链路上的“防错闸门”汇总成一张表,便于发布前逐项自检:

校验 位置 失败条件
tag 格式 ext-vscode-publish-stable.yml 不匹配 vX.Y.Z(可选 -suffix/.suffix
tag 与 package 版本一致 同上 Verify Tag Matches Package Version v<package version> != tag
CHANGELOG 首标题匹配 同上 Verify Changelog Entry 首个 ## [ 标题 ≠ ## [当前版本]
锁文件一致 bun install --frozen-lockfile 依赖变更未更新 bun.lock
原生模块就位 Assert better-sqlite3 native binary present better_sqlite3.node 缺失
市场令牌 Verify Marketplace Tokens VSCE_PAT / OVSX_PAT 缺失
测试先行 test job(复用 ext-vscode-test.yml)+ needs: test 任意测试 job 失败

这套设计的核心思想可以概括为一句话:人工负责“写给人看的版本说明与版本号一致性”,CI 负责“用多重硬校验把不一致挡在打包之前”;发布永远锚定在测试通过的 main commit 上,tag 只是这个 commit 的可追溯别名,而 changelog、package 版本、tag 三者的一致性由工作流中的独立校验步骤保证。

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