Cline VS Code 扩展发布实践:A/B 组合 VSIX、灰度放量与紧急回滚全链路 Cline VS Code 扩展发布实践A/B 组合 VSIX、灰度放量与紧急回滚全链路【免费下载链接】clineAutonomous coding agent as an SDK, IDE extension, or CLI assistant.项目地址: https://gitcode.com/GitHub_Trending/cl/cline本文以 Cline 仓库中的扩展发布技能文档 SKILL.md 为主体完整讲解 Cline VS Code 扩展在“legacynpm 老架构→ SDKbun 新架构”迁移期的发布体系如何选择一个安全的版本号、如何用 PostHog 标志位控制灰度比例、如何分发 stable / nightly / legacy hotfix 三类构建以及出问题时的降级与回滚手段。读完后你将掌握一个“一个 VSIX 里装两个完整扩展、按机器灰度选择其一”的真实灰度发布方案的设计原理、操作命令与源码级实现细节。背景为什么是“组合 A/B VSIX”Cline 正处于从 legacy 扩展npm 工具链、预 SDK 时代向 next 扩展基于 SDK 的 bun 工具链位于main分支的apps/vscode/的迁移中途。VS Code Marketplace 没有分阶段发布能力——发布一个版本会立即推送给所有用户。为了在不过夜“一刀切”的情况下把 SDK 版扩展放到一部分用户手里Cline 在main与legacy-extension两个分支上各构建一个完整扩展再叠加一个约 40 KB 的加载器loader打成单个组合 VSIX由加载器按 PostHog 标志ext-sdk-bundle-rollout的百分比为每台机器在每个窗口中二选一。组合 VSIX 的内部结构见 apps/vscode-rollout/README.mdextension.js ← loader入口 package.json ← 两份 bundle 清单的 UNION构建时生成 assets/, walkthrough/ ← 清单引用的资源 next/ ← SDK 扩展构建自 main legacy/ ← legacy 扩展构建自 legacy-extension 分支最终目标Endgame当 next 束以 100% 灰度稳定运行足够久之后stable 通道回到对main的普通构建走 ext-vscode-publish-stable.yml退役全部 legacy/rollout 机制。四个发布通道与工作流矩阵通道Marketplace ID工作流触发方式版本号来源Stable组合saoudrizwan.claude-devext-vscode-ab-package.yml仅手动 dispatchpublish输入默认 false手动输入 semver如4.1.0Nightly组合saoudrizwan.cline-nightlyext-vscode-publish-nightly.yml手动 dispatch自动major.minor.unix 时间戳基于 main 的 apps/vscode/package.jsonLegacy hotfix独立saoudrizwan.claude-devext-vscode-publish-legacy.yml手动 dispatchlegacy-extension分支上apps/vscode/package.json的版本Stable standalone切换后saoudrizwan.claude-devext-vscode-publish-stable.yml手动 dispatchmain上apps/vscode/package.json的版本所有分发工作流都从main触发GitHub 要求工作流文件存在于默认分支各工作流再各自 checkout 真正要构建的 ref。所有发布路径在发布前都有测试门禁nightly 与 ab-package 运行可复用的 bun 测试套件 ext-vscode-test.yml测mainab-package 额外内联运行 legacy 分支的 npm 套件legacy 工作流则内联整条 npm 套件。环境门禁environment gate方面stable 路径挂publish环境需要必需审核人在 Actions 界面批准nightly 挂PublishNightly环境。需要注意当前仓库的一个最新变化nightly 工作流的 cron 触发已被刻意移除——因为PublishNightly环境后来加了必需审核人无人值守的 cron 会一直卡在waiting审批上、占住并发组连续吞掉后续排程工作流文件头注释记录了 2026-07-31 至 2026-08-21 间 20 次 nightly 被连坐取消的事故。因此现在 nightly 只能手动 dispatch详见工作流on:段注释。黄金法则任何发布前必读的五条规则1. 一个 listing一条单调递增的版本线claude-dev这个 Marketplace listing 被多个工作流、多个分支共同发布。版本在 Marketplace 上单调递增且不可下架只能被新版本顶掉不能删除。因此每次 stable 发布的版本必须严格大于该 listing 历史上从任何分支发布过的最高版本。选择版本前先查线上现值curl -s -X POST https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery \ -H Content-Type: application/json -H Accept: application/json;api-version3.0-preview.1 \ -d {filters:[{criteria:[{filterType:7,value:saoudrizwan.claude-dev}]}],flags:16} \ | python3 -c import json,sys; vjson.load(sys.stdin)[results][0][extensions][0][versions][0]; print(v[version], v[lastUpdated])这条规则在 ext-vscode-ab-package.yml 中被自动化了两遍preflight作业先校验版本格式纯X.Y.Z不带v前缀和后缀并硬失败于“不大于线上版本”publish作业在真正发布前再查一次——因为环境审批可能等待数天期间若有 legacy hotfix 落地第二次检查能拦住“旧代码线顶掉新代码线”的事故工作流中两处检查的注释明确提醒“Keep both copies of this check in sync”。但选版本时仍要自己跑一次上面的查询。2. 任何 stable 组合发布前先查灰度标志ext-sdk-bundle-rollout标志在 nightly 与 stable 之间共享加载器向/decide只发送机器 id不带通道属性因此不存在按通道的定向投放。如果标志当前为高值nightly 团队在吃狗粮你此时发布 stablestable 用户也会在同样比例上直接拿到 next 束。有效比例可以不加 PostHog 管理权限就实测——用任意已发布 loader 里内联的 PostHog key 采样/decidenode -e const KEY process.argv[1]; // phc_... 从已发布的 VSIX loader 中提取 (async () { let t 0, n 200; for (let i 0; i n; i 20) { const rs await Promise.all(Array.from({length: 20}, (_, j) fetch(https://data.cline.bot/decide?v3, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({api_key: KEY, distinct_id: probe-${ij}-${Math.random()}}) }).then(r r.json()))); for (const r of rs) if ((r.featureFlags||{})[ext-sdk-bundle-rollout] true) t; } console.log(~${(100*t/n).toFixed(1)}% (${t}/${n})); })() $KEY标志的修改在 PostHog 界面Cline 项目进行。0% 就是 kill switch——该标志是双向的没有单独的 killswitch 标志把比例调低后受影响机器在下次窗口重载时退回 legacy。3. 推送前先问人commit 和 tag 的推送、环境审批权限归维护者所有操作前必须先征得同意。4. Changelog 在仓库根目录Changelog 位于仓库根的 CHANGELOG.md且必须落在被发布的那个分支上——不是apps/vscode/CHANGELOG.md该文件不存在。legacy 与 stable 工作流都硬失败于“第一个标题不是## [version]”的情况。5. 卡住的并发组要手动清理ext-vscode-ab-package以版本号为并发组键ext-vscode-ab-package-${version}cancel-in-progress: false。只有publishtrue的运行需要等环境审批publishfalse的纯构建演练无门禁地跑完但一次停留在waiting状态的发布运行仍然会阻塞同版本之后的一切 dispatch——重新分发前先用gh run cancel id取消。Stable 发布组合 A/B VSIX当前主路径发布前检查Pre-flight# 1. 线上现值与下一版本必须大于现值——法则 1 # 2. 标志比例法则 2——决定本次发布时它应该停在什么位置 # 3. legacy 分支尖端 未晋升队列实际运行的代码确认它是已发布的 hotfix 线 git fetch origin main legacy-extension git log --oneline -3 origin/legacy-extension # 4. 最廉价地预演最可能的构建失败union manifest # 若 views/viewsContainers/configuration 在两分支间漂移会硬失败 git show origin/main:apps/vscode/package.json /tmp/next.json git show origin/legacy-extension:apps/vscode/package.json /tmp/legacy.json node apps/vscode-rollout/scripts/gen-manifest.mjs --next /tmp/next.json --legacy /tmp/legacy.json --version VERSION # 预期只有两条警告engines 取并集取较新值 walkthrough 文案漂移在main上的发布准备要走 PR不要直接 push在根 CHANGELOG.md 顶部添加## [VERSION]条目把 apps/vscode/package.json 提升到VERSION让仓库反映已发布线。副作用nightly 版本号会以新基准生成major.minor.unix-ts——无害独立 listing依然单调。分发Dispatchgh workflow run ext-vscode-ab-package.yml --ref main \ -f versionVERSION -f next-refmain -f publishtrue # legacy 束始终从受保护的 legacy-extension 分支构建刻意不作为输入 # publishfalse 只构建可安装的 .vsix 工件不发布、也无需任何环境审批 gh run list --workflowext-vscode-ab-package.yml --limit 1从 ext-vscode-ab-package.yml 的作业编排看整条流水线的顺序是preflight校验版本格式纯X.Y.Z、拒绝publishtrue且next-ref ! main的组合bun 测试门禁只覆盖 main非 main 的 next-ref 只允许纯构建演练、校验版本大于线上版本test-next复用 ext-vscode-test.yml 跑 bun 套件测的是 dispatch 时的 main 尖端test-legacycheckout 硬编码的legacy-extension分支内联 npm 套件lint typecheck →ci:build→test:unit→ 扩展集成测试 → webview 测试并输出tested-sha——构建作业会钉死到这个精确 revision绝不重解析分支名保证“测过什么就构建什么”build无环境门禁的打包作业。它先验证 changelog 首条仅publishtrue时、用bun install --frozen-lockfile安装 next 依赖、bun run build:sdk构建cline/*工作区包、断言better-sqlite3原生二进制存在、用set-version.mjs把组合版本戳进两个 bundle 的package.jsonAbout 页和遥测都读 bundle 自己的清单、以CLINE_ROLLOUT_VARIANT: next|legacy环境变量分别构建两束该变量被 esbuild 内联给所有遥测事件打上extension_variant、构建 loader 并跑 smoke-loader.mjs 冒烟测试、stitch.mjs拼装 staging 目录、断言 manifest 身份saoudrizwan.claude-devVERSION两个子清单版本一致最后vsce package出.vsix工件publish仅publishtrue挂publish环境等待审批然后再次核对版本单调性先发 Marketplacevsce publish两个 PAT 在任何不可逆发布前都先被校验避免“发了一半”、再发 Open VSXovsx publish。查看某个运行在等什么gh api repos/cline/cline/actions/runs/run-id/pending_deployments发布后Post-publish验证 Marketplace 已提供新版本法则 1 的查询——“Published”出现在日志后Marketplace 校验可能滞后几分钟到一小时。同时验证 Open VSXcurl -s https://open-vsx.org/api/saoudrizwan/claude-dev | python3 -c import json,sys; djson.load(sys.stdin); print(d[version], d[timestamp])Tag、GitHub Release附 .vsix、Slack release-bot 帖子都是自动的且全部continue-on-error——因为发布本身已成功记账失败不影响运行变绿这些步骤以 Marketplace 发布结果为门控而非步骤顺序。但已知一个会失败的点当被构建的 commit 触碰了.github/workflows/**时默认 token 无法创建refs/tags/v*这类 ref无权限可授予能修复它tag 推送会失败。手动兜底git tag vVERSION main-sha-built # 推送前先问 git push origin vVERSION gh release create vVERSION --title vVERSION --notes changelog 对应段落 path-to.vsix另外若被构建的 main revision 上根CHANGELOG.md不以## [VERSION]开头真实发布会在 build 阶段早失败——发布准备 PR 必须先合入再分发。工件深度体检gh run download run-idunionpackage.json是saoudrizwan.claude-devVERSIONnext/package.json与legacy/package.json携带相同版本grep -c phc_ extension/extension.js≥ 1loader 的 PostHog key 已内联两束 dist 中都不允许残留process.env.TELEMETRY_SERVICE_API_KEY/process.env.CLINE_ROLLOUT_VARIANT字面量残留意味着该次构建缺了对应环境变量遥测会静默失效。监控在otel.otel_logs中过滤extension_version VERSION看extension.rollout.bundle_activatedstable 队列可干净切分——nightly 版本是时间戳关注 next/legacy 比例与崩溃回退率Metabase 仪表盘 17rollout 任务错误率19错误深潜。extension.rollout.loader_decision含double_failure只在 PostHog不在 ClickHouse。按放量计划调标志例如 0% 发布 → 1% → 逐步上调每次改动后用法则 2 的探针复核。提前宣布降档——调低比例也会把 nightly 狗粮用户降级除非他们设置了cline-nightly.rollout.bundleOverride: next。该路径的已知局限engines.vscode取并集向上取main 的地板生效例如^1.101.0对 legacy 的^1.84.0老版本 VS Code 用户根本不会被提供组合 VSIX。这是 rollout 期间的安全失败fail-safe但必须在 100% 之前解决红红叉运行不等于发布失败任何会推 tag 的路径上tag-push 步骤失败会让整条运行变红但发布可能早已成功——看日志里有没有 “Published”。Nightly 发布当前仓库中的 nightly 是手动 dispatchcron 已移除见上文说明gh workflow run ext-vscode-publish-nightly.yml --ref main # 真实发布 gh workflow run ext-vscode-publish-nightly.yml --ref main -f dry-runtrue # 只出工件 gh run watch run-id --exit-status --interval 60nightly 不需要 changelog/版本准备——版本是算出来的major.minor.unix-seconds基于 dispatch 时 next 的package.json基准版本工作流中的 “Compute nightly version” 步骤因此天然持续压过先前所有 nightly。构建前 nightlify.mjs 会把两束清单改写成 nightly 身份——与独立 nightly 历来应用的改写完全相同两分支的apps/vscode/scripts/publish-nightly.mjs是源头真相stablenightly清单nameclaude-devcline-nightly贡献 ID / context key / 设置前缀cline.*cline-nightly.*版本操作者提供4.1.0major.minor.unix-seconds由于身份不同nightly 可以与 stable 同时安装。nightly 构建还会在状态栏显示Cline: Next/Cline: Legacy指示器stable 构建从不显示见 extension.ts 的showNightlyBundleIndicator。发布作业被工作流自身与PublishNightly环境的部署分支策略双重限定为只接受main。验证方式用 Marketplace 查询对saoudrizwan.cline-nightly执行同样的版本核对。红运行 ≠ 发布失败终末 tag-push 步骤在 main 尖端触碰.github/workflows/**时必然失败若日志出现 “Published” 即发布成功此时用你自己的凭据手动推nightly-main-UTC ts-sha12tag。Legacy hotfix 与紧急回滚这条路径有两个用途在legacy-extension分支上发布修复以及作为结构性回滚——从有问题的组合 stable VSIX 全身撤退一个更高版本的独立 legacy 发布会整体顶掉组合 VSIX连同 loader对所有用户生效。注意“next 束行为异常”不需要走这条路——把标志调到 0% 即可。# 在 legacy-extension 分支上提交修复把 apps/vscode/package.json 提升到 # 该 listing 历史上发布过的最高版本之上法则 1——包括组合版本 # 例如线上组合版 4.1.0 → hotfix 应为 4.1.1而不是 4.0.13 # 在根 CHANGELOG.md 加对应的 ## [x.y.z] 条目推送。 gh workflow run ext-vscode-publish-legacy.yml --ref main \ -f release-typerelease # 分支在 workflow 中硬编码为 legacy-extension刻意不作为输入从 ext-vscode-publish-legacy.yml 看npm 测试套件在无门禁阶段运行绝不带着 write token 执行被 checkout 的代码publish 作业才升到contents: write并挂publish环境该工作流自行推导并推送vversiontag、创建 GitHub Release——无需手动打 tag同时发布 Marketplace 与 Open VSX。注意该分支是 npm 代码库3.89.x 代码按 4.0.x 版本前滚用npm永远不要用bun且预期的是老单体布局apps/vscode/src/core/...。Cutover退役 A/B 机制的终局当 next 束在 100% 下稳定运行足够久后解决 engines 地板决定让 VS Code 低于 main 的engines.vscode的用户停留在最后一个组合版本是否可接受或先把 main 的地板降下来把main的 apps/vscode/package.json 提升到高于一切已发布版本根 CHANGELOG.md 条目匹配两者都被工作流强制从 main 发独立版本gh workflow run ext-vscode-publish-stable.yml --ref main——它测 main、自行打vversiontag、创建 GitHub Release、发布 Marketplace Open VSX过渡期继续盯同样的 rollout 遥测——extension_variant随用户离开组合构建而从事件中消失这本身就是采纳信号只有当独立版本成为主流之后退役legacy-extension分支保留作历史、删除 ext-vscode-publish-legacy.yml 与 ext-vscode-ab-package.yml、把 nightly 工作流改回 main 的普通构建、删除apps/vscode-rollout/目录并在 PostHog 归档ext-sdk-bundle-rollout标志。注意归档时机loader 把被删除的标志当作 legacy所以标志要保持在 100%直到组合 VSIX 的激活量归零再归档对仍跑着组合 VSIX 的机器无害——标志缺失只是让它们维持现状直到更新更新技能文档删除组合时代章节保留独立发布流程。源码级深潜loader 是怎么做的以下实现细节可在apps/vscode-rollout/包中逐行核对它们解释了上文操作规则背后的工程原因。决策逻辑同步、离线、可强制cohort.ts 中的decideBundle是整个加载器的核心优先级链为CLINE_BUNDLE_OVERRIDE 环境变量 prefix.rollout.bundleOverride 设置 本 VSIX 版本曾崩溃FAILED_VERSION_STATE_KEY legacy 上一窗口后台刷新缓存的分配COHORT_STATE_KEY legacy它被要求同步执行、绝不阻塞网络——只消费上一窗口后台刷新缓存的状态因此百分比变更在下次窗口重载时才生效刻意模仿 VS Code 自身实验的语义。parseRolloutAssignment的解析刻意严格只有字面布尔true才晋升到 next——多变量变体字符串、数字、payload、缺失/已删除的标志全部安全失败到 legacy。这就是“标志必须保持布尔发布标志”这条操作纪律的源码依据。后台刷新与身份一致性rollout.ts 的refreshCohort在选定的 bundle 成功激活后以 10 秒超时向https://data.cline.bot/decide?v3发一次探测只缓存下一窗口的分配永远不翻转已激活的窗口任何失败网络错误、无 key 的本地构建都让缓存保持不动sticky on transient failures。distinct id 的推导镜像了扩展遥测的实现优先读共享的~/.cline/data/globalState.json中的cline.generatedMachineId否则 machine-id再退回vscode.env.machineId保证灰度队列成员可以与遥测行为在仪表盘上关联——这正是“loader 只发机器 id、无通道属性”的由来。崩溃自愈与路径代理extension.ts 的activateBundle如果 next 束激活时抛异常loader 会dispose掉半注册的 subscriptions、把本 VSIX 版本钉回 legacycline.rollout.nextActivationFailedVersion、上报fallback遥测然后激活 legacy——一次崩溃的 rollout无需 Marketplace 重新发布即可自愈而下一个新版本又可以再试一次 next。fallback 路径跳过后台刷新防止它把刚钉死的机器重新晋升回去若 legacy 也失败double_failureloader 直报事件是仅存的记录。scoped-context.ts 用一个 Proxy 包裹ExtensionContext只重定向extensionUri/extensionPath/asAbsolutePath到vsix 根/next|legacy/子目录让每束从自己的子树解析 webview 构建和资源存储相关属性globalState、secrets 等原样穿透——两束继续共享独立扩展时代用过的同一份~/.cline/data与 VS Code 存储用户状态在队列切换和 VSIX 升级中存活。这就是“降级不丢数据、SDK 会话在重新晋升后重现”的实现基础。Union manifest 的生成规则VS Code 在任何代码运行前就静态读取package.json贡献因此 shipped 清单必须同时服务两个队列。gen-manifest.mjs 在 stitch 时从两分支的真实清单重新生成它两侧都声明的贡献原样通过只在一侧声明的菜单项/键绑定被 AND 上cline.sdkBundle/!cline.sdkBundle的when条件views/viewsContainers/configuration/walkthroughs必须逐字相同无法在运行时安全门控漂移即构建失败——这就是 pre-flight 那步本地预演存在的理由。engines允许漂移并集取较新要求。易错点索引Gotchasinputs.*在schedule事件上是空字符串——编辑 nightly 工作流时保留|| default兜底当前文件里 legacy-ref 就带着这个兜底注释解释是为未来可能恢复非 dispatch 触发时保持正确在apps/vscode里直接bun run package不会构建cline/*工作区依赖——全新 checkout 需要先bun run build:sdk工作流已处理workflow YAML 里 job 级if:分支检查只是建议性的dispatch 的分支运行的是它自己那份文件副本真正强制的边界是仓库设置里每个环境的部署分支策略Marketplace PATVSCE_PAT/OVSX_PAT只挂载到发布步骤两个发布工作流都没有不可信触发面等待环境审批的运行不会很快超时——可以挂着好几天并且对 ab-package 的发布运行会阻塞其版本号的并发组本地手动测试的强制开关CLINE_BUNDLE_OVERRIDEnext|legacy环境变量从终端全新启动 VS Code或prefix.rollout.bundleOverride设置 重载窗口两者在遥测中都上报为override不会污染队列数据。关键文件索引内容路径发布技能文档本文主体.cline/skills/publish-extension/SKILL.mdloader 设计与 rollout runbookapps/vscode-rollout/README.md组合 stable 打包工作流.github/workflows/ext-vscode-ab-package.ymlnightly 工作流.github/workflows/ext-vscode-publish-nightly.ymllegacy hotfix 工作流.github/workflows/ext-vscode-publish-legacy.yml独立 stable 工作流cutover 后.github/workflows/ext-vscode-publish-stable.yml决策/常量/解析逻辑apps/vscode-rollout/src/cohort.ts后台刷新与 loader 遥测apps/vscode-rollout/src/rollout.tsloader 入口与崩溃回退apps/vscode-rollout/src/extension.ts路径代理apps/vscode-rollout/src/scoped-context.tsunion manifest / nightly 改写 / 拼装 / 冒烟脚本apps/vscode-rollout/scripts根 changelogCHANGELOG.md本地复现整条打包链路摘自 apps/vscode-rollout/README.md# 1. 用各自工具链构建两束 cd apps/vscode bun run package # next cd legacy worktree/apps/vscode npm run package # legacy先 npm ci # 2. 构建 loader、拼装、打包 cd apps/vscode-rollout bun run build # 开发构建CI 用 build:production 注入 PostHog key node scripts/stitch.mjs \ --next ../vscode --legacy legacy worktree/apps/vscode \ --loader dist/extension.js --version 4.1.0 --out /tmp/cline-ab-staging node scripts/smoke-loader.mjs /tmp/cline-ab-staging cd /tmp/cline-ab-staging vsce package --no-dependencies --allow-package-secrets sendgrid本地构建没有TELEMETRY_SERVICE_API_KEYloader 会完全跳过 PostHog所有人留在 legacy除非设置CLINE_BUNDLE_OVERRIDE——这也是“遥测键必须内联进 loader”这一体检项存在的意义。【免费下载链接】clineAutonomous coding agent as an SDK, IDE extension, or CLI assistant.项目地址: https://gitcode.com/GitHub_Trending/cl/cline创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考