@@ -10,12 +10,173 @@ interface PlaywrightExtensionOptions {
10
10
browsers ?: PlaywrightBrowser [ ] ;
11
11
12
12
/**
13
- * Whether to support non- headless mode.
13
+ * Run the browsers in headless mode (Recommended)
14
14
* @default true
15
15
*/
16
16
headless ?: boolean ;
17
17
}
18
18
19
+ /**
20
+ * This list is from the official playwright registry.
21
+ *
22
+ * @see https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/registry/nativeDeps.ts
23
+ */
24
+ const debian12Deps = {
25
+ tools : [
26
+ "xvfb" ,
27
+ "fonts-noto-color-emoji" ,
28
+ "fonts-unifont" ,
29
+ "libfontconfig1" ,
30
+ "libfreetype6" ,
31
+ "xfonts-scalable" ,
32
+ "fonts-liberation" ,
33
+ "fonts-ipafont-gothic" ,
34
+ "fonts-wqy-zenhei" ,
35
+ "fonts-tlwg-loma-otf" ,
36
+ "fonts-freefont-ttf" ,
37
+ ] ,
38
+ chromium : [
39
+ "libasound2" ,
40
+ "libatk-bridge2.0-0" ,
41
+ "libatk1.0-0" ,
42
+ "libatspi2.0-0" ,
43
+ "libcairo2" ,
44
+ "libcups2" ,
45
+ "libdbus-1-3" ,
46
+ "libdrm2" ,
47
+ "libgbm1" ,
48
+ "libglib2.0-0" ,
49
+ "libnspr4" ,
50
+ "libnss3" ,
51
+ "libpango-1.0-0" ,
52
+ "libx11-6" ,
53
+ "libxcb1" ,
54
+ "libxcomposite1" ,
55
+ "libxdamage1" ,
56
+ "libxext6" ,
57
+ "libxfixes3" ,
58
+ "libxkbcommon0" ,
59
+ "libxrandr2" ,
60
+ ] ,
61
+ firefox : [
62
+ "libasound2" ,
63
+ "libatk1.0-0" ,
64
+ "libcairo-gobject2" ,
65
+ "libcairo2" ,
66
+ "libdbus-1-3" ,
67
+ "libdbus-glib-1-2" ,
68
+ "libfontconfig1" ,
69
+ "libfreetype6" ,
70
+ "libgdk-pixbuf-2.0-0" ,
71
+ "libglib2.0-0" ,
72
+ "libgtk-3-0" ,
73
+ "libharfbuzz0b" ,
74
+ "libpango-1.0-0" ,
75
+ "libpangocairo-1.0-0" ,
76
+ "libx11-6" ,
77
+ "libx11-xcb1" ,
78
+ "libxcb-shm0" ,
79
+ "libxcb1" ,
80
+ "libxcomposite1" ,
81
+ "libxcursor1" ,
82
+ "libxdamage1" ,
83
+ "libxext6" ,
84
+ "libxfixes3" ,
85
+ "libxi6" ,
86
+ "libxrandr2" ,
87
+ "libxrender1" ,
88
+ "libxtst6" ,
89
+ ] ,
90
+ webkit : [
91
+ "libsoup-3.0-0" ,
92
+ "gstreamer1.0-libav" ,
93
+ "gstreamer1.0-plugins-bad" ,
94
+ "gstreamer1.0-plugins-base" ,
95
+ "gstreamer1.0-plugins-good" ,
96
+ "libatk-bridge2.0-0" ,
97
+ "libatk1.0-0" ,
98
+ "libcairo2" ,
99
+ "libdbus-1-3" ,
100
+ "libdrm2" ,
101
+ "libegl1" ,
102
+ "libenchant-2-2" ,
103
+ "libepoxy0" ,
104
+ "libevdev2" ,
105
+ "libfontconfig1" ,
106
+ "libfreetype6" ,
107
+ "libgbm1" ,
108
+ "libgdk-pixbuf-2.0-0" ,
109
+ "libgles2" ,
110
+ "libglib2.0-0" ,
111
+ "libglx0" ,
112
+ "libgstreamer-gl1.0-0" ,
113
+ "libgstreamer-plugins-base1.0-0" ,
114
+ "libgstreamer1.0-0" ,
115
+ "libgtk-4-1" ,
116
+ "libgudev-1.0-0" ,
117
+ "libharfbuzz-icu0" ,
118
+ "libharfbuzz0b" ,
119
+ "libhyphen0" ,
120
+ "libicu72" ,
121
+ "libjpeg62-turbo" ,
122
+ "liblcms2-2" ,
123
+ "libmanette-0.2-0" ,
124
+ "libnotify4" ,
125
+ "libopengl0" ,
126
+ "libopenjp2-7" ,
127
+ "libopus0" ,
128
+ "libpango-1.0-0" ,
129
+ "libpng16-16" ,
130
+ "libproxy1v5" ,
131
+ "libsecret-1-0" ,
132
+ "libwayland-client0" ,
133
+ "libwayland-egl1" ,
134
+ "libwayland-server0" ,
135
+ "libwebp7" ,
136
+ "libwebpdemux2" ,
137
+ "libwoff1" ,
138
+ "libx11-6" ,
139
+ "libxcomposite1" ,
140
+ "libxdamage1" ,
141
+ "libxkbcommon0" ,
142
+ "libxml2" ,
143
+ "libxslt1.1" ,
144
+ "libatomic1" ,
145
+ "libevent-2.1-7" ,
146
+ "libavif15" ,
147
+ ] ,
148
+ lib2package : {
149
+ "libavif.so.15" : "libavif15" ,
150
+ "libsoup-3.0.so.0" : "libsoup-3.0-0" ,
151
+ "libasound.so.2" : "libasound2" ,
152
+ "libatk-1.0.so.0" : "libatk1.0-0" ,
153
+ "libatk-bridge-2.0.so.0" : "libatk-bridge2.0-0" ,
154
+ "libatspi.so.0" : "libatspi2.0-0" ,
155
+ "libcairo.so.2" : "libcairo2" ,
156
+ "libcups.so.2" : "libcups2" ,
157
+ "libdbus-1.so.3" : "libdbus-1-3" ,
158
+ "libdrm.so.2" : "libdrm2" ,
159
+ "libgbm.so.1" : "libgbm1" ,
160
+ "libgio-2.0.so.0" : "libglib2.0-0" ,
161
+ "libglib-2.0.so.0" : "libglib2.0-0" ,
162
+ "libgobject-2.0.so.0" : "libglib2.0-0" ,
163
+ "libnspr4.so" : "libnspr4" ,
164
+ "libnss3.so" : "libnss3" ,
165
+ "libnssutil3.so" : "libnss3" ,
166
+ "libpango-1.0.so.0" : "libpango-1.0-0" ,
167
+ "libsmime3.so" : "libnss3" ,
168
+ "libX11.so.6" : "libx11-6" ,
169
+ "libxcb.so.1" : "libxcb1" ,
170
+ "libXcomposite.so.1" : "libxcomposite1" ,
171
+ "libXdamage.so.1" : "libxdamage1" ,
172
+ "libXext.so.6" : "libxext6" ,
173
+ "libXfixes.so.3" : "libxfixes3" ,
174
+ "libxkbcommon.so.0" : "libxkbcommon0" ,
175
+ "libXrandr.so.2" : "libxrandr2" ,
176
+ "libgtk-4.so.1" : "libgtk-4-1" ,
177
+ } ,
178
+ } ;
179
+
19
180
/**
20
181
* Creates a Playwright extension for trigger.dev
21
182
* @param options Configuration options
@@ -24,6 +185,23 @@ export function playwright(options: PlaywrightExtensionOptions = {}) {
24
185
return new PlaywrightExtension ( options ) ;
25
186
}
26
187
188
+ /**
189
+ * Background:
190
+ *
191
+ * Running `npx playwright install --with-deps` normally will install the browsers and the dependencies.
192
+ * However, this is not possible in a build context, because we don't have sudo access.
193
+ *
194
+ * So we need to install the dependencies manually and then download and install the browsers.
195
+ * This has a few challenges:
196
+ * 1. We don't want to download all browsers, only the ones we need with it's dependencies
197
+ * The less dependencies we have to install, the faster the build, and the smaller the image.
198
+ * 2. We need to know where to download the browsers from
199
+ * while we can hardcode the download url it might change over time (as it has in the past)
200
+ * so we need to download the browser info first and then parse the output to get the download url.
201
+ *
202
+ * Note: While this looks like we are downloading & installing a lot of stuff, it's actually not that bad
203
+ * since running `npx playwright install --with-deps` will result in the same amount of downloads.
204
+ */
27
205
class PlaywrightExtension implements BuildExtension {
28
206
public readonly name = "PlaywrightExtension" ;
29
207
private readonly options : Required < PlaywrightExtensionOptions > ;
@@ -43,7 +221,7 @@ class PlaywrightExtension implements BuildExtension {
43
221
) ;
44
222
45
223
const instructions : string [ ] = [
46
- // Base dependencies
224
+ // Base dependencies, we need these to download the browsers
47
225
`RUN apt-get update && apt-get install -y --no-install-recommends \
48
226
curl \
49
227
unzip \
@@ -57,90 +235,26 @@ class PlaywrightExtension implements BuildExtension {
57
235
`RUN npm install -g playwright` ,
58
236
] ;
59
237
60
- // Browser-specific dependencies
61
- const chromiumDeps = [
62
- "libnspr4" ,
63
- "libatk1.0-0" ,
64
- "libatk-bridge2.0-0" ,
65
- "libatspi2.0-0" ,
66
- "libasound2" ,
67
- "libnss3" ,
68
- "libxcomposite1" ,
69
- "libxdamage1" ,
70
- "libxfixes3" ,
71
- "libxrandr2" ,
72
- "libgbm1" ,
73
- "libxkbcommon0" ,
74
- ] ;
75
-
76
- const firefoxDeps = [
77
- "libgtk-3.0" ,
78
- "libgtk-4-1" ,
79
- "libgtk-4-common" ,
80
- "libgtk-4-dev" ,
81
- "libgtk-4-doc" ,
82
- "libasound2" ,
83
- ] ;
84
-
85
- const webkitDeps = [
86
- "libenchant-2-2" ,
87
- "libgl1" ,
88
- "libgles2" ,
89
- "libgstreamer-gl1.0-0" ,
90
- "libgstreamer-plugins-base1.0-0" ,
91
- "libgstreamer-plugins-bad1.0-0" ,
92
- "libharfbuzz-icu0" ,
93
- "libhyphen0" ,
94
- "libicu72" ,
95
- "libjpeg-dev" ,
96
- "libopenjp2-7" ,
97
- "libopus0" ,
98
- "libpng-dev" ,
99
- "libsecret-1-0" ,
100
- "libvpx7" ,
101
- "libwebp7" ,
102
- "libwoff1" ,
103
- "libx11-6" ,
104
- "libxcomposite1" ,
105
- "libxdamage1" ,
106
- "libxrender1" ,
107
- "libxt6" ,
108
- "libgtk-4-1" ,
109
- "libgraphene-1.0-0" ,
110
- "libxslt1.1" ,
111
- "libevent-2.1-7" ,
112
- "libmanette-0.2-0" ,
113
- "libwebpdemux2" ,
114
- "libwebpmux3" ,
115
- "libatomic1" ,
116
- "libavif15" ,
117
- "libx264-dev" ,
118
- "flite" ,
119
- "libatk1.0-0" ,
120
- "libatk-bridge2.0-0" ,
121
- ] ;
238
+ const deps = [ ...debian12Deps . tools , ...Object . values ( debian12Deps . lib2package ) ] ;
239
+ if ( this . options . browsers . includes ( "chromium" ) ) deps . push ( ...debian12Deps . chromium ) ;
240
+ if ( this . options . browsers . includes ( "firefox" ) ) deps . push ( ...debian12Deps . firefox ) ;
241
+ if ( this . options . browsers . includes ( "webkit" ) ) deps . push ( ...debian12Deps . webkit ) ;
122
242
123
- const deps = [ ] ;
124
- if ( this . options . browsers . includes ( "chromium" ) ) deps . push ( ...chromiumDeps ) ;
125
- if ( this . options . browsers . includes ( "firefox" ) ) deps . push ( ...firefoxDeps ) ;
126
- if ( this . options . browsers . includes ( "webkit" ) ) deps . push ( ...webkitDeps ) ;
127
-
128
- const uniqueDeps = [ ...new Set ( deps ) ] ;
129
-
130
- if ( uniqueDeps . length > 0 ) {
131
- instructions . push (
132
- `RUN apt-get update && apt-get install -y --no-install-recommends ${ uniqueDeps . join ( " " ) } \
243
+ instructions . push (
244
+ `RUN apt-get update && apt-get install -y --no-install-recommends ${ deps . join ( " " ) } \
133
245
&& apt-get clean && rm -rf /var/lib/apt/lists/*`
134
- ) ;
135
- }
246
+ ) ;
136
247
137
- // Setup Playwright browsers
248
+ // Setup directory for playwright browsers
138
249
instructions . push ( `RUN mkdir -p /ms-playwright` ) ;
139
- instructions . push ( `RUN npx playwright install --dry-run > /tmp/browser-info.txt` ) ;
140
250
251
+ /**
252
+ * `npx playwright install --dry-run` prints the download urls for the browsers.
253
+ * We save this output to a file and then parse it to get the download urls for the browsers.
254
+ */
255
+ instructions . push ( `RUN npx playwright install --dry-run > /tmp/browser-info.txt` ) ;
141
256
this . options . browsers . forEach ( ( browser ) => {
142
257
const browserType = browser === "chromium" ? "chromium-headless-shell" : browser ;
143
-
144
258
instructions . push (
145
259
`RUN grep -A5 "browser: ${ browserType } " /tmp/browser-info.txt > /tmp/${ browser } -info.txt` ,
146
260
@@ -160,9 +274,9 @@ class PlaywrightExtension implements BuildExtension {
160
274
161
275
// Environment variables
162
276
const envVars : Record < string , string > = {
163
- PLAYWRIGHT_BROWSERS_PATH : "/ms-playwright" ,
164
- PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD : "1" ,
165
- PLAYWRIGHT_SKIP_BROWSER_VALIDATION : "1" ,
277
+ PLAYWRIGHT_BROWSERS_PATH : "/ms-playwright" , // where playwright will find the browsers
278
+ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD : "1" , // we already downloaded the browsers
279
+ PLAYWRIGHT_SKIP_BROWSER_VALIDATION : "1" , // we already downloaded the browsers
166
280
} ;
167
281
168
282
if ( ! this . options . headless ) {
@@ -173,7 +287,7 @@ class PlaywrightExtension implements BuildExtension {
173
287
`RUN chmod +x /usr/local/bin/xvfb-exec`
174
288
) ;
175
289
176
- envVars . DISPLAY = ":99" ;
290
+ envVars . DISPLAY = ":99" ; // Virtual display for the browsers
177
291
}
178
292
179
293
context . addLayer ( {
0 commit comments