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:
bc04a51800
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
This commit is contained in:
parent
6dd4d756dc
commit
2064d27dc6
|
|
@ -4,17 +4,17 @@
|
||||||
{
|
{
|
||||||
"name": "chromium",
|
"name": "chromium",
|
||||||
"revision": "857950",
|
"revision": "857950",
|
||||||
"download": true
|
"installByDefault": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox",
|
"name": "firefox",
|
||||||
"revision": "1238",
|
"revision": "1238",
|
||||||
"download": true
|
"installByDefault": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webkit",
|
"name": "webkit",
|
||||||
"revision": "1446",
|
"revision": "1446",
|
||||||
"download": true,
|
"installByDefault": true,
|
||||||
"revisionOverrides": {
|
"revisionOverrides": {
|
||||||
"mac10.14": "1443"
|
"mac10.14": "1443"
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
{
|
{
|
||||||
"name": "ffmpeg",
|
"name": "ffmpeg",
|
||||||
"revision": "1005",
|
"revision": "1005",
|
||||||
"download": true
|
"installByDefault": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ if (!args.some(arg => arg === '--no-cleanup')) {
|
||||||
// 5. Generate browsers.json
|
// 5. Generate browsers.json
|
||||||
const browsersJSON = require(path.join(ROOT_PATH, 'browsers.json'));
|
const browsersJSON = require(path.join(ROOT_PATH, 'browsers.json'));
|
||||||
for (const browser of browsersJSON.browsers)
|
for (const browser of browsersJSON.browsers)
|
||||||
browser.download = package.browsers.includes(browser.name);
|
browser.installByDefault = package.browsers.includes(browser.name);
|
||||||
await writeToPackage('browsers.json', JSON.stringify(browsersJSON, null, 2));
|
await writeToPackage('browsers.json', JSON.stringify(browsersJSON, null, 2));
|
||||||
|
|
||||||
// 6. Bake commit SHA into the package
|
// 6. Bake commit SHA into the package
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||||
|
|
||||||
const PACKAGE_PATH = path.join(__dirname, '..', '..');
|
const PACKAGE_PATH = path.join(__dirname, '..', '..');
|
||||||
|
|
||||||
export async function installBrowsersWithProgressBar(browserNames: BrowserName[] = allBrowserNames) {
|
export async function installBrowsersWithProgressBar(browserNames: BrowserName[] = Registry.currentPackageRegistry().installByDefault()) {
|
||||||
// PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD should have a value of 0 or 1
|
// PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD should have a value of 0 or 1
|
||||||
if (getAsBooleanFromENV('PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD')) {
|
if (getAsBooleanFromENV('PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD')) {
|
||||||
browserFetcher.logPolitely('Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set');
|
browserFetcher.logPolitely('Skipping browsers download because `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` env variable is set');
|
||||||
|
|
@ -74,7 +74,7 @@ async function validateCache(linksDir: string, browserNames: BrowserName[]) {
|
||||||
linkTarget = (await fsReadFileAsync(linkPath)).toString();
|
linkTarget = (await fsReadFileAsync(linkPath)).toString();
|
||||||
const linkRegistry = new Registry(linkTarget);
|
const linkRegistry = new Registry(linkTarget);
|
||||||
for (const browserName of allBrowserNames) {
|
for (const browserName of allBrowserNames) {
|
||||||
if (!linkRegistry.shouldDownload(browserName))
|
if (!linkRegistry.shouldRetain(browserName))
|
||||||
continue;
|
continue;
|
||||||
const usedBrowserPath = linkRegistry.browserDirectory(browserName);
|
const usedBrowserPath = linkRegistry.browserDirectory(browserName);
|
||||||
const browserRevision = linkRegistry.revision(browserName);
|
const browserRevision = linkRegistry.revision(browserName);
|
||||||
|
|
@ -105,10 +105,8 @@ async function validateCache(linksDir: string, browserNames: BrowserName[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Install missing browsers for this package.
|
// 3. Install missing browsers for this package.
|
||||||
const myRegistry = new Registry(PACKAGE_PATH);
|
const myRegistry = Registry.currentPackageRegistry();
|
||||||
for (const browserName of browserNames) {
|
for (const browserName of browserNames) {
|
||||||
if (!myRegistry.shouldDownload(browserName))
|
|
||||||
continue;
|
|
||||||
await browserFetcher.downloadBrowserWithProgressBar(myRegistry, browserName).catch(e => {
|
await browserFetcher.downloadBrowserWithProgressBar(myRegistry, browserName).catch(e => {
|
||||||
throw new Error(`Failed to download ${browserName}, caused by\n${e.stack}`);
|
throw new Error(`Failed to download ${browserName}, caused by\n${e.stack}`);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,13 @@ import { assert, getFromENV } from './utils';
|
||||||
export type BrowserName = 'chromium'|'webkit'|'firefox'|'ffmpeg';
|
export type BrowserName = 'chromium'|'webkit'|'firefox'|'ffmpeg';
|
||||||
export const allBrowserNames: BrowserName[] = ['chromium', 'webkit', 'firefox', 'ffmpeg'];
|
export const allBrowserNames: BrowserName[] = ['chromium', 'webkit', 'firefox', 'ffmpeg'];
|
||||||
|
|
||||||
|
const PACKAGE_PATH = path.join(__dirname, '..', '..');
|
||||||
|
|
||||||
type BrowserPlatform = 'win32'|'win64'|'mac10.13'|'mac10.14'|'mac10.15'|'mac11'|'mac11-arm64'|'ubuntu18.04'|'ubuntu20.04';
|
type BrowserPlatform = 'win32'|'win64'|'mac10.13'|'mac10.14'|'mac10.15'|'mac11'|'mac11-arm64'|'ubuntu18.04'|'ubuntu20.04';
|
||||||
type BrowserDescriptor = {
|
type BrowserDescriptor = {
|
||||||
name: BrowserName,
|
name: BrowserName,
|
||||||
revision: string,
|
revision: string,
|
||||||
download: boolean,
|
installByDefault: boolean,
|
||||||
browserDirectory: string,
|
browserDirectory: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -199,9 +201,17 @@ export function isBrowserDirectory(browserDirectory: string): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentPackageRegistry: Registry | undefined = undefined;
|
||||||
|
|
||||||
export class Registry {
|
export class Registry {
|
||||||
private _descriptors: BrowserDescriptor[];
|
private _descriptors: BrowserDescriptor[];
|
||||||
|
|
||||||
|
static currentPackageRegistry() {
|
||||||
|
if (!currentPackageRegistry)
|
||||||
|
currentPackageRegistry = new Registry(PACKAGE_PATH);
|
||||||
|
return currentPackageRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(packagePath: string) {
|
constructor(packagePath: string) {
|
||||||
const browsersJSON = JSON.parse(fs.readFileSync(path.join(packagePath, 'browsers.json'), 'utf8'));
|
const browsersJSON = JSON.parse(fs.readFileSync(path.join(packagePath, 'browsers.json'), 'utf8'));
|
||||||
this._descriptors = browsersJSON['browsers'].map((obj: any) => {
|
this._descriptors = browsersJSON['browsers'].map((obj: any) => {
|
||||||
|
|
@ -212,7 +222,7 @@ export class Registry {
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
revision,
|
revision,
|
||||||
download: obj.download,
|
installByDefault: !!obj.installByDefault,
|
||||||
browserDirectory,
|
browserDirectory,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -283,10 +293,17 @@ export class Registry {
|
||||||
return util.format(urlTemplate, downloadHost, browser.revision);
|
return util.format(urlTemplate, downloadHost, browser.revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldDownload(browserName: BrowserName): boolean {
|
shouldRetain(browserName: BrowserName): boolean {
|
||||||
// Older versions do not have "download" field. We assume they need all browsers
|
// We retain browsers if they are found in the descriptor.
|
||||||
// from the list. So we want to skip all browsers that are explicitly marked as "download: false".
|
// 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.
|
||||||
const browser = this._descriptors.find(browser => browser.name === browserName);
|
const browser = this._descriptors.find(browser => browser.name === browserName);
|
||||||
return !!browser && browser.download !== false;
|
return !!browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
installByDefault(): BrowserName[] {
|
||||||
|
return this._descriptors.filter(browser => browser.installByDefault).map(browser => browser.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue