mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat: support local aliases when launching a browser (#11947)
This commit is contained in:
parent
bf71b34fca
commit
561e4cd6ee
@ -23,3 +23,10 @@ The constructor for this class is marked as internal. Third-party code should no
|
||||
| executablePath | <code>readonly</code> | string | |
|
||||
| path | <code>readonly</code> | string | Path to the root of the installation folder. Use [computeExecutablePath()](./browsers.computeexecutablepath.md) to get the path to the executable binary. |
|
||||
| platform | | [BrowserPlatform](./browsers.browserplatform.md) | |
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Modifiers | Description |
|
||||
| ----------------------------------------------------------------------- | --------- | ----------- |
|
||||
| [readMetadata()](./browsers.installedbrowser.readmetadata.md) | | |
|
||||
| [writeMetadata(metadata)](./browsers.installedbrowser.writemetadata.md) | | |
|
||||
|
17
docs/browsers-api/browsers.installedbrowser.readmetadata.md
Normal file
17
docs/browsers-api/browsers.installedbrowser.readmetadata.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
sidebar_label: InstalledBrowser.readMetadata
|
||||
---
|
||||
|
||||
# InstalledBrowser.readMetadata() method
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
class InstalledBrowser {
|
||||
readMetadata(): Metadata;
|
||||
}
|
||||
```
|
||||
|
||||
**Returns:**
|
||||
|
||||
Metadata
|
23
docs/browsers-api/browsers.installedbrowser.writemetadata.md
Normal file
23
docs/browsers-api/browsers.installedbrowser.writemetadata.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
sidebar_label: InstalledBrowser.writeMetadata
|
||||
---
|
||||
|
||||
# InstalledBrowser.writeMetadata() method
|
||||
|
||||
#### Signature:
|
||||
|
||||
```typescript
|
||||
class InstalledBrowser {
|
||||
writeMetadata(metadata: Metadata): void;
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------- | ----------- |
|
||||
| metadata | Metadata | |
|
||||
|
||||
**Returns:**
|
||||
|
||||
void
|
@ -12,12 +12,13 @@ export interface InstallOptions
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description | Default |
|
||||
| ------------------------ | --------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| baseUrl | <code>optional</code> | string | Determines the host that will be used for downloading. | <p>Either</p><p>- https://storage.googleapis.com/chrome-for-testing-public or - https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central</p> |
|
||||
| browser | | [Browser](./browsers.browser.md) | Determines which browser to install. | |
|
||||
| buildId | | string | Determines which buildId to download. BuildId should uniquely identify binaries and they are used for caching. | |
|
||||
| cacheDir | | string | Determines the path to download browsers to. | |
|
||||
| downloadProgressCallback | <code>optional</code> | (downloadedBytes: number, totalBytes: number) => void | Provides information about the progress of the download. | |
|
||||
| platform | <code>optional</code> | [BrowserPlatform](./browsers.browserplatform.md) | Determines which platform the browser will be suited for. | **Auto-detected.** |
|
||||
| unpack | <code>optional</code> | boolean | Whether to unpack and install browser archives. | <code>true</code> |
|
||||
| Property | Modifiers | Type | Description | Default |
|
||||
| ------------------------ | --------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| baseUrl | <code>optional</code> | string | Determines the host that will be used for downloading. | <p>Either</p><p>- https://storage.googleapis.com/chrome-for-testing-public or - https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central</p> |
|
||||
| browser | | [Browser](./browsers.browser.md) | Determines which browser to install. | |
|
||||
| buildId | | string | Determines which buildId to download. BuildId should uniquely identify binaries and they are used for caching. | |
|
||||
| buildIdAlias | <code>optional</code> | string | An alias for the provided <code>buildId</code>. It will be used to maintain local metadata to support aliases in the <code>launch</code> command. | |
|
||||
| cacheDir | | string | Determines the path to download browsers to. | |
|
||||
| downloadProgressCallback | <code>optional</code> | (downloadedBytes: number, totalBytes: number) => void | Provides information about the progress of the download. | |
|
||||
| platform | <code>optional</code> | [BrowserPlatform](./browsers.browserplatform.md) | Determines which platform the browser will be suited for. | **Auto-detected.** |
|
||||
| unpack | <code>optional</code> | boolean | Whether to unpack and install browser archives. | <code>true</code> |
|
||||
|
@ -238,6 +238,7 @@ export class CLI {
|
||||
}
|
||||
args.browser.buildId = pinnedVersion;
|
||||
}
|
||||
const originalBuildId = args.browser.buildId;
|
||||
args.browser.buildId = await resolveBuildId(
|
||||
args.browser.name,
|
||||
args.platform,
|
||||
@ -253,6 +254,10 @@ export class CLI {
|
||||
args.browser.buildId
|
||||
),
|
||||
baseUrl: args.baseUrl,
|
||||
buildIdAlias:
|
||||
originalBuildId !== args.browser.buildId
|
||||
? originalBuildId
|
||||
: undefined,
|
||||
});
|
||||
console.log(
|
||||
`${args.browser.name}@${
|
||||
|
@ -8,13 +8,18 @@ import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import debug from 'debug';
|
||||
|
||||
import {
|
||||
Browser,
|
||||
type BrowserPlatform,
|
||||
executablePathByBrowser,
|
||||
getVersionComparator,
|
||||
} from './browser-data/browser-data.js';
|
||||
import {detectBrowserPlatform} from './detectPlatform.js';
|
||||
|
||||
const debugCache = debug('puppeteer:browsers:cache');
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -57,6 +62,14 @@ export class InstalledBrowser {
|
||||
this.buildId
|
||||
);
|
||||
}
|
||||
|
||||
readMetadata(): Metadata {
|
||||
return this.#cache.readMetadata(this.browser);
|
||||
}
|
||||
|
||||
writeMetadata(metadata: Metadata): void {
|
||||
this.#cache.writeMetadata(this.browser, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,6 +93,11 @@ export interface ComputeExecutablePathOptions {
|
||||
buildId: string;
|
||||
}
|
||||
|
||||
export interface Metadata {
|
||||
// Maps an alias (canary/latest/dev/etc.) to a buildId.
|
||||
aliases: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache used by Puppeteer relies on the following structure:
|
||||
*
|
||||
@ -112,6 +130,39 @@ export class Cache {
|
||||
return path.join(this.#rootDir, browser);
|
||||
}
|
||||
|
||||
metadataFile(browser: Browser): string {
|
||||
return path.join(this.browserRoot(browser), '.metadata');
|
||||
}
|
||||
|
||||
readMetadata(browser: Browser): Metadata {
|
||||
const metatadaPath = this.metadataFile(browser);
|
||||
if (!fs.existsSync(metatadaPath)) {
|
||||
return {aliases: {}};
|
||||
}
|
||||
// TODO: add type-safe parsing.
|
||||
const data = JSON.parse(fs.readFileSync(metatadaPath, 'utf8'));
|
||||
if (typeof data !== 'object') {
|
||||
throw new Error('.metadata is not an object');
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
writeMetadata(browser: Browser, metadata: Metadata): void {
|
||||
const metatadaPath = this.metadataFile(browser);
|
||||
fs.mkdirSync(path.dirname(metatadaPath), {recursive: true});
|
||||
fs.writeFileSync(metatadaPath, JSON.stringify(metadata, null, 2));
|
||||
}
|
||||
|
||||
resolveAlias(browser: Browser, alias: string): string | undefined {
|
||||
const metadata = this.readMetadata(browser);
|
||||
if (alias === 'latest') {
|
||||
return Object.values(metadata.aliases || {})
|
||||
.sort(getVersionComparator(browser))
|
||||
.at(-1);
|
||||
}
|
||||
return metadata.aliases[alias];
|
||||
}
|
||||
|
||||
installationDir(
|
||||
browser: Browser,
|
||||
platform: BrowserPlatform,
|
||||
@ -134,6 +185,12 @@ export class Cache {
|
||||
platform: BrowserPlatform,
|
||||
buildId: string
|
||||
): void {
|
||||
const metadata = this.readMetadata(browser);
|
||||
for (const alias of Object.keys(metadata.aliases)) {
|
||||
if (metadata.aliases[alias] === buildId) {
|
||||
delete metadata.aliases[alias];
|
||||
}
|
||||
}
|
||||
fs.rmSync(this.installationDir(browser, platform, buildId), {
|
||||
force: true,
|
||||
recursive: true,
|
||||
@ -180,6 +237,12 @@ export class Cache {
|
||||
`Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`
|
||||
);
|
||||
}
|
||||
try {
|
||||
options.buildId =
|
||||
this.resolveAlias(options.browser, options.buildId) ?? options.buildId;
|
||||
} catch {
|
||||
debugCache('could not read .metadata file for the browser');
|
||||
}
|
||||
const installationDir = this.installationDir(
|
||||
options.browser,
|
||||
options.platform,
|
||||
|
@ -62,6 +62,13 @@ export interface InstallOptions {
|
||||
* binaries and they are used for caching.
|
||||
*/
|
||||
buildId: string;
|
||||
/**
|
||||
* An alias for the provided `buildId`. It will be used to maintain local
|
||||
* metadata to support aliases in the `launch` command.
|
||||
*
|
||||
* @example 'canary'
|
||||
*/
|
||||
buildIdAlias?: string;
|
||||
/**
|
||||
* Provides information about the progress of the download.
|
||||
*/
|
||||
@ -233,17 +240,23 @@ async function installUrl(
|
||||
} finally {
|
||||
debugTimeEnd('extract');
|
||||
}
|
||||
const installedBrowser = new InstalledBrowser(
|
||||
cache,
|
||||
options.browser,
|
||||
options.buildId,
|
||||
options.platform
|
||||
);
|
||||
if (options.buildIdAlias) {
|
||||
const metadata = installedBrowser.readMetadata();
|
||||
metadata.aliases[options.buildIdAlias] = options.buildId;
|
||||
installedBrowser.writeMetadata(metadata);
|
||||
}
|
||||
return installedBrowser;
|
||||
} finally {
|
||||
if (existsSync(archivePath)) {
|
||||
await unlink(archivePath);
|
||||
}
|
||||
}
|
||||
return new InstalledBrowser(
|
||||
cache,
|
||||
options.browser,
|
||||
options.buildId,
|
||||
options.platform
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
72
packages/browsers/test/src/Cache.spec.ts
Normal file
72
packages/browsers/test/src/Cache.spec.ts
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2024 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import assert from 'assert';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import {Browser, Cache} from '../../lib/cjs/main.js';
|
||||
|
||||
describe('Cache', () => {
|
||||
let tmpDir = '/tmp/puppeteer-browsers-test';
|
||||
let cache: Cache;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
|
||||
cache = new Cache(tmpDir);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cache.clear();
|
||||
});
|
||||
|
||||
it('return empty metadata if .metadata file does not exist', async function () {
|
||||
assert.deepStrictEqual(cache.readMetadata(Browser.CHROME), {
|
||||
aliases: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('throw an error if .metadata is malformed', async function () {
|
||||
// @ts-expect-error wrong type on purpose;
|
||||
cache.writeMetadata(Browser.CHROME, 'metadata');
|
||||
assert.throws(() => {
|
||||
return cache.readMetadata(Browser.CHROME);
|
||||
}, new Error(`.metadata is not an object`));
|
||||
});
|
||||
|
||||
it('writes and reads .metadata', async function () {
|
||||
cache.writeMetadata(Browser.CHROME, {
|
||||
aliases: {
|
||||
canary: '123.0.0.0',
|
||||
},
|
||||
});
|
||||
assert.deepStrictEqual(cache.readMetadata(Browser.CHROME), {
|
||||
aliases: {
|
||||
canary: '123.0.0.0',
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(
|
||||
cache.resolveAlias(Browser.CHROME, 'canary'),
|
||||
'123.0.0.0'
|
||||
);
|
||||
});
|
||||
|
||||
it('resolves latest', async function () {
|
||||
cache.writeMetadata(Browser.CHROME, {
|
||||
aliases: {
|
||||
canary: '115.0.5789',
|
||||
stable: '114.0.5789',
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(
|
||||
cache.resolveAlias(Browser.CHROME, 'latest'),
|
||||
'115.0.5789'
|
||||
);
|
||||
});
|
||||
});
|
@ -74,6 +74,8 @@ export async function downloadBrowser(): Promise<void> {
|
||||
buildId,
|
||||
downloadProgressCallback: makeProgressCallback(browser, buildId),
|
||||
baseUrl: downloadBaseUrl,
|
||||
buildIdAlias:
|
||||
buildId !== unresolvedBuildId ? unresolvedBuildId : undefined,
|
||||
})
|
||||
.then(result => {
|
||||
logPolitely(
|
||||
@ -112,6 +114,10 @@ export async function downloadBrowser(): Promise<void> {
|
||||
shellBuildId
|
||||
),
|
||||
baseUrl: downloadBaseUrl,
|
||||
buildIdAlias:
|
||||
shellBuildId !== unresolvedShellBuildId
|
||||
? unresolvedShellBuildId
|
||||
: undefined,
|
||||
})
|
||||
.then(result => {
|
||||
logPolitely(
|
||||
|
Loading…
Reference in New Issue
Block a user