Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 != ''
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 …)
Expand Down
42 changes: 42 additions & 0 deletions docs/explorations/2026-11-03-playwright-cache-distro.md
Original file line number Diff line number Diff line change
@@ -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 也能起来才算闭环。
11 changes: 8 additions & 3 deletions test/unit/workflow-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 \}\}/,
);
});
});

Expand Down
Loading