2021-02-08 16:02:49 -08:00
/ * *
* Copyright 2017 Google Inc . All rights reserved .
* Modifications copyright ( c ) Microsoft Corporation .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
import * as os from 'os' ;
2021-02-11 06:36:15 -08:00
import path from 'path' ;
2021-02-08 16:02:49 -08:00
import * as util from 'util' ;
2021-07-06 20:59:16 -07:00
import * as fs from 'fs' ;
2022-04-18 16:50:25 -08:00
import { lockfile } from '../../utilsBundle' ;
2023-01-13 13:50:38 -08:00
import { fetchData } from '../../utils/network' ;
import { getEmbedderName } from '../../utils/userAgent' ;
2023-08-17 17:53:08 +02:00
import { getFromENV , getAsBooleanFromENV , calculateSha1 , wrapInASCIIBox , getPackageManagerExecCommand } from '../../utils' ;
2022-04-07 19:18:22 -08:00
import { removeFolders , existsAsync , canAccessFile } from '../../utils/fileUtils' ;
2023-10-16 10:49:57 -07:00
import { type HostPlatform , hostPlatform , isOfficiallySupportedPlatform } from '../../utils/hostPlatform' ;
2022-04-07 19:18:22 -08:00
import { spawnAsync } from '../../utils/spawnAsync' ;
2022-04-06 13:57:14 -08:00
import type { DependencyGroup } from './dependencies' ;
2022-08-23 10:39:59 -07:00
import { transformCommandsForRoot , dockerVersion , readDockerVersionSync } from './dependencies' ;
2022-04-06 13:57:14 -08:00
import { installDependenciesLinux , installDependenciesWindows , validateDependenciesLinux , validateDependenciesWindows } from './dependencies' ;
2021-07-06 20:59:16 -07:00
import { downloadBrowserWithProgressBar , logPolitely } from './browserFetcher' ;
2022-04-06 21:21:27 -08:00
export { writeDockerVersion } from './dependencies' ;
2024-01-30 14:36:51 -08:00
import { debugLogger } from '../../utils/debugLogger' ;
2021-02-08 16:02:49 -08:00
2022-04-06 21:21:27 -08:00
const PACKAGE_PATH = path . join ( __dirname , '..' , '..' , '..' ) ;
const BIN_PATH = path . join ( __dirname , '..' , '..' , '..' , 'bin' ) ;
fix(installer): retain browsers installed via Playwrigth CLI (#5904)
Browser registry is responsible for 3 things:
1. Remove downloaded browsers if there are no packages that refer to them
2. Install default browsers needed for the current package
3. Install browsers on-demand when used through Playwright CLI
Currently, registry relies on a single "download" field in `browsers.json`
to carry both (1) and (2). However, browsers in (3) are marked as
`download: false` so that they aren't installed automatically in (2), so
auto-remove procedure in (1) removes them on subsequent installation.
One possible approach to fix this would be modifying package's `browsers.json` to
change `download: false` to `true` when browsers are installed with
Playwright CLI. This approach was explored here:
https://github.com/microsoft/playwright/commit/bc04a51800d6d6322e43b7d147fc0ec42181e084
We decided against this since we have a history of issues related to
package modifications after NPM installation. This breaks all
sorts of yarn/npm caching mechanisms.
Instead, this patch is a two-step refactor:
- remove the "download" field in `browsers.json`. Now, all registries
(including old ones from previously-released versions) will retain any
browsers that are mentioned in the `browsers.json`.
- add a new flag "installByDefault", that is **only used** for default
installation.
With this change, the registry tasks are done like this:
- (1) auto-removal: if browser has a back reference, it is retained,
otherwise it is removed from registry
- (2) default installation: use only `installByDefault` to carry default installations
- (3) CLI installation: simply installs browsers. Since we retain
everythings that's referenced in (1), browsers aren't removed.
Fixes #5902
2021-03-22 11:43:29 -07:00
2022-06-17 20:47:32 +02:00
const PLAYWRIGHT_CDN_MIRRORS = [
2024-12-18 22:26:01 +01:00
'https://cdn.playwright.dev/dbazure/download/playwright' , // ESRP CDN
2024-11-14 16:19:42 +01:00
'https://playwright.download.prss.microsoft.com/dbazure/download/playwright' , // Directly hit ESRP CDN
2024-12-18 22:26:01 +01:00
'https://cdn.playwright.dev' , // Hit the Storage Bucket directly
2022-06-17 20:47:32 +02:00
] ;
2022-06-20 11:24:23 -07:00
if ( process . env . PW_TEST_CDN_THAT_SHOULD_WORK ) {
for ( let i = 0 ; i < PLAYWRIGHT_CDN_MIRRORS . length ; i ++ ) {
const cdn = PLAYWRIGHT_CDN_MIRRORS [ i ] ;
2024-11-14 16:19:42 +01:00
if ( cdn !== process . env . PW_TEST_CDN_THAT_SHOULD_WORK ) {
const parsedCDN = new URL ( cdn ) ;
parsedCDN . hostname = parsedCDN . hostname + '.does-not-resolve.playwright.dev' ;
PLAYWRIGHT_CDN_MIRRORS [ i ] = parsedCDN . toString ( ) ;
}
2022-06-20 11:24:23 -07:00
}
}
2021-02-08 16:02:49 -08:00
const EXECUTABLE_PATHS = {
2021-03-31 13:32:10 -05:00
'chromium' : {
2021-12-29 21:40:45 -07:00
'linux' : [ 'chrome-linux' , 'chrome' ] ,
'mac' : [ 'chrome-mac' , 'Chromium.app' , 'Contents' , 'MacOS' , 'Chromium' ] ,
'win' : [ 'chrome-win' , 'chrome.exe' ] ,
2021-02-08 16:02:49 -08:00
} ,
2024-10-22 15:47:50 +02:00
'chromium-headless-shell' : {
'linux' : [ 'chrome-linux' , 'headless_shell' ] ,
'mac' : [ 'chrome-mac' , 'headless_shell' ] ,
'win' : [ 'chrome-win' , 'headless_shell.exe' ] ,
} ,
2021-03-31 13:32:10 -05:00
'firefox' : {
2021-12-29 21:40:45 -07:00
'linux' : [ 'firefox' , 'firefox' ] ,
'mac' : [ 'firefox' , 'Nightly.app' , 'Contents' , 'MacOS' , 'firefox' ] ,
'win' : [ 'firefox' , 'firefox.exe' ] ,
2021-02-08 16:02:49 -08:00
} ,
2021-03-31 13:32:10 -05:00
'webkit' : {
2021-12-29 21:40:45 -07:00
'linux' : [ 'pw_run.sh' ] ,
'mac' : [ 'pw_run.sh' ] ,
'win' : [ 'Playwright.exe' ] ,
2021-02-08 16:02:49 -08:00
} ,
2021-03-31 13:32:10 -05:00
'ffmpeg' : {
2021-12-29 21:40:45 -07:00
'linux' : [ 'ffmpeg-linux' ] ,
'mac' : [ 'ffmpeg-mac' ] ,
'win' : [ 'ffmpeg-win64.exe' ] ,
2021-02-08 16:02:49 -08:00
} ,
} ;
2023-10-16 10:49:57 -07:00
type DownloadPaths = Record < HostPlatform , string | undefined > ;
2024-11-14 12:20:44 +00:00
const DOWNLOAD_PATHS : Record < BrowserName | InternalTool , DownloadPaths > = {
2021-03-31 13:32:10 -05:00
'chromium' : {
2022-03-23 15:06:14 -06:00
'<unknown>' : undefined ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-x64' : undefined ,
2023-10-17 15:54:20 -04:00
'ubuntu20.04-x64' : 'builds/chromium/%s/chromium-linux.zip' ,
'ubuntu22.04-x64' : 'builds/chromium/%s/chromium-linux.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-x64' : 'builds/chromium/%s/chromium-linux.zip' ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-arm64' : undefined ,
2021-11-29 22:23:33 +05:30
'ubuntu20.04-arm64' : 'builds/chromium/%s/chromium-linux-arm64.zip' ,
2022-06-09 13:20:18 +02:00
'ubuntu22.04-arm64' : 'builds/chromium/%s/chromium-linux-arm64.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-arm64' : 'builds/chromium/%s/chromium-linux-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian11-x64' : 'builds/chromium/%s/chromium-linux.zip' ,
2023-01-23 15:54:25 +01:00
'debian11-arm64' : 'builds/chromium/%s/chromium-linux-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian12-x64' : 'builds/chromium/%s/chromium-linux.zip' ,
2023-07-06 22:41:36 -07:00
'debian12-arm64' : 'builds/chromium/%s/chromium-linux-arm64.zip' ,
2021-11-29 22:23:33 +05:30
'mac10.13' : 'builds/chromium/%s/chromium-mac.zip' ,
'mac10.14' : 'builds/chromium/%s/chromium-mac.zip' ,
'mac10.15' : 'builds/chromium/%s/chromium-mac.zip' ,
'mac11' : 'builds/chromium/%s/chromium-mac.zip' ,
'mac11-arm64' : 'builds/chromium/%s/chromium-mac-arm64.zip' ,
2021-12-29 21:40:45 -07:00
'mac12' : 'builds/chromium/%s/chromium-mac.zip' ,
'mac12-arm64' : 'builds/chromium/%s/chromium-mac-arm64.zip' ,
2023-06-05 09:51:39 -07:00
'mac13' : 'builds/chromium/%s/chromium-mac.zip' ,
'mac13-arm64' : 'builds/chromium/%s/chromium-mac-arm64.zip' ,
2024-03-26 23:05:14 +01:00
'mac14' : 'builds/chromium/%s/chromium-mac.zip' ,
'mac14-arm64' : 'builds/chromium/%s/chromium-mac-arm64.zip' ,
2024-09-26 12:49:02 -07:00
'mac15' : 'builds/chromium/%s/chromium-mac.zip' ,
'mac15-arm64' : 'builds/chromium/%s/chromium-mac-arm64.zip' ,
2021-11-29 22:23:33 +05:30
'win64' : 'builds/chromium/%s/chromium-win64.zip' ,
2021-02-08 16:02:49 -08:00
} ,
2024-10-22 15:47:50 +02:00
'chromium-headless-shell' : {
'<unknown>' : undefined ,
'ubuntu18.04-x64' : undefined ,
'ubuntu20.04-x64' : 'builds/chromium/%s/chromium-headless-shell-linux.zip' ,
'ubuntu22.04-x64' : 'builds/chromium/%s/chromium-headless-shell-linux.zip' ,
'ubuntu24.04-x64' : 'builds/chromium/%s/chromium-headless-shell-linux.zip' ,
'ubuntu18.04-arm64' : undefined ,
'ubuntu20.04-arm64' : 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip' ,
'ubuntu22.04-arm64' : 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip' ,
'ubuntu24.04-arm64' : 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip' ,
'debian11-x64' : 'builds/chromium/%s/chromium-headless-shell-linux.zip' ,
'debian11-arm64' : 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip' ,
'debian12-x64' : 'builds/chromium/%s/chromium-headless-shell-linux.zip' ,
'debian12-arm64' : 'builds/chromium/%s/chromium-headless-shell-linux-arm64.zip' ,
'mac10.13' : undefined ,
'mac10.14' : undefined ,
'mac10.15' : undefined ,
'mac11' : 'builds/chromium/%s/chromium-headless-shell-mac.zip' ,
'mac11-arm64' : 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip' ,
'mac12' : 'builds/chromium/%s/chromium-headless-shell-mac.zip' ,
'mac12-arm64' : 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip' ,
'mac13' : 'builds/chromium/%s/chromium-headless-shell-mac.zip' ,
'mac13-arm64' : 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip' ,
'mac14' : 'builds/chromium/%s/chromium-headless-shell-mac.zip' ,
'mac14-arm64' : 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip' ,
'mac15' : 'builds/chromium/%s/chromium-headless-shell-mac.zip' ,
'mac15-arm64' : 'builds/chromium/%s/chromium-headless-shell-mac-arm64.zip' ,
'win64' : 'builds/chromium/%s/chromium-headless-shell-win64.zip' ,
} ,
2022-04-22 10:43:57 -06:00
'chromium-tip-of-tree' : {
'<unknown>' : undefined ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-x64' : undefined ,
2023-10-17 15:54:20 -04:00
'ubuntu20.04-x64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip' ,
'ubuntu22.04-x64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-x64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip' ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-arm64' : undefined ,
2022-04-22 10:43:57 -06:00
'ubuntu20.04-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip' ,
2022-06-09 13:20:18 +02:00
'ubuntu22.04-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian11-x64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip' ,
2023-01-23 15:54:25 +01:00
'debian11-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian12-x64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux.zip' ,
2023-07-06 22:41:36 -07:00
'debian12-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-linux-arm64.zip' ,
2022-04-22 10:43:57 -06:00
'mac10.13' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip' ,
'mac10.14' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip' ,
'mac10.15' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip' ,
'mac11' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip' ,
'mac11-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip' ,
'mac12' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip' ,
'mac12-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip' ,
2023-06-05 09:51:39 -07:00
'mac13' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip' ,
'mac13-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip' ,
2024-03-26 23:05:14 +01:00
'mac14' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip' ,
'mac14-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip' ,
2024-09-26 12:49:02 -07:00
'mac15' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac.zip' ,
'mac15-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-mac-arm64.zip' ,
2022-04-22 10:43:57 -06:00
'win64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-win64.zip' ,
} ,
2024-12-11 18:11:33 -08:00
'chromium-tip-of-tree-headless-shell' : {
'<unknown>' : undefined ,
'ubuntu18.04-x64' : undefined ,
'ubuntu20.04-x64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip' ,
'ubuntu22.04-x64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip' ,
'ubuntu24.04-x64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip' ,
'ubuntu18.04-arm64' : undefined ,
'ubuntu20.04-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip' ,
'ubuntu22.04-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip' ,
'ubuntu24.04-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip' ,
'debian11-x64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip' ,
'debian11-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip' ,
'debian12-x64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux.zip' ,
'debian12-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-linux-arm64.zip' ,
'mac10.13' : undefined ,
'mac10.14' : undefined ,
'mac10.15' : undefined ,
'mac11' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip' ,
'mac11-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip' ,
'mac12' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip' ,
'mac12-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip' ,
'mac13' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip' ,
'mac13-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip' ,
'mac14' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip' ,
'mac14-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip' ,
'mac15' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac.zip' ,
'mac15-arm64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-mac-arm64.zip' ,
'win64' : 'builds/chromium-tip-of-tree/%s/chromium-tip-of-tree-headless-shell-win64.zip' ,
} ,
2021-03-31 13:32:10 -05:00
'firefox' : {
2022-03-23 15:06:14 -06:00
'<unknown>' : undefined ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-x64' : undefined ,
2023-10-17 15:54:20 -04:00
'ubuntu20.04-x64' : 'builds/firefox/%s/firefox-ubuntu-20.04.zip' ,
'ubuntu22.04-x64' : 'builds/firefox/%s/firefox-ubuntu-22.04.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-x64' : 'builds/firefox/%s/firefox-ubuntu-24.04.zip' ,
2022-12-21 18:37:42 +01:00
'ubuntu18.04-arm64' : undefined ,
2021-11-29 22:23:33 +05:30
'ubuntu20.04-arm64' : 'builds/firefox/%s/firefox-ubuntu-20.04-arm64.zip' ,
2022-06-09 13:20:18 +02:00
'ubuntu22.04-arm64' : 'builds/firefox/%s/firefox-ubuntu-22.04-arm64.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-arm64' : 'builds/firefox/%s/firefox-ubuntu-24.04-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian11-x64' : 'builds/firefox/%s/firefox-debian-11.zip' ,
2023-01-23 15:54:25 +01:00
'debian11-arm64' : 'builds/firefox/%s/firefox-debian-11-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian12-x64' : 'builds/firefox/%s/firefox-debian-12.zip' ,
2023-07-13 12:56:44 +02:00
'debian12-arm64' : 'builds/firefox/%s/firefox-debian-12-arm64.zip' ,
2024-05-19 16:28:32 +02:00
'mac10.13' : 'builds/firefox/%s/firefox-mac.zip' ,
'mac10.14' : 'builds/firefox/%s/firefox-mac.zip' ,
'mac10.15' : 'builds/firefox/%s/firefox-mac.zip' ,
'mac11' : 'builds/firefox/%s/firefox-mac.zip' ,
'mac11-arm64' : 'builds/firefox/%s/firefox-mac-arm64.zip' ,
'mac12' : 'builds/firefox/%s/firefox-mac.zip' ,
'mac12-arm64' : 'builds/firefox/%s/firefox-mac-arm64.zip' ,
'mac13' : 'builds/firefox/%s/firefox-mac.zip' ,
'mac13-arm64' : 'builds/firefox/%s/firefox-mac-arm64.zip' ,
'mac14' : 'builds/firefox/%s/firefox-mac.zip' ,
'mac14-arm64' : 'builds/firefox/%s/firefox-mac-arm64.zip' ,
2024-09-26 12:49:02 -07:00
'mac15' : 'builds/firefox/%s/firefox-mac.zip' ,
'mac15-arm64' : 'builds/firefox/%s/firefox-mac-arm64.zip' ,
2021-11-29 22:23:33 +05:30
'win64' : 'builds/firefox/%s/firefox-win64.zip' ,
2021-02-08 16:02:49 -08:00
} ,
2021-06-08 09:34:17 -07:00
'firefox-beta' : {
2022-03-23 15:06:14 -06:00
'<unknown>' : undefined ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-x64' : undefined ,
2023-10-17 15:54:20 -04:00
'ubuntu20.04-x64' : 'builds/firefox-beta/%s/firefox-beta-ubuntu-20.04.zip' ,
'ubuntu22.04-x64' : 'builds/firefox-beta/%s/firefox-beta-ubuntu-22.04.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-x64' : 'builds/firefox-beta/%s/firefox-beta-ubuntu-24.04.zip' ,
2022-12-21 18:37:42 +01:00
'ubuntu18.04-arm64' : undefined ,
2021-11-02 16:58:22 -07:00
'ubuntu20.04-arm64' : undefined ,
2022-06-09 13:20:18 +02:00
'ubuntu22.04-arm64' : 'builds/firefox-beta/%s/firefox-beta-ubuntu-22.04-arm64.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-arm64' : 'builds/firefox-beta/%s/firefox-beta-ubuntu-24.04-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian11-x64' : 'builds/firefox-beta/%s/firefox-beta-debian-11.zip' ,
2023-01-23 15:54:25 +01:00
'debian11-arm64' : 'builds/firefox-beta/%s/firefox-beta-debian-11-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian12-x64' : 'builds/firefox-beta/%s/firefox-beta-debian-12.zip' ,
2023-07-13 12:56:44 +02:00
'debian12-arm64' : 'builds/firefox-beta/%s/firefox-beta-debian-12-arm64.zip' ,
2024-05-19 16:28:32 +02:00
'mac10.13' : 'builds/firefox-beta/%s/firefox-beta-mac.zip' ,
'mac10.14' : 'builds/firefox-beta/%s/firefox-beta-mac.zip' ,
'mac10.15' : 'builds/firefox-beta/%s/firefox-beta-mac.zip' ,
'mac11' : 'builds/firefox-beta/%s/firefox-beta-mac.zip' ,
'mac11-arm64' : 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip' ,
'mac12' : 'builds/firefox-beta/%s/firefox-beta-mac.zip' ,
'mac12-arm64' : 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip' ,
'mac13' : 'builds/firefox-beta/%s/firefox-beta-mac.zip' ,
'mac13-arm64' : 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip' ,
'mac14' : 'builds/firefox-beta/%s/firefox-beta-mac.zip' ,
'mac14-arm64' : 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip' ,
2024-09-26 12:49:02 -07:00
'mac15' : 'builds/firefox-beta/%s/firefox-beta-mac.zip' ,
'mac15-arm64' : 'builds/firefox-beta/%s/firefox-beta-mac-arm64.zip' ,
2021-11-29 22:23:33 +05:30
'win64' : 'builds/firefox-beta/%s/firefox-beta-win64.zip' ,
2021-04-19 23:26:33 -05:00
} ,
2021-03-31 13:32:10 -05:00
'webkit' : {
2022-03-23 15:06:14 -06:00
'<unknown>' : undefined ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-x64' : undefined ,
2023-10-17 15:54:20 -04:00
'ubuntu20.04-x64' : 'builds/webkit/%s/webkit-ubuntu-20.04.zip' ,
'ubuntu22.04-x64' : 'builds/webkit/%s/webkit-ubuntu-22.04.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-x64' : 'builds/webkit/%s/webkit-ubuntu-24.04.zip' ,
2022-12-21 18:37:42 +01:00
'ubuntu18.04-arm64' : undefined ,
2021-11-29 22:23:33 +05:30
'ubuntu20.04-arm64' : 'builds/webkit/%s/webkit-ubuntu-20.04-arm64.zip' ,
2022-06-09 13:20:18 +02:00
'ubuntu22.04-arm64' : 'builds/webkit/%s/webkit-ubuntu-22.04-arm64.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-arm64' : 'builds/webkit/%s/webkit-ubuntu-24.04-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian11-x64' : 'builds/webkit/%s/webkit-debian-11.zip' ,
2023-01-23 15:54:25 +01:00
'debian11-arm64' : 'builds/webkit/%s/webkit-debian-11-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian12-x64' : 'builds/webkit/%s/webkit-debian-12.zip' ,
2023-07-13 12:56:44 +02:00
'debian12-arm64' : 'builds/webkit/%s/webkit-debian-12-arm64.zip' ,
2021-02-08 16:02:49 -08:00
'mac10.13' : undefined ,
2021-11-29 22:23:33 +05:30
'mac10.14' : 'builds/deprecated-webkit-mac-10.14/%s/deprecated-webkit-mac-10.14.zip' ,
2022-09-30 04:40:17 -04:00
'mac10.15' : 'builds/deprecated-webkit-mac-10.15/%s/deprecated-webkit-mac-10.15.zip' ,
2022-03-15 16:09:56 -07:00
'mac11' : 'builds/webkit/%s/webkit-mac-11.zip' ,
2021-11-29 22:23:33 +05:30
'mac11-arm64' : 'builds/webkit/%s/webkit-mac-11-arm64.zip' ,
2021-12-29 21:40:45 -07:00
'mac12' : 'builds/webkit/%s/webkit-mac-12.zip' ,
'mac12-arm64' : 'builds/webkit/%s/webkit-mac-12-arm64.zip' ,
2023-06-05 09:51:39 -07:00
'mac13' : 'builds/webkit/%s/webkit-mac-13.zip' ,
'mac13-arm64' : 'builds/webkit/%s/webkit-mac-13-arm64.zip' ,
2024-03-26 23:05:14 +01:00
'mac14' : 'builds/webkit/%s/webkit-mac-14.zip' ,
'mac14-arm64' : 'builds/webkit/%s/webkit-mac-14-arm64.zip' ,
2024-09-26 12:49:02 -07:00
'mac15' : 'builds/webkit/%s/webkit-mac-15.zip' ,
'mac15-arm64' : 'builds/webkit/%s/webkit-mac-15-arm64.zip' ,
2021-11-29 22:23:33 +05:30
'win64' : 'builds/webkit/%s/webkit-win64.zip' ,
2021-02-08 16:02:49 -08:00
} ,
2021-03-31 13:32:10 -05:00
'ffmpeg' : {
2022-03-23 15:06:14 -06:00
'<unknown>' : undefined ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-x64' : undefined ,
2023-10-17 15:54:20 -04:00
'ubuntu20.04-x64' : 'builds/ffmpeg/%s/ffmpeg-linux.zip' ,
'ubuntu22.04-x64' : 'builds/ffmpeg/%s/ffmpeg-linux.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-x64' : 'builds/ffmpeg/%s/ffmpeg-linux.zip' ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-arm64' : undefined ,
2021-11-29 22:23:33 +05:30
'ubuntu20.04-arm64' : 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip' ,
2022-06-09 13:20:18 +02:00
'ubuntu22.04-arm64' : 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-arm64' : 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian11-x64' : 'builds/ffmpeg/%s/ffmpeg-linux.zip' ,
2023-01-23 15:54:25 +01:00
'debian11-arm64' : 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip' ,
2023-10-17 15:54:20 -04:00
'debian12-x64' : 'builds/ffmpeg/%s/ffmpeg-linux.zip' ,
2023-07-06 22:41:36 -07:00
'debian12-arm64' : 'builds/ffmpeg/%s/ffmpeg-linux-arm64.zip' ,
2021-11-29 22:23:33 +05:30
'mac10.13' : 'builds/ffmpeg/%s/ffmpeg-mac.zip' ,
'mac10.14' : 'builds/ffmpeg/%s/ffmpeg-mac.zip' ,
'mac10.15' : 'builds/ffmpeg/%s/ffmpeg-mac.zip' ,
'mac11' : 'builds/ffmpeg/%s/ffmpeg-mac.zip' ,
2021-12-29 21:10:29 -07:00
'mac11-arm64' : 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip' ,
2021-12-29 21:40:45 -07:00
'mac12' : 'builds/ffmpeg/%s/ffmpeg-mac.zip' ,
'mac12-arm64' : 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip' ,
2023-06-05 09:51:39 -07:00
'mac13' : 'builds/ffmpeg/%s/ffmpeg-mac.zip' ,
'mac13-arm64' : 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip' ,
2024-03-26 23:05:14 +01:00
'mac14' : 'builds/ffmpeg/%s/ffmpeg-mac.zip' ,
'mac14-arm64' : 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip' ,
2024-09-26 12:49:02 -07:00
'mac15' : 'builds/ffmpeg/%s/ffmpeg-mac.zip' ,
'mac15-arm64' : 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip' ,
2021-11-29 22:23:33 +05:30
'win64' : 'builds/ffmpeg/%s/ffmpeg-win64.zip' ,
2021-02-08 16:02:49 -08:00
} ,
2022-10-31 16:08:26 -07:00
'android' : {
'<unknown>' : 'builds/android/%s/android.zip' ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-x64' : undefined ,
2023-10-17 15:54:20 -04:00
'ubuntu20.04-x64' : 'builds/android/%s/android.zip' ,
'ubuntu22.04-x64' : 'builds/android/%s/android.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-x64' : 'builds/android/%s/android.zip' ,
2024-01-24 17:46:20 +01:00
'ubuntu18.04-arm64' : undefined ,
2023-10-16 10:49:57 -07:00
'ubuntu20.04-arm64' : 'builds/android/%s/android.zip' ,
'ubuntu22.04-arm64' : 'builds/android/%s/android.zip' ,
2024-06-03 12:47:16 +02:00
'ubuntu24.04-arm64' : 'builds/android/%s/android.zip' ,
2023-10-17 15:54:20 -04:00
'debian11-x64' : 'builds/android/%s/android.zip' ,
2023-10-16 10:49:57 -07:00
'debian11-arm64' : 'builds/android/%s/android.zip' ,
2023-10-17 15:54:20 -04:00
'debian12-x64' : 'builds/android/%s/android.zip' ,
2023-10-16 10:49:57 -07:00
'debian12-arm64' : 'builds/android/%s/android.zip' ,
'mac10.13' : 'builds/android/%s/android.zip' ,
'mac10.14' : 'builds/android/%s/android.zip' ,
'mac10.15' : 'builds/android/%s/android.zip' ,
'mac11' : 'builds/android/%s/android.zip' ,
'mac11-arm64' : 'builds/android/%s/android.zip' ,
'mac12' : 'builds/android/%s/android.zip' ,
'mac12-arm64' : 'builds/android/%s/android.zip' ,
'mac13' : 'builds/android/%s/android.zip' ,
'mac13-arm64' : 'builds/android/%s/android.zip' ,
2024-03-26 23:05:14 +01:00
'mac14' : 'builds/android/%s/android.zip' ,
'mac14-arm64' : 'builds/android/%s/android.zip' ,
2024-09-26 12:49:02 -07:00
'mac15' : 'builds/android/%s/android.zip' ,
'mac15-arm64' : 'builds/android/%s/android.zip' ,
2023-10-16 10:49:57 -07:00
'win64' : 'builds/android/%s/android.zip' ,
2022-10-31 16:08:26 -07:00
} ,
2024-09-04 11:36:52 -07:00
// TODO(bidi): implement downloads.
'bidi' : {
} as DownloadPaths ,
2021-02-08 16:02:49 -08:00
} ;
2021-07-15 12:27:16 -05:00
export const registryDirectory = ( ( ) = > {
2021-02-12 11:12:06 -08:00
let result : string ;
2021-02-08 16:02:49 -08:00
const envDefined = getFromENV ( 'PLAYWRIGHT_BROWSERS_PATH' ) ;
2021-02-12 11:12:06 -08:00
if ( envDefined === '0' ) {
2022-04-06 21:21:27 -08:00
result = path . join ( __dirname , '..' , '..' , '..' , '.local-browsers' ) ;
2021-02-12 11:12:06 -08:00
} else if ( envDefined ) {
result = envDefined ;
} else {
let cacheDirectory : string ;
if ( process . platform === 'linux' )
cacheDirectory = process . env . XDG_CACHE_HOME || path . join ( os . homedir ( ) , '.cache' ) ;
else if ( process . platform === 'darwin' )
cacheDirectory = path . join ( os . homedir ( ) , 'Library' , 'Caches' ) ;
else if ( process . platform === 'win32' )
cacheDirectory = process . env . LOCALAPPDATA || path . join ( os . homedir ( ) , 'AppData' , 'Local' ) ;
else
throw new Error ( 'Unsupported platform: ' + process . platform ) ;
result = path . join ( cacheDirectory , 'ms-playwright' ) ;
}
if ( ! path . isAbsolute ( result ) ) {
// It is important to resolve to the absolute path:
// - for unzipping to work correctly;
// - so that registry directory matches between installation and execution.
// INIT_CWD points to the root of `npm/yarn install` and is probably what
// the user meant when typing the relative path.
result = path . resolve ( getFromENV ( 'INIT_CWD' ) || process . cwd ( ) , result ) ;
}
return result ;
2021-02-08 16:02:49 -08:00
} ) ( ) ;
2021-07-09 16:10:23 -07:00
function isBrowserDirectory ( browserDirectory : string ) : boolean {
2021-02-08 16:02:49 -08:00
const baseName = path . basename ( browserDirectory ) ;
2021-07-13 15:57:40 -07:00
for ( const browserName of allDownloadable ) {
2024-11-05 10:34:00 +01:00
if ( baseName . startsWith ( browserName . replace ( /-/g , '_' ) + '-' ) )
2021-02-08 16:02:49 -08:00
return true ;
}
return false ;
}
2021-08-31 16:50:17 +02:00
type BrowsersJSON = {
comment : string
browsers : {
name : string ,
revision : string ,
2022-05-05 05:17:13 -06:00
browserVersion? : string ,
2021-08-31 16:50:17 +02:00
installByDefault : boolean ,
revisionOverrides ? : { [ os : string ] : string } ,
} [ ]
} ;
2021-07-13 15:57:40 -07:00
type BrowsersJSONDescriptor = {
name : string ,
2021-07-09 16:10:23 -07:00
revision : string ,
2024-10-23 18:14:55 +02:00
hasRevisionOverride : boolean
2022-05-05 05:17:13 -06:00
browserVersion? : string ,
2021-07-09 16:10:23 -07:00
installByDefault : boolean ,
2021-07-13 15:57:40 -07:00
dir : string ,
2021-07-09 16:10:23 -07:00
} ;
2024-10-22 15:47:50 +02:00
function readDescriptors ( browsersJSON : BrowsersJSON ) : BrowsersJSONDescriptor [ ] {
2024-12-12 12:23:03 -08:00
const headlessShells : BrowsersJSON [ 'browsers' ] = [ ] ;
for ( const browserName of [ 'chromium' , 'chromium-tip-of-tree' ] ) {
headlessShells . push ( {
. . . browsersJSON . browsers . find ( browser = > browser . name === browserName ) ! ,
name : ` ${ browserName } -headless-shell ` ,
} ) ;
}
return [ . . . browsersJSON . browsers , . . . headlessShells ] . map ( obj = > {
2021-07-09 16:10:23 -07:00
const name = obj . name ;
const revisionOverride = ( obj . revisionOverrides || { } ) [ hostPlatform ] ;
const revision = revisionOverride || obj . revision ;
const browserDirectoryPrefix = revisionOverride ? ` ${ name } _ ${ hostPlatform } _special ` : ` ${ name } ` ;
2021-07-13 15:57:40 -07:00
const descriptor : BrowsersJSONDescriptor = {
2021-07-09 16:10:23 -07:00
name ,
revision ,
2024-10-23 18:14:55 +02:00
hasRevisionOverride : ! ! revisionOverride ,
2022-05-05 05:17:13 -06:00
// We only put browser version for the supported operating systems.
browserVersion : revisionOverride ? undefined : obj . browserVersion ,
2021-07-09 16:10:23 -07:00
installByDefault : ! ! obj . installByDefault ,
// Method `isBrowserDirectory` determines directory to be browser iff
// it starts with some browser name followed by '-'. Some browser names
// are prefixes of others, e.g. 'webkit' is a prefix of `webkit-technology-preview`.
// To avoid older registries erroneously removing 'webkit-technology-preview', we have to
// ensure that browser folders to never include dashes inside.
2021-07-13 15:57:40 -07:00
dir : path.join ( registryDirectory , browserDirectoryPrefix . replace ( /-/g , '_' ) + '-' + revision ) ,
2021-07-09 16:10:23 -07:00
} ;
return descriptor ;
} ) ;
}
fix(installer): retain browsers installed via Playwrigth CLI (#5904)
Browser registry is responsible for 3 things:
1. Remove downloaded browsers if there are no packages that refer to them
2. Install default browsers needed for the current package
3. Install browsers on-demand when used through Playwright CLI
Currently, registry relies on a single "download" field in `browsers.json`
to carry both (1) and (2). However, browsers in (3) are marked as
`download: false` so that they aren't installed automatically in (2), so
auto-remove procedure in (1) removes them on subsequent installation.
One possible approach to fix this would be modifying package's `browsers.json` to
change `download: false` to `true` when browsers are installed with
Playwright CLI. This approach was explored here:
https://github.com/microsoft/playwright/commit/bc04a51800d6d6322e43b7d147fc0ec42181e084
We decided against this since we have a history of issues related to
package modifications after NPM installation. This breaks all
sorts of yarn/npm caching mechanisms.
Instead, this patch is a two-step refactor:
- remove the "download" field in `browsers.json`. Now, all registries
(including old ones from previously-released versions) will retain any
browsers that are mentioned in the `browsers.json`.
- add a new flag "installByDefault", that is **only used** for default
installation.
With this change, the registry tasks are done like this:
- (1) auto-removal: if browser has a back reference, it is retained,
otherwise it is removed from registry
- (2) default installation: use only `installByDefault` to carry default installations
- (3) CLI installation: simply installs browsers. Since we retain
everythings that's referenced in (1), browsers aren't removed.
Fixes #5902
2021-03-22 11:43:29 -07:00
2024-09-04 11:36:52 -07:00
export type BrowserName = 'chromium' | 'firefox' | 'webkit' | 'bidi' ;
2024-12-11 18:11:33 -08:00
type InternalTool = 'ffmpeg' | 'firefox-beta' | 'chromium-tip-of-tree' | 'chromium-headless-shell' | 'chromium-tip-of-tree-headless-shell' | 'android' ;
2024-09-09 17:22:19 -07:00
type BidiChannel = 'bidi-firefox-stable' | 'bidi-firefox-beta' | 'bidi-firefox-nightly' | 'bidi-chrome-canary' | 'bidi-chrome-stable' | 'bidi-chromium' ;
2024-11-14 12:20:44 +00:00
type ChromiumChannel = 'chrome' | 'chrome-beta' | 'chrome-dev' | 'chrome-canary' | 'msedge' | 'msedge-beta' | 'msedge-dev' | 'msedge-canary' ;
2024-12-11 18:11:33 -08:00
const allDownloadable = [ 'android' , 'chromium' , 'firefox' , 'webkit' , 'ffmpeg' , 'firefox-beta' , 'chromium-tip-of-tree' , 'chromium-headless-shell' , 'chromium-tip-of-tree-headless-shell' ] ;
2021-07-13 15:57:40 -07:00
export interface Executable {
2021-07-13 19:03:49 -07:00
type : 'browser' | 'tool' | 'channel' ;
2024-09-04 11:36:52 -07:00
name : BrowserName | InternalTool | ChromiumChannel | BidiChannel ;
2021-07-13 15:57:40 -07:00
browserName : BrowserName | undefined ;
2021-07-13 19:03:49 -07:00
installType : 'download-by-default' | 'download-on-demand' | 'install-script' | 'none' ;
directory : string | undefined ;
2022-09-08 09:05:09 -07:00
downloadURLs? : string [ ] ,
browserVersion? : string ,
2021-08-20 21:32:21 +02:00
executablePathOrDie ( sdkLanguage : string ) : string ;
2021-10-27 18:58:13 +02:00
executablePath ( sdkLanguage : string ) : string | undefined ;
2024-01-25 20:55:53 +01:00
_validateHostRequirements ( sdkLanguage : string ) : Promise < void > ;
2021-07-13 15:57:40 -07:00
}
interface ExecutableImpl extends Executable {
2021-07-13 19:03:49 -07:00
_install ? : ( ) = > Promise < void > ;
_dependencyGroup? : DependencyGroup ;
2022-04-01 11:05:53 -06:00
_isHermeticInstallation? : boolean ;
2021-07-13 15:57:40 -07:00
}
2021-02-08 16:02:49 -08:00
export class Registry {
2021-07-13 15:57:40 -07:00
private _executables : ExecutableImpl [ ] ;
2021-02-08 16:02:49 -08:00
2021-08-31 16:50:17 +02:00
constructor ( browsersJSON : BrowsersJSON ) {
const descriptors = readDescriptors ( browsersJSON ) ;
2021-07-13 19:03:49 -07:00
const findExecutablePath = ( dir : string , name : keyof typeof EXECUTABLE_PATHS ) = > {
2021-12-29 21:40:45 -07:00
let tokens = undefined ;
2022-07-13 04:09:24 -07:00
if ( process . platform === 'linux' )
2021-12-29 21:40:45 -07:00
tokens = EXECUTABLE_PATHS [ name ] [ 'linux' ] ;
2022-07-13 04:09:24 -07:00
else if ( process . platform === 'darwin' )
2021-12-29 21:40:45 -07:00
tokens = EXECUTABLE_PATHS [ name ] [ 'mac' ] ;
2022-07-13 04:09:24 -07:00
else if ( process . platform === 'win32' )
2021-12-29 21:40:45 -07:00
tokens = EXECUTABLE_PATHS [ name ] [ 'win' ] ;
2021-07-13 15:57:40 -07:00
return tokens ? path . join ( dir , . . . tokens ) : undefined ;
} ;
2021-08-20 21:32:21 +02:00
const executablePathOrDie = ( name : string , e : string | undefined , installByDefault : boolean , sdkLanguage : string ) = > {
2021-07-13 19:03:49 -07:00
if ( ! e )
throw new Error ( ` ${ name } is not supported on ${ hostPlatform } ` ) ;
2021-08-20 21:32:21 +02:00
const installCommand = buildPlaywrightCLICommand ( sdkLanguage , ` install ${ installByDefault ? '' : ' ' + name } ` ) ;
2021-07-16 16:00:27 -08:00
if ( ! canAccessFile ( e ) ) {
2022-08-23 10:39:59 -07:00
const currentDockerVersion = readDockerVersionSync ( ) ;
const preferredDockerVersion = currentDockerVersion ? dockerVersion ( currentDockerVersion . dockerImageNameTemplate ) : null ;
const isOutdatedDockerImage = currentDockerVersion && preferredDockerVersion && currentDockerVersion . dockerImageName !== preferredDockerVersion . dockerImageName ;
const prettyMessage = isOutdatedDockerImage ? [
` Looks like ${ sdkLanguage === 'javascript' ? 'Playwright Test or ' : '' } Playwright was just updated to ${ preferredDockerVersion . driverVersion } . ` ,
` Please update docker image as well. ` ,
` - current: ${ currentDockerVersion . dockerImageName } ` ,
` - required: ${ preferredDockerVersion . dockerImageName } ` ,
` ` ,
` <3 Playwright Team ` ,
] . join ( '\n' ) : [
2022-04-20 21:02:28 +02:00
` Looks like ${ sdkLanguage === 'javascript' ? 'Playwright Test or ' : '' } Playwright was just installed or updated. ` ,
2021-07-16 16:00:27 -08:00
` Please run the following command to download new browser ${ installByDefault ? 's' : '' } : ` ,
` ` ,
2021-08-20 21:32:21 +02:00
` ${ installCommand } ` ,
2021-07-16 16:00:27 -08:00
` ` ,
` <3 Playwright Team ` ,
] . join ( '\n' ) ;
throw new Error ( ` Executable doesn't exist at ${ e } \ n ${ wrapInASCIIBox ( prettyMessage , 1 ) } ` ) ;
}
2021-07-13 19:03:49 -07:00
return e ;
} ;
2021-07-13 15:57:40 -07:00
this . _executables = [ ] ;
const chromium = descriptors . find ( d = > d . name === 'chromium' ) ! ;
2021-07-13 19:03:49 -07:00
const chromiumExecutable = findExecutablePath ( chromium . dir , 'chromium' ) ;
2021-07-13 15:57:40 -07:00
this . _executables . push ( {
type : 'browser' ,
name : 'chromium' ,
browserName : 'chromium' ,
2021-07-13 19:03:49 -07:00
directory : chromium.dir ,
executablePath : ( ) = > chromiumExecutable ,
2021-08-20 21:32:21 +02:00
executablePathOrDie : ( sdkLanguage : string ) = > executablePathOrDie ( 'chromium' , chromiumExecutable , chromium . installByDefault , sdkLanguage ) ,
2021-07-13 15:57:40 -07:00
installType : chromium.installByDefault ? 'download-by-default' : 'download-on-demand' ,
2024-10-22 15:47:50 +02:00
_validateHostRequirements : ( sdkLanguage : string ) = > this . _validateHostRequirements ( sdkLanguage , chromium . dir , [ 'chrome-linux' ] , [ ] , [ 'chrome-win' ] ) ,
2022-09-08 09:05:09 -07:00
downloadURLs : this._downloadURLs ( chromium ) ,
browserVersion : chromium.browserVersion ,
_install : ( ) = > this . _downloadExecutable ( chromium , chromiumExecutable ) ,
2021-07-13 19:03:49 -07:00
_dependencyGroup : 'chromium' ,
2022-04-01 11:05:53 -06:00
_isHermeticInstallation : true ,
2022-04-22 10:43:57 -06:00
} ) ;
2024-11-05 10:34:00 +01:00
const chromiumHeadlessShell = descriptors . find ( d = > d . name === 'chromium-headless-shell' ) ! ;
const chromiumHeadlessShellExecutable = findExecutablePath ( chromiumHeadlessShell . dir , 'chromium-headless-shell' ) ;
2024-10-22 15:47:50 +02:00
this . _executables . push ( {
2024-11-13 10:52:28 +00:00
type : 'channel' ,
2024-10-22 15:47:50 +02:00
name : 'chromium-headless-shell' ,
browserName : 'chromium' ,
2024-11-05 10:34:00 +01:00
directory : chromiumHeadlessShell.dir ,
2024-10-22 15:47:50 +02:00
executablePath : ( ) = > chromiumHeadlessShellExecutable ,
2024-11-14 12:20:44 +00:00
executablePathOrDie : ( sdkLanguage : string ) = > executablePathOrDie ( 'chromium' , chromiumHeadlessShellExecutable , chromiumHeadlessShell . installByDefault , sdkLanguage ) ,
2024-11-05 10:34:00 +01:00
installType : chromiumHeadlessShell.installByDefault ? 'download-by-default' : 'download-on-demand' ,
_validateHostRequirements : ( sdkLanguage : string ) = > this . _validateHostRequirements ( sdkLanguage , chromiumHeadlessShell . dir , [ 'chrome-linux' ] , [ ] , [ 'chrome-win' ] ) ,
downloadURLs : this._downloadURLs ( chromiumHeadlessShell ) ,
2024-10-22 15:47:50 +02:00
browserVersion : chromium.browserVersion ,
2024-11-05 10:34:00 +01:00
_install : ( ) = > this . _downloadExecutable ( chromiumHeadlessShell , chromiumHeadlessShellExecutable ) ,
2024-10-22 15:47:50 +02:00
_dependencyGroup : 'chromium' ,
_isHermeticInstallation : true ,
} ) ;
2024-12-11 18:11:33 -08:00
const chromiumTipOfTreeHeadlessShell = descriptors . find ( d = > d . name === 'chromium-tip-of-tree-headless-shell' ) ! ;
const chromiumTipOfTreeHeadlessShellExecutable = findExecutablePath ( chromiumTipOfTreeHeadlessShell . dir , 'chromium-headless-shell' ) ;
this . _executables . push ( {
type : 'channel' ,
name : 'chromium-tip-of-tree-headless-shell' ,
browserName : 'chromium' ,
directory : chromiumTipOfTreeHeadlessShell.dir ,
executablePath : ( ) = > chromiumTipOfTreeHeadlessShellExecutable ,
executablePathOrDie : ( sdkLanguage : string ) = > executablePathOrDie ( 'chromium' , chromiumTipOfTreeHeadlessShellExecutable , chromiumTipOfTreeHeadlessShell . installByDefault , sdkLanguage ) ,
installType : chromiumTipOfTreeHeadlessShell.installByDefault ? 'download-by-default' : 'download-on-demand' ,
_validateHostRequirements : ( sdkLanguage : string ) = > this . _validateHostRequirements ( sdkLanguage , chromiumTipOfTreeHeadlessShell . dir , [ 'chrome-linux' ] , [ ] , [ 'chrome-win' ] ) ,
downloadURLs : this._downloadURLs ( chromiumTipOfTreeHeadlessShell ) ,
browserVersion : chromium.browserVersion ,
_install : ( ) = > this . _downloadExecutable ( chromiumTipOfTreeHeadlessShell , chromiumTipOfTreeHeadlessShellExecutable ) ,
_dependencyGroup : 'chromium' ,
_isHermeticInstallation : true ,
} ) ;
2022-04-22 10:43:57 -06:00
const chromiumTipOfTree = descriptors . find ( d = > d . name === 'chromium-tip-of-tree' ) ! ;
const chromiumTipOfTreeExecutable = findExecutablePath ( chromiumTipOfTree . dir , 'chromium' ) ;
this . _executables . push ( {
type : 'tool' ,
name : 'chromium-tip-of-tree' ,
browserName : 'chromium' ,
directory : chromiumTipOfTree.dir ,
executablePath : ( ) = > chromiumTipOfTreeExecutable ,
executablePathOrDie : ( sdkLanguage : string ) = > executablePathOrDie ( 'chromium-tip-of-tree' , chromiumTipOfTreeExecutable , chromiumTipOfTree . installByDefault , sdkLanguage ) ,
installType : chromiumTipOfTree.installByDefault ? 'download-by-default' : 'download-on-demand' ,
2024-10-22 15:47:50 +02:00
_validateHostRequirements : ( sdkLanguage : string ) = > this . _validateHostRequirements ( sdkLanguage , chromiumTipOfTree . dir , [ 'chrome-linux' ] , [ ] , [ 'chrome-win' ] ) ,
2022-09-08 09:05:09 -07:00
downloadURLs : this._downloadURLs ( chromiumTipOfTree ) ,
browserVersion : chromiumTipOfTree.browserVersion ,
_install : ( ) = > this . _downloadExecutable ( chromiumTipOfTree , chromiumTipOfTreeExecutable ) ,
2022-04-22 10:43:57 -06:00
_dependencyGroup : 'chromium' ,
2022-04-01 11:05:53 -06:00
_isHermeticInstallation : true ,
2021-07-13 15:57:40 -07:00
} ) ;
2021-02-08 16:02:49 -08:00
2021-07-13 19:03:49 -07:00
this . _executables . push ( this . _createChromiumChannel ( 'chrome' , {
'linux' : '/opt/google/chrome/chrome' ,
'darwin' : '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' ,
'win32' : ` \\ Google \\ Chrome \\ Application \\ chrome.exe ` ,
} , ( ) = > this . _installChromiumChannel ( 'chrome' , {
'linux' : 'reinstall_chrome_stable_linux.sh' ,
'darwin' : 'reinstall_chrome_stable_mac.sh' ,
'win32' : 'reinstall_chrome_stable_win.ps1' ,
} ) ) ) ;
this . _executables . push ( this . _createChromiumChannel ( 'chrome-beta' , {
'linux' : '/opt/google/chrome-beta/chrome' ,
'darwin' : '/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta' ,
'win32' : ` \\ Google \\ Chrome Beta \\ Application \\ chrome.exe ` ,
} , ( ) = > this . _installChromiumChannel ( 'chrome-beta' , {
'linux' : 'reinstall_chrome_beta_linux.sh' ,
'darwin' : 'reinstall_chrome_beta_mac.sh' ,
'win32' : 'reinstall_chrome_beta_win.ps1' ,
} ) ) ) ;
this . _executables . push ( this . _createChromiumChannel ( 'chrome-dev' , {
'linux' : '/opt/google/chrome-unstable/chrome' ,
'darwin' : '/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev' ,
'win32' : ` \\ Google \\ Chrome Dev \\ Application \\ chrome.exe ` ,
} ) ) ;
this . _executables . push ( this . _createChromiumChannel ( 'chrome-canary' , {
'linux' : '' ,
'darwin' : '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary' ,
'win32' : ` \\ Google \\ Chrome SxS \\ Application \\ chrome.exe ` ,
} ) ) ;
this . _executables . push ( this . _createChromiumChannel ( 'msedge' , {
2021-11-01 22:03:51 +01:00
'linux' : '/opt/microsoft/msedge/msedge' ,
2021-07-13 19:03:49 -07:00
'darwin' : '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge' ,
'win32' : ` \\ Microsoft \\ Edge \\ Application \\ msedge.exe ` ,
} , ( ) = > this . _installMSEdgeChannel ( 'msedge' , {
2021-11-01 22:03:51 +01:00
'linux' : 'reinstall_msedge_stable_linux.sh' ,
2021-07-13 19:03:49 -07:00
'darwin' : 'reinstall_msedge_stable_mac.sh' ,
'win32' : 'reinstall_msedge_stable_win.ps1' ,
} ) ) ) ;
this . _executables . push ( this . _createChromiumChannel ( 'msedge-beta' , {
'linux' : '/opt/microsoft/msedge-beta/msedge' ,
'darwin' : '/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta' ,
'win32' : ` \\ Microsoft \\ Edge Beta \\ Application \\ msedge.exe ` ,
} , ( ) = > this . _installMSEdgeChannel ( 'msedge-beta' , {
'darwin' : 'reinstall_msedge_beta_mac.sh' ,
'linux' : 'reinstall_msedge_beta_linux.sh' ,
'win32' : 'reinstall_msedge_beta_win.ps1' ,
} ) ) ) ;
this . _executables . push ( this . _createChromiumChannel ( 'msedge-dev' , {
'linux' : '/opt/microsoft/msedge-dev/msedge' ,
'darwin' : '/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev' ,
'win32' : ` \\ Microsoft \\ Edge Dev \\ Application \\ msedge.exe ` ,
2021-07-16 08:56:51 -08:00
} , ( ) = > this . _installMSEdgeChannel ( 'msedge-dev' , {
'darwin' : 'reinstall_msedge_dev_mac.sh' ,
'linux' : 'reinstall_msedge_dev_linux.sh' ,
'win32' : 'reinstall_msedge_dev_win.ps1' ,
} ) ) ) ;
2021-07-13 19:03:49 -07:00
this . _executables . push ( this . _createChromiumChannel ( 'msedge-canary' , {
'linux' : '' ,
'darwin' : '/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary' ,
'win32' : ` \\ Microsoft \\ Edge SxS \\ Application \\ msedge.exe ` ,
} ) ) ;
2024-09-09 17:22:19 -07:00
this . _executables . push ( this . _createBidiFirefoxChannel ( 'bidi-firefox-stable' , {
'linux' : '/firefox/firefox' ,
'darwin' : '/Firefox.app/Contents/MacOS/firefox' ,
'win32' : '\\core\\firefox.exe' ,
2024-09-04 11:36:52 -07:00
} ) ) ;
2024-09-09 17:22:19 -07:00
this . _executables . push ( this . _createBidiFirefoxChannel ( 'bidi-firefox-beta' , {
'linux' : '/firefox/firefox' ,
'darwin' : '/Firefox.app/Contents/MacOS/firefox' ,
'win32' : '\\core\\firefox.exe' ,
} ) ) ;
this . _executables . push ( this . _createBidiFirefoxChannel ( 'bidi-firefox-nightly' , {
'linux' : '/firefox/firefox' ,
'darwin' : '/Firefox Nightly.app/Contents/MacOS/firefox' ,
'win32' : '\\firefox\\firefox.exe' ,
} ) ) ;
2024-09-07 09:16:42 +02:00
this . _executables . push ( this . _createBidiChannel ( 'bidi-chrome-stable' , {
'linux' : '/opt/google/chrome/chrome' ,
'darwin' : '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' ,
'win32' : ` \\ Google \\ Chrome \\ Application \\ chrome.exe ` ,
} ) ) ;
2024-09-05 14:56:07 -07:00
this . _executables . push ( this . _createBidiChannel ( 'bidi-chrome-canary' , {
'linux' : '' ,
'darwin' : '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary' ,
'win32' : ` \\ Google \\ Chrome SxS \\ Application \\ chrome.exe ` ,
} ) ) ;
2024-09-09 17:22:19 -07:00
this . _executables . push ( {
type : 'browser' ,
name : 'bidi-chromium' ,
browserName : 'bidi' ,
directory : chromium.dir ,
executablePath : ( ) = > chromiumExecutable ,
executablePathOrDie : ( sdkLanguage : string ) = > executablePathOrDie ( 'chromium' , chromiumExecutable , chromium . installByDefault , sdkLanguage ) ,
installType : 'download-on-demand' ,
2024-10-22 15:47:50 +02:00
_validateHostRequirements : ( sdkLanguage : string ) = > this . _validateHostRequirements ( sdkLanguage , chromium . dir , [ 'chrome-linux' ] , [ ] , [ 'chrome-win' ] ) ,
2024-09-09 17:22:19 -07:00
downloadURLs : this._downloadURLs ( chromium ) ,
browserVersion : chromium.browserVersion ,
_install : ( ) = > this . _downloadExecutable ( chromium , chromiumExecutable ) ,
_dependencyGroup : 'chromium' ,
_isHermeticInstallation : true ,
} ) ;
2024-09-04 11:36:52 -07:00
2021-07-13 15:57:40 -07:00
const firefox = descriptors . find ( d = > d . name === 'firefox' ) ! ;
2021-07-13 19:03:49 -07:00
const firefoxExecutable = findExecutablePath ( firefox . dir , 'firefox' ) ;
2021-07-13 15:57:40 -07:00
this . _executables . push ( {
type : 'browser' ,
name : 'firefox' ,
browserName : 'firefox' ,
2021-07-13 19:03:49 -07:00
directory : firefox.dir ,
executablePath : ( ) = > firefoxExecutable ,
2021-08-20 21:32:21 +02:00
executablePathOrDie : ( sdkLanguage : string ) = > executablePathOrDie ( 'firefox' , firefoxExecutable , firefox . installByDefault , sdkLanguage ) ,
2021-07-13 15:57:40 -07:00
installType : firefox.installByDefault ? 'download-by-default' : 'download-on-demand' ,
2024-10-22 15:47:50 +02:00
_validateHostRequirements : ( sdkLanguage : string ) = > this . _validateHostRequirements ( sdkLanguage , firefox . dir , [ 'firefox' ] , [ ] , [ 'firefox' ] ) ,
2022-09-08 09:05:09 -07:00
downloadURLs : this._downloadURLs ( firefox ) ,
browserVersion : firefox.browserVersion ,
_install : ( ) = > this . _downloadExecutable ( firefox , firefoxExecutable ) ,
2021-07-13 19:03:49 -07:00
_dependencyGroup : 'firefox' ,
2022-04-01 11:05:53 -06:00
_isHermeticInstallation : true ,
2021-07-13 15:57:40 -07:00
} ) ;
2021-02-08 16:02:49 -08:00
2021-07-13 15:57:40 -07:00
const firefoxBeta = descriptors . find ( d = > d . name === 'firefox-beta' ) ! ;
2021-07-13 19:03:49 -07:00
const firefoxBetaExecutable = findExecutablePath ( firefoxBeta . dir , 'firefox' ) ;
2021-07-13 15:57:40 -07:00
this . _executables . push ( {
type : 'tool' ,
name : 'firefox-beta' ,
browserName : 'firefox' ,
2021-07-13 19:03:49 -07:00
directory : firefoxBeta.dir ,
executablePath : ( ) = > firefoxBetaExecutable ,
2021-08-20 21:32:21 +02:00
executablePathOrDie : ( sdkLanguage : string ) = > executablePathOrDie ( 'firefox-beta' , firefoxBetaExecutable , firefoxBeta . installByDefault , sdkLanguage ) ,
2021-07-13 15:57:40 -07:00
installType : firefoxBeta.installByDefault ? 'download-by-default' : 'download-on-demand' ,
2024-10-22 15:47:50 +02:00
_validateHostRequirements : ( sdkLanguage : string ) = > this . _validateHostRequirements ( sdkLanguage , firefoxBeta . dir , [ 'firefox' ] , [ ] , [ 'firefox' ] ) ,
2022-09-08 09:05:09 -07:00
downloadURLs : this._downloadURLs ( firefoxBeta ) ,
browserVersion : firefoxBeta.browserVersion ,
_install : ( ) = > this . _downloadExecutable ( firefoxBeta , firefoxBetaExecutable ) ,
2021-07-13 19:03:49 -07:00
_dependencyGroup : 'firefox' ,
2022-04-01 11:05:53 -06:00
_isHermeticInstallation : true ,
2021-07-13 15:57:40 -07:00
} ) ;
2021-02-08 16:02:49 -08:00
2021-07-13 15:57:40 -07:00
const webkit = descriptors . find ( d = > d . name === 'webkit' ) ! ;
2021-07-13 19:03:49 -07:00
const webkitExecutable = findExecutablePath ( webkit . dir , 'webkit' ) ;
2021-07-13 15:57:40 -07:00
const webkitLinuxLddDirectories = [
path . join ( 'minibrowser-gtk' ) ,
path . join ( 'minibrowser-gtk' , 'bin' ) ,
path . join ( 'minibrowser-gtk' , 'lib' ) ,
2022-04-08 11:50:53 -07:00
path . join ( 'minibrowser-gtk' , 'sys' , 'lib' ) ,
2021-07-13 15:57:40 -07:00
path . join ( 'minibrowser-wpe' ) ,
path . join ( 'minibrowser-wpe' , 'bin' ) ,
path . join ( 'minibrowser-wpe' , 'lib' ) ,
2022-04-08 11:50:53 -07:00
path . join ( 'minibrowser-wpe' , 'sys' , 'lib' ) ,
2021-07-13 15:57:40 -07:00
] ;
this . _executables . push ( {
type : 'browser' ,
name : 'webkit' ,
browserName : 'webkit' ,
2021-07-13 19:03:49 -07:00
directory : webkit.dir ,
executablePath : ( ) = > webkitExecutable ,
2021-08-20 21:32:21 +02:00
executablePathOrDie : ( sdkLanguage : string ) = > executablePathOrDie ( 'webkit' , webkitExecutable , webkit . installByDefault , sdkLanguage ) ,
2021-07-13 15:57:40 -07:00
installType : webkit.installByDefault ? 'download-by-default' : 'download-on-demand' ,
2024-10-22 15:47:50 +02:00
_validateHostRequirements : ( sdkLanguage : string ) = > this . _validateHostRequirements ( sdkLanguage , webkit . dir , webkitLinuxLddDirectories , [ 'libGLESv2.so.2' , 'libx264.so' ] , [ '' ] ) ,
2022-09-08 09:05:09 -07:00
downloadURLs : this._downloadURLs ( webkit ) ,
browserVersion : webkit.browserVersion ,
_install : ( ) = > this . _downloadExecutable ( webkit , webkitExecutable ) ,
2021-07-13 19:03:49 -07:00
_dependencyGroup : 'webkit' ,
2022-04-01 11:05:53 -06:00
_isHermeticInstallation : true ,
2021-07-13 15:57:40 -07:00
} ) ;
const ffmpeg = descriptors . find ( d = > d . name === 'ffmpeg' ) ! ;
2021-07-13 19:03:49 -07:00
const ffmpegExecutable = findExecutablePath ( ffmpeg . dir , 'ffmpeg' ) ;
2021-07-13 15:57:40 -07:00
this . _executables . push ( {
type : 'tool' ,
name : 'ffmpeg' ,
browserName : undefined ,
2021-07-13 19:03:49 -07:00
directory : ffmpeg.dir ,
executablePath : ( ) = > ffmpegExecutable ,
2021-08-20 21:32:21 +02:00
executablePathOrDie : ( sdkLanguage : string ) = > executablePathOrDie ( 'ffmpeg' , ffmpegExecutable , ffmpeg . installByDefault , sdkLanguage ) ,
2021-07-13 15:57:40 -07:00
installType : ffmpeg.installByDefault ? 'download-by-default' : 'download-on-demand' ,
2024-01-25 20:55:53 +01:00
_validateHostRequirements : ( ) = > Promise . resolve ( ) ,
2022-09-08 09:05:09 -07:00
downloadURLs : this._downloadURLs ( ffmpeg ) ,
_install : ( ) = > this . _downloadExecutable ( ffmpeg , ffmpegExecutable ) ,
2021-07-13 19:03:49 -07:00
_dependencyGroup : 'tools' ,
2022-04-01 11:05:53 -06:00
_isHermeticInstallation : true ,
2021-07-13 15:57:40 -07:00
} ) ;
2022-10-31 16:08:26 -07:00
const android = descriptors . find ( d = > d . name === 'android' ) ! ;
this . _executables . push ( {
type : 'tool' ,
name : 'android' ,
browserName : undefined ,
directory : android.dir ,
executablePath : ( ) = > undefined ,
executablePathOrDie : ( ) = > '' ,
installType : 'download-on-demand' ,
2024-01-25 20:55:53 +01:00
_validateHostRequirements : ( ) = > Promise . resolve ( ) ,
2022-10-31 16:08:26 -07:00
downloadURLs : this._downloadURLs ( android ) ,
_install : ( ) = > this . _downloadExecutable ( android ) ,
_dependencyGroup : 'tools' ,
_isHermeticInstallation : true ,
} ) ;
2024-09-04 11:36:52 -07:00
this . _executables . push ( {
type : 'browser' ,
name : 'bidi' ,
browserName : 'bidi' ,
directory : undefined ,
executablePath : ( ) = > undefined ,
executablePathOrDie : ( ) = > '' ,
installType : 'none' ,
_validateHostRequirements : ( ) = > Promise . resolve ( ) ,
downloadURLs : [ ] ,
_install : ( ) = > Promise . resolve ( ) ,
_dependencyGroup : 'tools' ,
_isHermeticInstallation : true ,
} ) ;
2021-02-08 16:02:49 -08:00
}
2021-07-13 19:03:49 -07:00
private _createChromiumChannel ( name : ChromiumChannel , lookAt : Record < 'linux' | 'darwin' | 'win32' , string > , install ? : ( ) = > Promise < void > ) : ExecutableImpl {
2021-10-27 18:58:13 +02:00
const executablePath = ( sdkLanguage : string , shouldThrow : boolean ) = > {
2021-07-13 19:03:49 -07:00
const suffix = lookAt [ process . platform as 'linux' | 'darwin' | 'win32' ] ;
if ( ! suffix ) {
if ( shouldThrow )
throw new Error ( ` Chromium distribution ' ${ name } ' is not supported on ${ process . platform } ` ) ;
return undefined ;
}
const prefixes = ( process . platform === 'win32' ? [
process . env . LOCALAPPDATA , process . env . PROGRAMFILES , process . env [ 'PROGRAMFILES(X86)' ]
] . filter ( Boolean ) : [ '' ] ) as string [ ] ;
for ( const prefix of prefixes ) {
const executablePath = path . join ( prefix , suffix ) ;
if ( canAccessFile ( executablePath ) )
return executablePath ;
}
if ( ! shouldThrow )
return undefined ;
const location = prefixes . length ? ` at ${ path . join ( prefixes [ 0 ] , suffix ) } ` : ` ` ;
2021-10-27 18:58:13 +02:00
const installation = install ? ` \ nRun " ${ buildPlaywrightCLICommand ( sdkLanguage , 'install ' + name ) } " ` : '' ;
2021-07-13 19:03:49 -07:00
throw new Error ( ` Chromium distribution ' ${ name } ' is not found ${ location } ${ installation } ` ) ;
} ;
return {
type : 'channel' ,
name ,
browserName : 'chromium' ,
directory : undefined ,
2021-10-27 18:58:13 +02:00
executablePath : ( sdkLanguage : string ) = > executablePath ( sdkLanguage , false ) ,
executablePathOrDie : ( sdkLanguage : string ) = > executablePath ( sdkLanguage , true ) ! ,
2021-07-13 19:03:49 -07:00
installType : install ? 'install-script' : 'none' ,
2024-01-25 20:55:53 +01:00
_validateHostRequirements : ( ) = > Promise . resolve ( ) ,
2022-04-01 11:05:53 -06:00
_isHermeticInstallation : false ,
2021-07-13 19:03:49 -07:00
_install : install ,
} ;
}
2024-09-09 17:22:19 -07:00
private _createBidiFirefoxChannel ( name : BidiChannel , lookAt : Record < 'linux' | 'darwin' | 'win32' , string > , install ? : ( ) = > Promise < void > ) : ExecutableImpl {
const executablePath = ( sdkLanguage : string , shouldThrow : boolean ) = > {
const suffix = lookAt [ process . platform as 'linux' | 'darwin' | 'win32' ] ;
if ( ! suffix ) {
if ( shouldThrow )
throw new Error ( ` Firefox distribution ' ${ name } ' is not supported on ${ process . platform } ` ) ;
return undefined ;
}
const folder = path . resolve ( 'firefox' ) ;
let channelName = 'stable' ;
if ( name . includes ( 'beta' ) )
channelName = 'beta' ;
else if ( name . includes ( 'nightly' ) )
channelName = 'nightly' ;
const installedVersions = fs . readdirSync ( folder ) ;
const found = installedVersions . filter ( e = > e . includes ( channelName ) ) ;
if ( found . length === 1 )
return path . join ( folder , found [ 0 ] , suffix ) ;
if ( found . length > 1 ) {
if ( shouldThrow )
throw new Error ( ` Multiple Firefox installations found for channel ' ${ name } ': ${ found . join ( ', ' ) } ` ) ;
else
return undefined ;
}
if ( shouldThrow )
throw new Error ( ` Cannot find Firefox installation for channel ' ${ name } ' under ${ folder } ` ) ;
return undefined ;
} ;
return {
type : 'channel' ,
name ,
browserName : 'bidi' ,
directory : undefined ,
executablePath : ( sdkLanguage : string ) = > executablePath ( sdkLanguage , false ) ,
executablePathOrDie : ( sdkLanguage : string ) = > executablePath ( sdkLanguage , true ) ! ,
installType : 'none' ,
_validateHostRequirements : ( ) = > Promise . resolve ( ) ,
_isHermeticInstallation : true ,
_install : install ,
} ;
}
2024-09-04 11:36:52 -07:00
private _createBidiChannel ( name : BidiChannel , lookAt : Record < 'linux' | 'darwin' | 'win32' , string > , install ? : ( ) = > Promise < void > ) : ExecutableImpl {
const executablePath = ( sdkLanguage : string , shouldThrow : boolean ) = > {
const suffix = lookAt [ process . platform as 'linux' | 'darwin' | 'win32' ] ;
if ( ! suffix ) {
if ( shouldThrow )
throw new Error ( ` Firefox distribution ' ${ name } ' is not supported on ${ process . platform } ` ) ;
return undefined ;
}
const prefixes = ( process . platform === 'win32' ? [
process . env . LOCALAPPDATA , process . env . PROGRAMFILES , process . env [ 'PROGRAMFILES(X86)' ]
] . filter ( Boolean ) : [ '' ] ) as string [ ] ;
for ( const prefix of prefixes ) {
const executablePath = path . join ( prefix , suffix ) ;
if ( canAccessFile ( executablePath ) )
return executablePath ;
}
if ( ! shouldThrow )
return undefined ;
const location = prefixes . length ? ` at ${ path . join ( prefixes [ 0 ] , suffix ) } ` : ` ` ;
const installation = install ? ` \ nRun " ${ buildPlaywrightCLICommand ( sdkLanguage , 'install ' + name ) } " ` : '' ;
throw new Error ( ` Firefox distribution ' ${ name } ' is not found ${ location } ${ installation } ` ) ;
} ;
return {
type : 'channel' ,
name ,
browserName : 'bidi' ,
directory : undefined ,
executablePath : ( sdkLanguage : string ) = > executablePath ( sdkLanguage , false ) ,
executablePathOrDie : ( sdkLanguage : string ) = > executablePath ( sdkLanguage , true ) ! ,
installType : install ? 'install-script' : 'none' ,
_validateHostRequirements : ( ) = > Promise . resolve ( ) ,
_isHermeticInstallation : false ,
_install : install ,
} ;
}
2021-07-13 19:03:49 -07:00
executables ( ) : Executable [ ] {
return this . _executables ;
}
2021-07-13 15:57:40 -07:00
findExecutable ( name : BrowserName ) : Executable ;
findExecutable ( name : string ) : Executable | undefined ;
findExecutable ( name : string ) : Executable | undefined {
return this . _executables . find ( b = > b . name === name ) ;
fix(installer): retain browsers installed via Playwrigth CLI (#5904)
Browser registry is responsible for 3 things:
1. Remove downloaded browsers if there are no packages that refer to them
2. Install default browsers needed for the current package
3. Install browsers on-demand when used through Playwright CLI
Currently, registry relies on a single "download" field in `browsers.json`
to carry both (1) and (2). However, browsers in (3) are marked as
`download: false` so that they aren't installed automatically in (2), so
auto-remove procedure in (1) removes them on subsequent installation.
One possible approach to fix this would be modifying package's `browsers.json` to
change `download: false` to `true` when browsers are installed with
Playwright CLI. This approach was explored here:
https://github.com/microsoft/playwright/commit/bc04a51800d6d6322e43b7d147fc0ec42181e084
We decided against this since we have a history of issues related to
package modifications after NPM installation. This breaks all
sorts of yarn/npm caching mechanisms.
Instead, this patch is a two-step refactor:
- remove the "download" field in `browsers.json`. Now, all registries
(including old ones from previously-released versions) will retain any
browsers that are mentioned in the `browsers.json`.
- add a new flag "installByDefault", that is **only used** for default
installation.
With this change, the registry tasks are done like this:
- (1) auto-removal: if browser has a back reference, it is retained,
otherwise it is removed from registry
- (2) default installation: use only `installByDefault` to carry default installations
- (3) CLI installation: simply installs browsers. Since we retain
everythings that's referenced in (1), browsers aren't removed.
Fixes #5902
2021-03-22 11:43:29 -07:00
}
2021-09-22 14:43:13 -04:00
defaultExecutables ( ) : Executable [ ] {
return this . _executables . filter ( e = > e . installType === 'download-by-default' ) ;
}
2024-11-14 12:20:44 +00:00
private _dedupe ( executables : Executable [ ] ) : ExecutableImpl [ ] {
return Array . from ( new Set ( executables as ExecutableImpl [ ] ) ) ;
2021-02-08 16:02:49 -08:00
}
2021-07-01 16:17:59 -07:00
2024-10-22 15:47:50 +02:00
private async _validateHostRequirements ( sdkLanguage : string , browserDirectory : string , linuxLddDirectories : string [ ] , dlOpenLibraries : string [ ] , windowsExeAndDllDirectories : string [ ] ) {
2021-07-13 15:57:40 -07:00
if ( os . platform ( ) === 'linux' )
2021-10-27 18:58:13 +02:00
return await validateDependenciesLinux ( sdkLanguage , linuxLddDirectories . map ( d = > path . join ( browserDirectory , d ) ) , dlOpenLibraries ) ;
2021-07-13 15:57:40 -07:00
if ( os . platform ( ) === 'win32' && os . arch ( ) === 'x64' )
return await validateDependenciesWindows ( windowsExeAndDllDirectories . map ( d = > path . join ( browserDirectory , d ) ) ) ;
2021-07-01 16:17:59 -07:00
}
2021-07-01 17:14:04 -07:00
2021-11-25 01:04:42 +01:00
async installDeps ( executablesToInstallDeps : Executable [ ] , dryRun : boolean ) {
2024-11-14 12:20:44 +00:00
const executables = this . _dedupe ( executablesToInstallDeps ) ;
2021-07-13 19:03:49 -07:00
const targets = new Set < DependencyGroup > ( ) ;
2021-07-13 15:57:40 -07:00
for ( const executable of executables ) {
2021-07-13 19:03:49 -07:00
if ( executable . _dependencyGroup )
targets . add ( executable . _dependencyGroup ) ;
2021-07-01 17:14:04 -07:00
}
targets . add ( 'tools' ) ;
if ( os . platform ( ) === 'win32' )
2021-11-25 01:04:42 +01:00
return await installDependenciesWindows ( targets , dryRun ) ;
2021-07-01 17:14:04 -07:00
if ( os . platform ( ) === 'linux' )
2021-11-25 01:04:42 +01:00
return await installDependenciesLinux ( targets , dryRun ) ;
2021-07-01 17:14:04 -07:00
}
2021-07-06 20:59:16 -07:00
2022-04-01 11:05:53 -06:00
async install ( executablesToInstall : Executable [ ] , forceReinstall : boolean ) {
2024-11-14 12:20:44 +00:00
const executables = this . _dedupe ( executablesToInstall ) ;
2021-07-06 20:59:16 -07:00
await fs . promises . mkdir ( registryDirectory , { recursive : true } ) ;
const lockfilePath = path . join ( registryDirectory , '__dirlock' ) ;
const linksDir = path . join ( registryDirectory , '.links' ) ;
2021-11-01 17:48:18 -07:00
let releaseLock ;
2021-07-06 20:59:16 -07:00
try {
2021-11-01 17:48:18 -07:00
releaseLock = await lockfile . lock ( registryDirectory , {
retries : {
// Retry 20 times during 10 minutes with
// exponential back-off.
// See documentation at: https://www.npmjs.com/package/retry#retrytimeoutsoptions
2021-11-16 13:49:01 -08:00
retries : 20 ,
2021-11-01 17:48:18 -07:00
factor : 1.27579 ,
} ,
onCompromised : ( err : Error ) = > {
throw new Error ( ` ${ err . message } Path: ${ lockfilePath } ` ) ;
} ,
lockfilePath ,
} ) ;
2021-07-06 20:59:16 -07:00
// Create a link first, so that cache validation does not remove our own browsers.
await fs . promises . mkdir ( linksDir , { recursive : true } ) ;
await fs . promises . writeFile ( path . join ( linksDir , calculateSha1 ( PACKAGE_PATH ) ) , PACKAGE_PATH ) ;
// Remove stale browsers.
await this . _validateInstallationCache ( linksDir ) ;
2021-07-13 15:57:40 -07:00
// Install browsers for this package.
for ( const executable of executables ) {
2022-04-01 11:05:53 -06:00
if ( ! executable . _install )
2021-07-13 15:57:40 -07:00
throw new Error ( ` ERROR: Playwright does not support installing ${ executable . name } ` ) ;
2022-04-01 11:05:53 -06:00
2022-10-25 12:55:20 -04:00
const { embedderName } = getEmbedderName ( ) ;
if ( ! getAsBooleanFromENV ( 'CI' ) && ! executable . _isHermeticInstallation && ! forceReinstall && executable . executablePath ( embedderName ) ) {
const command = buildPlaywrightCLICommand ( embedderName , 'install --force ' + executable . name ) ;
2022-04-01 11:05:53 -06:00
throw new Error ( '\n' + wrapInASCIIBox ( [
` ATTENTION: " ${ executable . name } " is already installed on the system! ` ,
` ` ,
` " ${ executable . name } " installation is not hermetic; installing newer version ` ,
` requires *removal* of a current installation first. ` ,
` ` ,
` To *uninstall* current version and re-install latest " ${ executable . name } ": ` ,
` ` ,
` - Close all running instances of " ${ executable . name } ", if any ` ,
` - Use "--force" to install browser: ` ,
` ` ,
` ${ command } ` ,
` ` ,
` <3 Playwright Team ` ,
] . join ( '\n' ) , 1 ) ) ;
}
await executable . _install ( ) ;
2021-07-06 20:59:16 -07:00
}
2021-09-08 16:03:16 +03:00
} catch ( e ) {
if ( e . code === 'ELOCKED' ) {
const rmCommand = process . platform === 'win32' ? 'rm -R' : 'rm -rf' ;
throw new Error ( '\n' + wrapInASCIIBox ( [
` An active lockfile is found at: ` ,
` ` ,
` ${ lockfilePath } ` ,
` ` ,
` Either: ` ,
` - wait a few minutes if other Playwright is installing browsers in parallel ` ,
` - remove lock manually with: ` ,
` ` ,
` ${ rmCommand } ${ lockfilePath } ` ,
` ` ,
` <3 Playwright Team ` ,
] . join ( '\n' ) , 1 ) ) ;
} else {
throw e ;
}
2021-07-06 20:59:16 -07:00
} finally {
2021-11-01 17:48:18 -07:00
if ( releaseLock )
await releaseLock ( ) ;
2021-07-06 20:59:16 -07:00
}
}
2023-06-05 18:50:21 +02:00
async uninstall ( all : boolean ) : Promise < { numberOfBrowsersLeft : number } > {
const linksDir = path . join ( registryDirectory , '.links' ) ;
if ( all ) {
const links = await fs . promises . readdir ( linksDir ) . catch ( ( ) = > [ ] ) ;
for ( const link of links )
await fs . promises . unlink ( path . join ( linksDir , link ) ) ;
} else {
await fs . promises . unlink ( path . join ( linksDir , calculateSha1 ( PACKAGE_PATH ) ) ) . catch ( ( ) = > { } ) ;
}
// Remove stale browsers.
await this . _validateInstallationCache ( linksDir ) ;
return {
numberOfBrowsersLeft : ( await fs . promises . readdir ( registryDirectory ) . catch ( ( ) = > [ ] ) ) . filter ( browserDirectory = > isBrowserDirectory ( browserDirectory ) ) . length
} ;
}
2024-01-25 20:55:53 +01:00
async validateHostRequirementsForExecutablesIfNeeded ( executables : Executable [ ] , sdkLanguage : string ) {
if ( getAsBooleanFromENV ( 'PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS' ) ) {
process . stderr . write ( 'Skipping host requirements validation logic because `PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS` env variable is set.\n' ) ;
return ;
}
for ( const executable of executables )
await this . _validateHostRequirementsForExecutableIfNeeded ( executable , sdkLanguage ) ;
}
private async _validateHostRequirementsForExecutableIfNeeded ( executable : Executable , sdkLanguage : string ) {
const kMaximumReValidationPeriod = 30 * 24 * 60 * 60 * 1000 ; // 30 days
// Executable does not require validation.
if ( ! executable . directory )
return ;
const markerFile = path . join ( executable . directory , 'DEPENDENCIES_VALIDATED' ) ;
// Executable is already validated.
if ( await fs . promises . stat ( markerFile ) . then ( stat = > ( Date . now ( ) - stat . mtime . getTime ( ) ) < kMaximumReValidationPeriod ) . catch ( ( ) = > false ) )
return ;
debugLogger . log ( 'install' , ` validating host requirements for " ${ executable . name } " ` ) ;
try {
await executable . _validateHostRequirements ( sdkLanguage ) ;
debugLogger . log ( 'install' , ` validation passed for ${ executable . name } ` ) ;
} catch ( error ) {
debugLogger . log ( 'install' , ` validation failed for ${ executable . name } ` ) ;
throw error ;
}
await fs . promises . writeFile ( markerFile , '' ) . catch ( ( ) = > { } ) ;
}
2022-09-08 09:05:09 -07:00
private _downloadURLs ( descriptor : BrowsersJSONDescriptor ) : string [ ] {
2022-10-31 16:08:26 -07:00
const paths = ( DOWNLOAD_PATHS as any ) [ descriptor . name ] ;
const downloadPathTemplate : string | undefined = paths [ hostPlatform ] || paths [ '<unknown>' ] ;
2022-09-08 09:05:09 -07:00
if ( ! downloadPathTemplate )
return [ ] ;
2021-11-29 22:23:33 +05:30
const downloadPath = util . format ( downloadPathTemplate , descriptor . revision ) ;
2022-06-17 20:47:32 +02:00
let downloadURLs = PLAYWRIGHT_CDN_MIRRORS . map ( mirror = > ` ${ mirror } / ${ downloadPath } ` ) ;
2022-09-08 09:05:09 -07:00
let downloadHostEnv ;
if ( descriptor . name . startsWith ( 'chromium' ) )
downloadHostEnv = 'PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST' ;
else if ( descriptor . name . startsWith ( 'firefox' ) )
downloadHostEnv = 'PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST' ;
else if ( descriptor . name . startsWith ( 'webkit' ) )
downloadHostEnv = 'PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST' ;
2022-06-17 20:47:32 +02:00
const customHostOverride = ( downloadHostEnv && getFromENV ( downloadHostEnv ) ) || getFromENV ( 'PLAYWRIGHT_DOWNLOAD_HOST' ) ;
if ( customHostOverride )
downloadURLs = [ ` ${ customHostOverride } / ${ downloadPath } ` ] ;
2022-09-08 09:05:09 -07:00
return downloadURLs ;
}
2022-10-31 16:08:26 -07:00
private async _downloadExecutable ( descriptor : BrowsersJSONDescriptor , executablePath? : string ) {
2022-09-08 09:05:09 -07:00
const downloadURLs = this . _downloadURLs ( descriptor ) ;
2022-10-31 16:08:26 -07:00
if ( ! downloadURLs . length )
2022-09-08 09:05:09 -07:00
throw new Error ( ` ERROR: Playwright does not support ${ descriptor . name } on ${ hostPlatform } ` ) ;
2023-10-16 10:49:57 -07:00
if ( ! isOfficiallySupportedPlatform )
logPolitely ( ` BEWARE: your OS is not officially supported by Playwright; downloading fallback build for ${ hostPlatform } . ` ) ;
2024-10-23 18:14:55 +02:00
if ( descriptor . hasRevisionOverride ) {
const message = ` You are using a frozen ${ descriptor . name } browser which does not receive updates anymore on ${ hostPlatform } . Please update to the latest version of your operating system to test up-to-date browsers. ` ;
if ( process . env . GITHUB_ACTIONS )
console . log ( ` ::warning title=Playwright:: ${ message } ` ) ; // eslint-disable-line no-console
else
logPolitely ( message ) ;
}
2022-05-05 05:17:13 -06:00
const displayName = descriptor . name . split ( '-' ) . map ( word = > {
return word === 'ffmpeg' ? 'FFMPEG' : word . charAt ( 0 ) . toUpperCase ( ) + word . slice ( 1 ) ;
} ) . join ( ' ' ) ;
const title = descriptor . browserVersion
? ` ${ displayName } ${ descriptor . browserVersion } (playwright build v ${ descriptor . revision } ) `
: ` ${ displayName } playwright build v ${ descriptor . revision } ` ;
2021-07-13 15:57:40 -07:00
const downloadFileName = ` playwright-download- ${ descriptor . name } - ${ hostPlatform } - ${ descriptor . revision } .zip ` ;
2022-10-19 13:06:35 -07:00
const downloadConnectionTimeoutEnv = getFromENV ( 'PLAYWRIGHT_DOWNLOAD_CONNECTION_TIMEOUT' ) ;
const downloadConnectionTimeout = + ( downloadConnectionTimeoutEnv || '0' ) || 30 _000 ;
await downloadBrowserWithProgressBar ( title , descriptor . dir , executablePath , downloadURLs , downloadFileName , downloadConnectionTimeout ) . catch ( e = > {
2021-07-13 15:57:40 -07:00
throw new Error ( ` Failed to download ${ title } , caused by \ n ${ e . stack } ` ) ;
} ) ;
}
2021-07-16 08:56:51 -08:00
private async _installMSEdgeChannel ( channel : 'msedge' | 'msedge-beta' | 'msedge-dev' , scripts : Record < 'linux' | 'darwin' | 'win32' , string > ) {
2021-07-13 19:03:49 -07:00
const scriptArgs : string [ ] = [ ] ;
if ( process . platform !== 'linux' ) {
2022-07-25 16:55:24 -07:00
const products = lowercaseAllKeys ( JSON . parse ( await fetchData ( { url : 'https://edgeupdates.microsoft.com/api/products' } ) ) ) ;
2021-07-16 08:56:51 -08:00
const productName = {
'msedge' : 'Stable' ,
'msedge-beta' : 'Beta' ,
'msedge-dev' : 'Dev' ,
} [ channel ] ;
2022-07-25 16:55:24 -07:00
const product = products . find ( ( product : any ) = > product . product === productName ) ;
2021-07-13 19:03:49 -07:00
const searchConfig = ( {
2021-09-27 18:58:08 +02:00
darwin : { platform : 'MacOS' , arch : 'universal' , artifact : 'pkg' } ,
2021-10-27 23:48:17 +02:00
win32 : { platform : 'Windows' , arch : 'x64' , artifact : 'msi' } ,
2021-07-13 19:03:49 -07:00
} as any ) [ process . platform ] ;
2023-11-02 23:30:40 +01:00
const release = searchConfig ? product . releases . find ( ( release : any ) = > release . platform === searchConfig . platform && release . architecture === searchConfig . arch && release . artifacts . length > 0 ) : null ;
2022-07-25 16:55:24 -07:00
const artifact = release ? release . artifacts . find ( ( artifact : any ) = > artifact . artifactname === searchConfig . artifact ) : null ;
2021-07-13 19:03:49 -07:00
if ( artifact )
2022-07-25 16:55:24 -07:00
scriptArgs . push ( artifact . location /* url */ ) ;
2021-07-13 19:03:49 -07:00
else
throw new Error ( ` Cannot install ${ channel } on ${ process . platform } ` ) ;
}
await this . _installChromiumChannel ( channel , scripts , scriptArgs ) ;
}
private async _installChromiumChannel ( channel : string , scripts : Record < 'linux' | 'darwin' | 'win32' , string > , scriptArgs : string [ ] = [ ] ) {
const scriptName = scripts [ process . platform as 'linux' | 'darwin' | 'win32' ] ;
if ( ! scriptName )
throw new Error ( ` Cannot install ${ channel } on ${ process . platform } ` ) ;
2021-12-06 14:49:22 -08:00
const cwd = BIN_PATH ;
2021-10-04 10:25:15 +02:00
const isPowerShell = scriptName . endsWith ( '.ps1' ) ;
2021-12-06 14:49:22 -08:00
if ( isPowerShell ) {
const args = [
'-ExecutionPolicy' , 'Bypass' , '-File' ,
path . join ( BIN_PATH , scriptName ) ,
. . . scriptArgs
] ;
const { code } = await spawnAsync ( 'powershell.exe' , args , { cwd , stdio : 'inherit' } ) ;
if ( code !== 0 )
throw new Error ( ` Failed to install ${ channel } ` ) ;
} else {
2022-01-30 01:56:58 +08:00
const { command , args , elevatedPermissions } = await transformCommandsForRoot ( [ ` bash " ${ path . join ( BIN_PATH , scriptName ) } " ${ scriptArgs . join ( '' ) } ` ] ) ;
2021-12-06 14:49:22 -08:00
if ( elevatedPermissions )
console . log ( 'Switching to root user to install dependencies...' ) ; // eslint-disable-line no-console
const { code } = await spawnAsync ( command , args , { cwd , stdio : 'inherit' } ) ;
if ( code !== 0 )
throw new Error ( ` Failed to install ${ channel } ` ) ;
}
2021-07-13 19:03:49 -07:00
}
2021-07-06 20:59:16 -07:00
private async _validateInstallationCache ( linksDir : string ) {
// 1. Collect used downloads and package descriptors.
const usedBrowserPaths : Set < string > = new Set ( ) ;
for ( const fileName of await fs . promises . readdir ( linksDir ) ) {
const linkPath = path . join ( linksDir , fileName ) ;
let linkTarget = '' ;
try {
linkTarget = ( await fs . promises . readFile ( linkPath ) ) . toString ( ) ;
2021-08-31 16:50:17 +02:00
const browsersJSON = require ( path . join ( linkTarget , 'browsers.json' ) ) ;
const descriptors = readDescriptors ( browsersJSON ) ;
2021-07-13 15:57:40 -07:00
for ( const browserName of allDownloadable ) {
// We retain browsers if they are found in the descriptor.
// Note, however, that there are older versions out in the wild that rely on
// the "download" field in the browser descriptor and use its value
// to retain and download browsers.
// As of v1.10, we decided to abandon "download" field.
2021-07-09 16:10:23 -07:00
const descriptor = descriptors . find ( d = > d . name === browserName ) ;
if ( ! descriptor )
2021-07-06 20:59:16 -07:00
continue ;
2021-07-13 15:57:40 -07:00
const usedBrowserPath = descriptor . dir ;
2021-07-09 16:10:23 -07:00
const browserRevision = parseInt ( descriptor . revision , 10 ) ;
2021-07-06 20:59:16 -07:00
// Old browser installations don't have marker file.
2023-03-16 16:56:25 -07:00
// We switched chromium from 999999 to 1000, 300000 is the new Y2K.
const shouldHaveMarkerFile = ( browserName === 'chromium' && ( browserRevision >= 786218 || browserRevision < 300000 ) ) ||
2021-07-06 20:59:16 -07:00
( browserName === 'firefox' && browserRevision >= 1128 ) ||
( browserName === 'webkit' && browserRevision >= 1307 ) ||
// All new applications have a marker file right away.
( browserName !== 'firefox' && browserName !== 'chromium' && browserName !== 'webkit' ) ;
2023-06-16 20:40:15 +02:00
if ( ! shouldHaveMarkerFile || ( await existsAsync ( browserDirectoryToMarkerFilePath ( usedBrowserPath ) ) ) )
2021-07-06 20:59:16 -07:00
usedBrowserPaths . add ( usedBrowserPath ) ;
}
} catch ( e ) {
await fs . promises . unlink ( linkPath ) . catch ( e = > { } ) ;
}
}
// 2. Delete all unused browsers.
if ( ! getAsBooleanFromENV ( 'PLAYWRIGHT_SKIP_BROWSER_GC' ) ) {
let downloadedBrowsers = ( await fs . promises . readdir ( registryDirectory ) ) . map ( file = > path . join ( registryDirectory , file ) ) ;
downloadedBrowsers = downloadedBrowsers . filter ( file = > isBrowserDirectory ( file ) ) ;
const directories = new Set < string > ( downloadedBrowsers ) ;
for ( const browserDirectory of usedBrowserPaths )
directories . delete ( browserDirectory ) ;
for ( const directory of directories )
logPolitely ( 'Removing unused browser at ' + directory ) ;
await removeFolders ( [ . . . directories ] ) ;
}
}
}
2023-06-16 20:40:15 +02:00
export function browserDirectoryToMarkerFilePath ( browserDirectory : string ) : string {
2021-07-06 20:59:16 -07:00
return path . join ( browserDirectory , 'INSTALLATION_COMPLETE' ) ;
}
2021-10-27 18:58:13 +02:00
export function buildPlaywrightCLICommand ( sdkLanguage : string , parameters : string ) : string {
2021-08-20 21:32:21 +02:00
switch ( sdkLanguage ) {
case 'python' :
return ` playwright ${ parameters } ` ;
case 'java' :
2023-01-05 19:55:07 +01:00
return ` mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args=" ${ parameters } " ` ;
2021-08-20 21:32:21 +02:00
case 'csharp' :
2022-08-14 20:01:00 +02:00
return ` pwsh bin/Debug/netX/playwright.ps1 ${ parameters } ` ;
2023-08-17 17:53:08 +02:00
default : {
const packageManagerCommand = getPackageManagerExecCommand ( ) ;
return ` ${ packageManagerCommand } playwright ${ parameters } ` ;
}
2021-08-20 21:32:21 +02:00
}
}
2021-09-22 14:43:13 -04:00
export async function installBrowsersForNpmInstall ( browsers : string [ ] ) {
2021-07-06 20:59:16 -07:00
// PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD should have a value of 0 or 1
if ( getAsBooleanFromENV ( 'PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD' ) ) {
logPolitely ( 'Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set' ) ;
return false ;
}
2021-09-22 14:43:13 -04:00
const executables : Executable [ ] = [ ] ;
for ( const browserName of browsers ) {
const executable = registry . findExecutable ( browserName ) ;
if ( ! executable || executable . installType === 'none' )
throw new Error ( ` Cannot install ${ browserName } ` ) ;
executables . push ( executable ) ;
}
2022-04-01 11:05:53 -06:00
await registry . install ( executables , false /* forceReinstall */ ) ;
2021-02-08 16:02:49 -08:00
}
2021-07-09 16:10:23 -07:00
2021-10-01 17:06:00 -07:00
export function findChromiumChannel ( sdkLanguage : string ) : string | undefined {
// Fall back to the stable channels of popular vendors to work out of the box.
// Null means no installation and no channels found.
let channel = null ;
for ( const name of [ 'chromium' , 'chrome' , 'msedge' ] ) {
try {
registry . findExecutable ( name ) ! . executablePathOrDie ( sdkLanguage ) ;
channel = name === 'chromium' ? undefined : name ;
break ;
} catch ( e ) {
}
}
if ( channel === null ) {
const installCommand = buildPlaywrightCLICommand ( sdkLanguage , ` install chromium ` ) ;
const prettyMessage = [
` No chromium-based browser found on the system. ` ,
` Please run the following command to download one: ` ,
` ` ,
` ${ installCommand } ` ,
` ` ,
` <3 Playwright Team ` ,
] . join ( '\n' ) ;
throw new Error ( '\n' + wrapInASCIIBox ( prettyMessage , 1 ) ) ;
}
return channel ;
}
2022-07-25 16:55:24 -07:00
function lowercaseAllKeys ( json : any ) : any {
if ( typeof json !== 'object' || ! json )
return json ;
if ( Array . isArray ( json ) )
return json . map ( lowercaseAllKeys ) ;
const result : any = { } ;
for ( const [ key , value ] of Object . entries ( json ) )
result [ key . toLowerCase ( ) ] = lowercaseAllKeys ( value ) ;
return result ;
}
2022-04-06 21:21:27 -08:00
export const registry = new Registry ( require ( '../../../browsers.json' ) ) ;