diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 5827afa2e..b49ad9856 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -35,17 +35,21 @@ runs: BROWSERS: ${{ inputs.browsers }} run: | echo "version=$(pnpm exec playwright --version | awk '{ print $NF }')" >>"$GITHUB_OUTPUT" + echo "distro=$(. /etc/os-release && echo "$ID$VERSION_ID")" >>"$GITHUB_OUTPUT" echo "slug=${BROWSERS// /-}" >>"$GITHUB_OUTPUT" - # The browser binaries are the slow half of the install and they only ever - # change with the Playwright version, so a hit leaves just the apt step. + # The browser binaries are the slow half of the install. Playwright ships a + # separate build per distro release (WebKit links against that release's + # ICU), so the key carries the distro too: when ubuntu-latest moved to + # 26.04 a key without it restored the 24.04 WebKit, which cannot start + # there (libicudata.so.74 missing). - name: Restore the Playwright browser cache if: inputs.browsers != '' id: browser-cache uses: actions/cache@v6 with: path: ~/.cache/ms-playwright - key: playwright-${{ runner.os }}-${{ steps.playwright.outputs.version }}-${{ steps.playwright.outputs.slug }} + key: playwright-${{ runner.os }}-${{ steps.playwright.outputs.distro }}-${{ runner.arch }}-${{ steps.playwright.outputs.version }}-${{ steps.playwright.outputs.slug }} - name: Install Playwright browsers if: inputs.browsers != '' diff --git a/CLAUDE.md b/CLAUDE.md index 707ed37cb..d20494b0c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -434,7 +434,7 @@ apt-get,而 hosted runner 上的 apt 有过**拉完 release 索引后彻底停 每次挂在不同的 job(三个 e2e job 跑的是同一步,谁中枪是随机的),每次都要人工 重跑。apt 自身没有总时长上限,所以 `.github/scripts/install-playwright.sh` 给 每次尝试套 `timeout` 并重试,重试前清掉被杀的 apt 留下的锁;浏览器二进制 -按 Playwright 版本号进 `actions/cache`,命中时只跑 `install-deps`。 +按"发行版 + 架构 + Playwright 版本号"进 `actions/cache`(Playwright 每个 Ubuntu 版本一套构建,2026-11-03 漏了发行版,26.04 的机器取回 24.04 的 WebKit 起不来),命中时只跑 `install-deps`。 **缓存命中时直接不跑 apt**(2026-08-19 起)。读健康 run 的日志才看清 `install-deps` 到底在干什么:Chromium 要的库(libnss3 / libgbm1 / libasound2t64 / libcairo2 …) diff --git a/docs/explorations/2026-11-03-playwright-cache-distro.md b/docs/explorations/2026-11-03-playwright-cache-distro.md new file mode 100644 index 000000000..d3aa6fc65 --- /dev/null +++ b/docs/explorations/2026-11-03-playwright-cache-distro.md @@ -0,0 +1,42 @@ +# 夜间跨浏览器全红:浏览器缓存没带发行版 + +2026-11-03 的 Nightly corpus(run 41561076181)cross-browser 红:WebKit 三个分片 + serial +全部 1ms 失败,Firefox 分片 2 有 4 条失败。 + +## 根因(WebKit) + +日志里每条 WebKit 用例都是同一句: + +``` +MiniBrowser: error while loading shared libraries: libicudata.so.74: cannot open shared object file +``` + +- `ubuntu-latest` 从这周起指向 **ubuntu-26.04**(10-30 那次跨浏览器还是 24.04,全绿; + 中间两晚 gate 判定没有新提交、跨浏览器没跑,所以这是 26.04 上第一次跑)。 +- Playwright 1.64 对每个 Ubuntu 版本发一套独立构建(`coreBundle.js` 里有 + `ubuntu24.04-x64` / `ubuntu26.04-x64` 两套下载项),WebKit 链接的是那个版本自己的 ICU。 +- `.github/actions/setup` 的缓存 key 是 `playwright-Linux-1.64.0-webkit`——没有发行版。 + 26.04 的机器命中了 24.04 时存进去的缓存,拿到的是 24.04 的 WebKit,而 26.04 上没有 ICU 74。 +- 命中缓存时安装脚本直接 `exit 0`(不跑 apt,见 CLAUDE.md CI 一节),所以也没有任何一步会 + 发现版本对不上。 + +## 改动 + +- 缓存 key 加上 `/etc/os-release` 的 `$ID$VERSION_ID` 与 `runner.arch`: + `playwright-Linux-ubuntu26.04-X64-1.64.0-webkit`。换镜像就是冷缓存,走完整安装 + (含 `install-deps`),之后再命中的才是同一发行版的构建。 +- `test/unit/workflow-contract.test.ts` 钉住 key 里的发行版段。反向验证:只撤 + action.yml 的改动,这条用例变红;恢复后 68/68 绿。 + +## Firefox 那 4 条 + +`agent-interface-affordances.spec.ts:65`:`Tab` 再 `Shift+Tab` 之后 +"Import structured result" 按钮没拿到焦点(`inactive`),4 个视口/主题组合全挂、重试也挂。 +Firefox 能启动,但跑的同样是 24.04 的构建,跑在 26.04 上。是不是同一个原因,本地(macOS) +复现不了;以修复后在分支上手动触发一次 nightly(`skip_corpus`)的结果为准,若仍红再单独查。 + +## 还没验证的一点 + +缓存命中时不跑 apt,前提是 runner 镜像本来就带齐浏览器要的库——这是 24.04 上读日志得出的 +结论,26.04 上没验证过。验证方法:在分支上手动跑两次 nightly(第一次冷缓存,第二次命中), +第二次 WebKit 也能起来才算闭环。 diff --git a/test/unit/workflow-contract.test.ts b/test/unit/workflow-contract.test.ts index e6bd59b52..aae205647 100644 --- a/test/unit/workflow-contract.test.ts +++ b/test/unit/workflow-contract.test.ts @@ -105,10 +105,15 @@ describe('the shared setup action', () => { expect(action).not.toMatch(/run: pnpm exec playwright install/); }); - it('caches the browsers against the Playwright version, not the lockfile', () => { + it('caches the browsers against the distro and Playwright version, not the lockfile', () => { // Keying on the lockfile would evict the cache on every dependency bump - // and re-download the browsers for no reason. - expect(action).toMatch(/key: playwright-\$\{\{ runner\.os \}\}-\$\{\{ steps\.playwright\.outputs\.version \}\}/); + // and re-download the browsers for no reason. The distro is part of the + // key because Playwright builds per release: without it the 24.04 WebKit + // was restored onto the 26.04 runner and could not start. + expect(action).toMatch(/distro=\$\(\. \/etc\/os-release && echo "\$ID\$VERSION_ID"\)/); + expect(action).toMatch( + /key: playwright-\$\{\{ runner\.os \}\}-\$\{\{ steps\.playwright\.outputs\.distro \}\}-\$\{\{ runner\.arch \}\}-\$\{\{ steps\.playwright\.outputs\.version \}\}/, + ); }); });