rrweb Chrome 扩展发布流程简化设计:基于 Changesets 输出驱动的自动化发布与仪表盘手动恢复
【免费下载链接】rrwebrecord and replay the web项目地址: https://gitcode.com/gh_mirrors/rr/rrweb
导读
本文聚焦 rrweb 仓库中一份发布基础设施设计文档(docs/superpowers/specs/2026-07-22-chrome-extension-release-simplification-design.md),讲解如何在保留「npm 常规发布期间自动发布 Chrome 扩展」能力的同时,移除人工派发(manual dispatch)逻辑,将特权发布工作流收敛为仅由 push 触发、并以 Changesets 的published输出作为扩展构建与上传唯一开关的形态。读完本文,你将掌握该设计的完整工作流、被移除与保留的行为清单、仪表盘(Chrome Web Store developer dashboard)手动恢复路径,以及一套可自动执行的回归断言与验证命令,可直接对照 .github/workflows/release.yml 与 docs/releases/next-channel.md 落地。
背景与目标(Objective)
rrweb 仓库使用 Yarn Workspaces + Turborepo 的 monorepo 结构(见根目录 package.json 中的workspaces与turbo配置),其中 packages/web-extension/package.json 定义了@rrweb/web-extension私有包,用于构建、打包浏览器扩展。发布工作流由 GitHub Actions 承担,核心诉求是:
- 保持自动化:正常的 npm 发布过程中,Chrome 扩展也应随之自动发布到 Chrome Web Store;
- 移除复杂性:删除手工派发逻辑——
workflow_dispatch触发器、publish_chrome_extension输入、针对任意手工 ref 的 job 级 ref 守卫,以及 Chrome 构建/发布条件中基于事件名与事件输入的各类分支; - 保留恢复通道:通过 Chrome Web Store 开发者仪表盘(dashboard)保留一条文档化的手动恢复路径,避免扩展与 npm 版本脱节。
设计上刻意「收缩特权工作流」——凡是涉及发布权限的操作越少、越自动,越不易被人为误触,同时降低工作流的维护面。由于该改动只涉及发布基础设施与文档,不涉及任何已发布 npm 包的源码变更,因此按 Changesets 规范不需要新增 Changeset。
工作流设计:push-only + Changesets published 输出
触发器回归 push-only
设计目标是将.github/workflows/release.yml恢复为纯粹的 push 触发工作流,仅监听main与next两个长期分支:
on: push: branches: - main - next当前仓库中的 .github/workflows/release.yml 已呈现该最终形态:无workflow_dispatch、无publish_chrome_extension输入,同时保留了concurrency: ${{ github.workflow }}-${{ github.ref }}以确保同一分支上的并发运行被正确串行化。
唯一发布开关:steps.changesets.outputs.published
工作流使用官方changesets/action@v1执行「创建发布 PR 或发布到 npm」,并将该 step 标记为id: changesets。扩展的构建与上传步骤全部以steps.changesets.outputs.published == 'true'作为前置条件:
- name: Build Chrome Extension if: steps.changesets.outputs.published == 'true' run: NODE_OPTIONS='--max-old-space-size=4096' DISABLE_WORKER_INLINING=true yarn turbo run prepublish --filter=@rrweb/web-extension - name: Publish Chrome Extension if: steps.changesets.outputs.published == 'true' && github.ref == 'refs/heads/main' run: npx --yes chrome-webstore-upload-cli@4.0.1 --source ./packages/web-extension/dist/chrome.zip - name: Publish Next Chrome Extension if: steps.changesets.outputs.published == 'true' && github.ref == 'refs/heads/next' run: npx --yes chrome-webstore-upload-cli@4.0.1 --source ./packages/web-extension/dist/chrome.zip这段 YAML 的关键语义:
- 构建步骤只依赖
published == 'true',不区分分支——main与next的发布都需要先产出packages/web-extension/dist/chrome.zip; - 上传步骤在构建条件之上叠加
github.ref判断,实现稳定版与预发布版的分流:main上传到生产 listing,next上传到预发布 listing; - 构建命令中的
--filter=@rrweb/web-extension是 Turborepo 的过滤语法,只构建web-extension这一个包,避免全量构建拖慢发布链路。
上传客户端与凭据体系(保持不变)
上传继续使用钉死版本的chrome-webstore-upload-cli@4.0.1(API v2 客户端),通过npx --yes按需拉取。从 .github/workflows/release.yml 可看到两套完整的环境变量映射:
- 生产(main):扩展 ID 硬编码为
pdaldeopoccdhlkabbkcjmecmmoninhe,凭据使用CWS_PUBLISHER_ID、CWS_CLIENT_ID、CWS_CLIENT_SECRET、CWS_REFRESH_TOKEN四个 secrets; - 预发布(next):扩展 ID 使用
NEXT_CWS_EXTENSION_ID,凭据使用NEXT_CWS_PUBLISHER_ID、NEXT_CWS_CLIENT_ID、NEXT_CWS_CLIENT_SECRET、NEXT_CWS_REFRESH_TOKEN。
CWS_*与NEXT_CWS_*两套凭据必须严格隔离——nextlisting 的变更不得影响main使用的生产 listing。
被移除的手工派发行为
设计文档明确列出三块需要删除的内容,可作为审计清单:
workflow_dispatch触发器与publish_chrome_extension输入;- 为任意手工 ref 添加的 job 级 ref 守卫;
- Chrome 构建与发布条件中基于事件名(event-name)和事件输入(event-input)的分支。
删除后,工作流不再接受「手动选择 ref + 勾选发布扩展」的触发方式,扩展发布只能随 npm 发布自动发生,或走下述仪表盘手动恢复。
扩展如何被构建成 chrome.zip
要理解构建步骤的含义,需要看@rrweb/web-extension的脚本与 Vite 配置:
- packages/web-extension/package.json 中,
prepublish脚本为yarn build,而build依次执行pack:chrome与pack:firefox; pack:chrome为cross-env TARGET_BROWSER=chrome ZIP=true vite build;- packages/web-extension/vite.config.ts 通过
vite-plugin-web-extension动态生成 manifest,并用vite-plugin-zip-pack在ZIP === 'true'时把dist/chrome目录打包为dist/chrome.zip——这正是上传步骤引用的./packages/web-extension/dist/chrome.zip。
值得注意的版本联动逻辑也在这份配置里:扩展version并非独立维护,而是由getExtensionVersion()依据package.json中rrweb依赖版本计算得出(预发布版本号会剥离 prerelease 标识并拼接第 4 段数字,例如2.0.0.100这种特殊处理),因此「扩展版本必须与 npm 发布版本匹配」这一约束从构建阶段就已内建,手动恢复时核对 manifest 中的版本即是核对发布一致性。
手动恢复路径(Manual recovery)
自动链路(npm 发布 → 扩展上传)一旦中断——例如 npm 发布被单独恢复、但自动 Chrome 步骤未能发布对应扩展版本——就需要通过 Chrome Web Store 开发者仪表盘兜底。设计文档要求:
- 在 Chrome 构建步骤旁添加一行短注释,标明手动恢复入口;
- 注释链接到
docs/releases/next-channel.md中的恢复章节。
当前 .github/workflows/release.yml 中已存在该注释:
# Manual recovery: build and upload through the Chrome Web Store dashboard. # See docs/releases/next-channel.md#manual-chrome-extension-recovery.对应文档位于 docs/releases/next-channel.md 的## Manual Chrome extension recovery章节,恢复步骤如下:
1. 检出确切的发布提交
切到要发布的那个确切的main或next发布提交(而非分支最新 tip),确保扩展内容与 npm 发布内容一一对应。
2. 安装依赖并构建归档
yarn install --frozen-lockfile NODE_OPTIONS='--max-old-space-size=4096' \ DISABLE_WORKER_INLINING=true \ yarn turbo run prepublish --filter=@rrweb/web-extension unzip -t packages/web-extension/dist/chrome.zip--frozen-lockfile保证依赖与 lockfile 完全一致,杜绝漂移;NODE_OPTIONS='--max-old-space-size=4096'提高 Node 堆内存上限,避免大型构建 OOM;DISABLE_WORKER_INLINING=true禁用 worker 内联(该环境变量在仓库其他构建命令中同样出现,如根 package.json 的build:all脚本);unzip -t校验 ZIP 完整性,确保归档可被 Chrome Web Store 正常解析。
3. 核对 manifest 版本
确认packages/web-extension/dist/chrome/manifest.json中的version字段确实是目标发布版本——这是防止「npm 与扩展版本错位」的最后一道人工闸门。
4. 仪表盘上传
在 Chrome Web Store 开发者仪表盘中,main选择生产 listing,next选择预发布 listing,上传packages/web-extension/dist/chrome.zip并提交审核。生产与预发布 listing 必须保持隔离。
为什么是仪表盘而不是本地 CLI
设计文档明确说明:GitHub Actions secrets 无法被读回用于本地,因此本地运行chrome-webstore-upload-cli拿不到CWS_*/NEXT_CWS_*凭据,仪表盘上传便成为唯一受支持的手动恢复路径。这一取舍让「手动恢复」不扩大凭据暴露面,也无需为恢复场景引入新的密钥分发机制。
文档维护要求
恢复文档本身还有三条配套要求:
- 区分稳定版(
main)与预发布(next):分别说明生产 listing 与预发布 listing 的上传对象; - 列出相关 publisher ID secrets:
CWS_PUBLISHER_ID与NEXT_CWS_PUBLISHER_ID需出现在 docs/releases/next-channel.md 的凭据清单中(该文档的## Release workflow and Chrome extension章节已完整列出两套凭据); - 清理过时的
master分支引用:全部替换为main。该文档同时承担next通道的日常运维说明(.changeset/pre.json的 pre 模式维护、main与next之间 merge / cherry-pick 的边界、禁止把Version Packages (next)生成提交拷回main等),这些内容与扩展发布共同构成next通道的完整操作手册。
发布前的验证(Validation)
工作流回归断言
推送实现前,用 Node 内联脚本对release.yml做静态断言:YAML 可解析、手动触发痕迹(workflow_dispatch、publish_chrome_extension、github.event.inputs)全部不存在、自动发布闸门(steps.changesets.outputs.published == 'true')恰好出现 3 次(构建、main 上传、next 上传各一次)、API v2 CLI 与 publisher ID 变量仍然存在。设计文档中的完整断言等价于:
node -e "const fs=require('fs');const yaml=require('yaml');const s=fs.readFileSync('.github/workflows/release.yml','utf8');yaml.parse(s);for(const x of ['workflow_dispatch','publish_chrome_extension','github.event.inputs'])if(s.includes(x))throw new Error('manual release logic remains: '+x);if((s.match(/steps\.changesets\.outputs\.published == 'true'/g)||[]).length!==3)throw new Error('automatic publish gates changed');for(const x of ['chrome-webstore-upload-cli@4.0.1','CWS_PUBLISHER_ID','NEXT_CWS_PUBLISHER_ID'])if(!s.includes(x))throw new Error('missing '+x);console.log('push-only API v2 workflow assertions passed')"配套执行:
actionlint校验(GitHub Actions 专用 linter,可忽略 runner 过旧的告警);yarn prettier --check校验 YAML/文档格式;git diff --check检查空白错误。
文档断言
对 docs/releases/next-channel.md 断言:不存在master单词,且包含恢复章节标题、packages/web-extension/dist/chrome.zip构建产物路径、unzip -t完整性校验命令以及两个 publisher ID secrets。
构建产物验证
最后实际执行一次文档中的构建命令,并用unzip -t与 manifest 版本断言确认产物有效:
NODE_OPTIONS='--max-old-space-size=4096' DISABLE_WORKER_INLINING=true yarn turbo run prepublish --filter=@rrweb/web-extension unzip -t packages/web-extension/dist/chrome.zip node -e "const m=require('./packages/web-extension/dist/chrome/manifest.json');if(!m.version)throw new Error('manifest version missing');console.log('Chrome extension version:',m.version)"预期:构建退出码 0、unzip无错误、manifest 断言打印出版本号。
推送后检查
推送实现后,所有必需的 PR 检查必须通过;PR 描述需更新为同时描述「API v2 迁移、自动发布、仪表盘兜底」三件事;因移除手动派发而失效的历史评审线程,未经单独授权不得回复或 resolve——这属于对评审历史的保守处理,避免在自动化重构 PR 中夹带非授权的讨论性改动。
小结
该设计文档的价值在于一套「收缩但不减能力」的发布治理方案:push-only 触发器 + Changesetspublished输出把扩展发布严格绑定到成功完成的 npm 发布事件上,消除了人工误触发特权工作流的可能性;API v2 CLI 与双凭据体系保持稳定/预发布通道的自动分流;仪表盘手动恢复 + 完整文档在自动化失效时提供了不扩大凭据暴露面的兜底。而贯穿始终的回归断言(YAML 解析、手动痕迹检测、闸门计数、构建产物校验)使得这套约束本身也可被 CI 持续守护——这是任何拥有「发布即特权」场景的仓库都可以直接借鉴的工程实践。
相关阅读:
- 设计文档:docs/superpowers/specs/2026-07-22-chrome-extension-release-simplification-design.md
- 实现计划(含全部命令):docs/superpowers/plans/2026-07-22-chrome-extension-release-simplification.md
- 发布工作流:.github/workflows/release.yml
- 发布通道文档:docs/releases/next-channel.md
- 扩展包配置:packages/web-extension/package.json 与 packages/web-extension/vite.config.ts
【免费下载链接】rrwebrecord and replay the web项目地址: https://gitcode.com/gh_mirrors/rr/rrweb
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考