4.2 KiB
Agent Note: Web 浏览器预期输出的必需 CI 门禁
Status: implemented
English | 中文
问题
无密钥 Web 浏览器 e2e 车道只由本地 pnpm run test:web 运行,PR CI 不比较 apps/web/tests/snapshots/**/*.expected.md。因此,改变用户可见 Web 输出的 PR 可以在漏刷预期输出时保持绿色;后来任意分支显式运行 DSH_SNAPSHOT=refresh,都会替前序变更补账并产生与本分支无关的 diff。普通本地运行已经默认使用只读 replay,缺口是 PR 级的强制执行,而不是禁止 refresh 写入。
决策
Linux PR 的 node 24 / snapshots and artifacts 必须运行完整 Web 浏览器 replay/compare。scripts/run-gates.ts 把 test:web:built 作为 ci-consumers 的一个 gate,并显式注入 DSH_SNAPSHOT=replay;CI 永不以 record 或 refresh 模式运行,因此提交的 golden 与当前组装应用不一致时测试直接失败,不会在 runner 内静默改写后通过。
消费方 job 在消费方独立构建中负责唯一一次 Linux 构建,因此 apps/web/dist 和包的 lib/ 目录会保留在其工作区中,供浏览器套件使用。在托管运行器上,CI 按锁文件中的 Playwright 版本安装 Chromium 及其系统依赖。在持久化故障切换 VM 上,镜像负责预装 Linux 系统软件包,CI 只安装 Chromium,避免每次运行都通过 apt 改动系统。托管的默认分支 Linux 串行 job 运行该套件,并生成以操作系统和锁文件为键的浏览器缓存;PR 恢复该缓存,使必需路径无需承担压缩和上传开销,并可在锁文件变化时按操作系统前缀回退。自托管热备运行相同的比较,但不执行托管缓存操作。
本地 pnpm run test:web 仍先构建再运行浏览器全集;test:web:built 是已有构建产物的执行入口。开发者只在确认用户可见输出有意变化后显式运行 DSH_SNAPSHOT=refresh pnpm run test:web,评审每一处 expected diff,再以 replay 模式复验不再写文件。
对 PR 而言,门禁仅在 Linux 消费方 job 中运行:这些场景面向 POSIX,其他 PR job 不供给 Chromium。托管和自托管的默认分支 Linux 串行聚合作业也包含该比较,而 macOS 和 Windows 串行 job 仍不使用浏览器。PR 的 all checks passed 已依赖消费方 job,因此浏览器比较失败会阻止合并,无需新增 branch-protection check 名称。
一次自托管消费方运行中,web-snapshot 实测耗时 112.15 秒,完整消费方聚合实测耗时 114.97 秒。gate 调度器会在 built-package-invariants 成功后立即启动它,并发运行彼此独立的 gate,因此既不需要专用 job 超时,也不需要手动制定 YAML 顺序规则。
曾考虑的替代方案
继续只要求本地运行。 已否决:执行依赖开发者记忆,正是旧 golden 跨 PR 漂移的原因,不能保证产生行为变化的 PR 自己携带 expected diff。
让 CI 以 refresh 模式运行后检查工作树。 已否决:写后比较把断言机制变成生成器,若工作树检查接线失效就会把回归更新成绿色;replay 直接比较已有 golden,失败面更小。
新建独立 browser job 并重新构建全仓。 已否决:它会重复依赖安装和发布构建。现有 Linux 消费方 job 已负责该构建,并已被统一的 required verdict 聚合。
用 jsdom 快照代替真实 Chromium。 已否决:jsdom 不覆盖浏览器、HTTP/SSE 承载及真实 client plugin bundle 组合;它保留为快速的下层反馈,不能替代 assembled browser chain。
后果
每个 PR 都在合并前证明当前 Web 组装与所有已提交的浏览器 expected 一致,漏刷从“后续 PR 的无关变化”变成引入 PR 自己的失败。成本是消费方 job 需要供给 Chromium,并串行运行一轮浏览器场景;消费方独立构建与浏览器缓存避免重跑时重复构建和下载。门禁仍不声称跨平台浏览器一致性,Playwright/Chromium 升级若改变 aria 格式,升级 PR 必须显式 refresh 并评审 churn。