Cline 稳定版发布全流程解析:从 release 命令手册到 ext-vscode-publish-stable 工作流
本篇围绕 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)是帮助完成以下六件事:
- 选择/确认目标版本号;
- 面向终端用户人工整理
CHANGELOG.md条目; - 确保
package.json的版本号与 Changelog 一致; - 创建并推送发布提交 + tag;
- 触发发布工作流(publish workflow);
- 更新 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.json;hotfix-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.md与apps/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.md、package.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):
- 要求必须从
mainref 触发(WORKFLOW_REF == refs/heads/main); - 校验被测 SHA 必须位于
origin/main祖先链上; - 若 tag 已存在,其指向的 commit 必须恰好等于本次被测 SHA,否则报错;
- 若 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 的步骤:
- Checkout:
ref: main、fetch-tags: true、lfs: true; - Resolve Release Tag:如上所述;
- 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; - Install workspace dependencies:在仓库根执行
bun install --frozen-lockfile,一次解析整个 Bun workspace; - Build SDK packages:
bun run build:sdk,把@cline/*本地 workspace 包构建出dist/(扩展对这些包的依赖是源码符号链接,必须先构建); - Assert better-sqlite3 native binary present:检查
node_modules/better-sqlite3/build/Release/better_sqlite3.node存在,防止trustedDependencies的 postinstall 未执行导致原生模块缺失; - Install Publishing Tools:
npm install -g @vscode/vsce ovsx; - Get Version / Verify Tag Matches / Verify Changelog Entry / Verify Marketplace Tokens:如前所述的版本一致性双校验,外加检查
VSCE_PAT与OVSX_PAT两个 secrets 非空; - 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 则保留全文; - 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 两个渠道。
- Create GitHub Release:使用
softprops/action-gh-release@v1,tag_name为解析出的 tag,附件为apps/vscode/*.vsix,body 为截取的 Changelog 内容加prev_tag...tag的 Full Changelog compare 链接,prerelease标志取自release-type输入; - 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.json 中
release脚本指向 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.Ztag 指向当前 HEAD 且已推远端)。这与 VS Code 扩展的vX.Y.Ztag 体系是相互独立的命名空间。
关键校验点速查
把整条链路上的“防错闸门”汇总成一张表,便于发布前逐项自检:
| 校验 | 位置 | 失败条件 |
|---|---|---|
| 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 三者的一致性由工作流中的独立校验步骤保证。
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 StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00