Skip to content

Commit baeff45

Browse files
NERLOEclaude
andauthored
fix(build): playwright extension parses the Playwright 1.58+ install --dry-run output (#4881)
Fixes #3089 ## Problem The `playwright` build extension reads `npx playwright install --dry-run` to find each browser's install directory and download URL. Playwright 1.58 changed the per-browser header, so the extension's `grep` finds nothing and the image build fails at that step with exit code 1: ``` < 1.58 browser: chromium-headless-shell version 143.0.7499.4 >= 1.58 Chrome Headless Shell 151.0.7922.34 (playwright chromium-headless-shell v1234) ``` There is a second problem hiding behind the first. The old blocks were five lines (install location, download url, two fallback urls, blank), so `grep -A5` was exact. The Chrome-for-Testing blocks in 1.58+ (`chromium` and `chromium-headless-shell`, the default install) are three lines (install location, download url, blank; Firefox and WebKit still print the fallbacks), so `-A5` runs into the next browser's `Install location:` line. The downstream `grep "Install location:" | cut | xargs` then joins two paths and `basename` returns the *next* browser's directory: with the header grep alone fixed, `chromium-headless-shell` gets unpacked into `firefox-<build>/`. Any grep-only fix (including the pnpm patch shared in #3089) has this bug. ## Fix - The header match accepts both formats: `grep -E "browser: <name> |\(playwright <name> v"`. The trailing space / `v` keep `chromium` from matching the `chromium-headless-shell` block in either format. - The context window is narrowed to `-A2`, the two lines the extension actually reads. Both formats put `Install location:` and `Download url:` immediately after the header. - A unit test asserts the generated header pattern selects each browser's block in both formats and never another browser's. I left the `sed "s/mac-arm64/linux/g"` rewrite alone, and I do not think the "dead `linux64` URLs" report in #3089 is reachable from the extension. The `--dry-run` executes inside the Linux build container, so the URL it prints is already the Linux one and there is nothing for the rewrite to match. Checked by running the same command in a container: ``` $ docker run --rm --platform linux/amd64 node:22-bookworm-slim \ sh -c 'npx -y playwright@1.62.0 install --dry-run' Chrome Headless Shell 151.0.7922.34 (playwright chromium-headless-shell v1234) Download url: https://cdn.playwright.dev/builds/cft/151.0.7922.34/linux64/chrome-headless-shell-linux64.zip Firefox 153.0 (playwright firefox v1538) Download url: https://cdn.playwright.dev/dbazure/download/playwright/builds/firefox/1538/firefox-debian-12.zip ``` Rewriting `mac-arm64` to `linux64` would also be wrong for < 1.58, where the Linux artifact is `…-linux.zip`, so changing it would trade a dead branch for an incorrect one. ## Verification Ran the generated extraction (`grep -A2 -m1 -E … | grep "Install location:" | cut | xargs | basename`, and the same for `Download url:`) against real `--dry-run` output from Playwright 1.57.0 and 1.62.0, for all four browser keys: | output | browser | directory | url | |---|---|---|---| | 1.57 | chromium | chromium-1200 | chromium-linux.zip | | 1.57 | chromium-headless-shell | chromium_headless_shell-1200 | chromium-headless-shell-linux.zip | | 1.57 | firefox | firefox-1497 | firefox-linux.zip | | 1.57 | webkit | webkit-2227 | webkit-ubuntu-20.04.zip | | 1.62 | chromium | chromium-1234 | chrome-linux.zip | | 1.62 | chromium-headless-shell | chromium_headless_shell-1234 | chrome-headless-shell-linux.zip | | 1.62 | firefox | firefox-1538 | firefox-linux.zip | | 1.62 | webkit | webkit-2336 | webkit-mac-26-arm64.zip | (The webkit 1.62 URL shows the mac name only because my sample output came from a Mac; in the build container the dry-run prints Linux URLs.) End to end: built a `node:22-bookworm-slim` image (linux/amd64) that runs the extension's generated `RUN` steps verbatim for `chromium-headless-shell` against Playwright 1.62.0, then launched the browser through Playwright with `PLAYWRIGHT_BROWSERS_PATH=/ms-playwright`: ``` install dir: chromium_headless_shell-1234 Downloading from https://cdn.playwright.dev/builds/cft/151.0.7922.34/linux64/chrome-headless-shell-linux64.zip /ms-playwright/chromium_headless_shell-1234/chrome-headless-shell-linux64 title: pw-e2e-ok | version: 151.0.7922.34 ``` So the Chrome-for-Testing archive layout unpacks into the directory Playwright's registry expects, and the browser starts. ## Changeset `@trigger.dev/build` patch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
1 parent f151b7a commit baeff45

3 files changed

Lines changed: 60 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/build": patch
3+
---
4+
5+
The `playwright` build extension now works with Playwright 1.58 and later. 1.58 changed the `playwright install --dry-run` output, which made deploy image builds fail while downloading the browsers.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { describe, expect, it } from "vitest";
2+
import { dryRunHeaderPattern } from "./playwright.js";
3+
4+
// Real `playwright install --dry-run` headers, before and after the 1.58 format change.
5+
const HEADERS = {
6+
"1.57": {
7+
chromium: "browser: chromium version 143.0.7499.4",
8+
"chromium-headless-shell": "browser: chromium-headless-shell version 143.0.7499.4",
9+
firefox: "browser: firefox version 144.0.2",
10+
webkit: "browser: webkit version 26.0",
11+
},
12+
"1.62": {
13+
chromium: "Chrome for Testing 151.0.7922.34 (playwright chromium v1234)",
14+
"chromium-headless-shell":
15+
"Chrome Headless Shell 151.0.7922.34 (playwright chromium-headless-shell v1234)",
16+
firefox: "Firefox 153.0 (playwright firefox v1538)",
17+
webkit: "WebKit 26.5 (playwright webkit v2336)",
18+
},
19+
} as const;
20+
21+
const browsers = Object.keys(HEADERS["1.57"]) as Array<keyof (typeof HEADERS)["1.57"]>;
22+
23+
describe("playwright extension dry-run header pattern", () => {
24+
it.each(browsers)("selects the %s block in both output formats", (browser) => {
25+
const pattern = new RegExp(dryRunHeaderPattern(browser));
26+
27+
expect(pattern.test(HEADERS["1.57"][browser])).toBe(true);
28+
expect(pattern.test(HEADERS["1.62"][browser])).toBe(true);
29+
});
30+
31+
it.each(browsers)("does not select another browser's block for %s", (browser) => {
32+
const pattern = new RegExp(dryRunHeaderPattern(browser));
33+
34+
for (const other of browsers.filter((b) => b !== browser)) {
35+
expect(pattern.test(HEADERS["1.57"][other])).toBe(false);
36+
expect(pattern.test(HEADERS["1.62"][other])).toBe(false);
37+
}
38+
});
39+
});

packages/build/src/extensions/playwright.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,19 @@ export function playwright(options: PlaywrightExtensionOptions = {}) {
196196
return new PlaywrightExtension(options);
197197
}
198198

199+
/**
200+
* Extended regex selecting a browser's block header in `playwright install --dry-run` output.
201+
*
202+
* Playwright < 1.58 prints `browser: <name> version <v>`; 1.58+ prints
203+
* `<Product> <v> (playwright <name> v<build>)`. The trailing space / `v` keep
204+
* `chromium` from matching the `chromium-headless-shell` block.
205+
*
206+
* @internal
207+
*/
208+
export function dryRunHeaderPattern(browser: string): string {
209+
return `browser: ${browser} |\\(playwright ${browser} v`;
210+
}
211+
199212
/**
200213
* Background:
201214
*
@@ -317,7 +330,9 @@ class PlaywrightExtension implements BuildExtension {
317330

318331
Array.from(browsersToInstall).forEach((browser) => {
319332
instructions.push(
320-
`RUN grep -A5 -m1 "browser: ${browser}" /tmp/browser-info.txt > /tmp/${browser}-info.txt`,
333+
// Only the two lines after the header (install location, download url)
334+
// are read, so the window stops there.
335+
`RUN grep -A2 -m1 -E "${dryRunHeaderPattern(browser)}" /tmp/browser-info.txt > /tmp/${browser}-info.txt`,
321336

322337
`RUN INSTALL_DIR=$(grep "Install location:" /tmp/${browser}-info.txt | cut -d':' -f2- | xargs) && \
323338
DIR_NAME=$(basename "$INSTALL_DIR") && \

0 commit comments

Comments
 (0)