From bce68adfb7c40052c0bcb51c4399c24bd6620e37 Mon Sep 17 00:00:00 2001 From: Valentin Semirulnik Date: Sun, 5 Sep 2021 19:36:28 +0300 Subject: [PATCH 1/9] feat: add support for Apple Silicon chromium builds --- src/node/BrowserFetcher.ts | 23 ++++++++++++++--------- src/node/Launcher.ts | 3 +-- src/node/install.ts | 11 +++-------- src/revisions.ts | 2 +- utils/check_availability.js | 7 +++++-- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/node/BrowserFetcher.ts b/src/node/BrowserFetcher.ts index 20301e2462142..5de9df1f61a86 100644 --- a/src/node/BrowserFetcher.ts +++ b/src/node/BrowserFetcher.ts @@ -41,6 +41,7 @@ const downloadURLs = { chrome: { linux: '%s/chromium-browser-snapshots/Linux_x64/%d/%s.zip', mac: '%s/chromium-browser-snapshots/Mac/%d/%s.zip', + mac_arm: '%s/chromium-browser-snapshots/Mac_Arm/%d/%s.zip', win32: '%s/chromium-browser-snapshots/Win/%d/%s.zip', win64: '%s/chromium-browser-snapshots/Win_x64/%d/%s.zip', }, @@ -67,7 +68,7 @@ const browserConfig = { * Supported platforms. * @public */ -export type Platform = 'linux' | 'mac' | 'win32' | 'win64'; +export type Platform = 'linux' | 'mac' | 'mac_arm' | 'win32' | 'win64'; function archiveName( product: Product, @@ -76,7 +77,7 @@ function archiveName( ): string { if (product === 'chrome') { if (platform === 'linux') return 'chrome-linux'; - if (platform === 'mac') return 'chrome-mac'; + if (platform === 'mac' || platform === 'mac_arm') return 'chrome-mac'; if (platform === 'win32' || platform === 'win64') { // Windows archive name changed at r591479. return parseInt(revision, 10) > 591479 ? 'chrome-win' : 'chrome-win32'; @@ -198,21 +199,26 @@ export class BrowserFetcher { options.path || path.join(projectRoot, browserConfig[this._product].destination); this._downloadHost = options.host || browserConfig[this._product].host; - this.setPlatform(options.platform); + this.setPlatform(options.platform, this._product); assert( downloadURLs[this._product][this._platform], 'Unsupported platform: ' + this._platform ); } - private setPlatform(platformFromOptions?: Platform): void { + private setPlatform( + platformFromOptions?: Platform, + productFromOptions?: Product + ): void { if (platformFromOptions) { this._platform = platformFromOptions; return; } const platform = os.platform(); - if (platform === 'darwin') this._platform = 'mac'; + if (platform === 'darwin' && productFromOptions === 'chrome') + this._platform = os.arch() === 'arm64' ? 'mac_arm' : 'mac'; + else if (productFromOptions === 'firefox') this._platform = 'mac'; else if (platform === 'linux') this._platform = 'linux'; else if (platform === 'win32') this._platform = os.arch() === 'x64' ? 'win64' : 'win32'; @@ -295,8 +301,7 @@ export class BrowserFetcher { if (!(await existsAsync(this._downloadsFolder))) await mkdirAsync(this._downloadsFolder); - // Use Intel x86 builds on Apple M1 until native macOS arm64 - // Chromium builds are available. + // Use system Chromium builds on Linux ARM devices if (os.platform() !== 'darwin' && os.arch() === 'arm64') { handleArm64(); return; @@ -351,7 +356,7 @@ export class BrowserFetcher { const folderPath = this._getFolderPath(revision); let executablePath = ''; if (this._product === 'chrome') { - if (this._platform === 'mac') + if (this._platform === 'mac' || this._platform === 'mac_arm') executablePath = path.join( folderPath, archiveName(this._product, this._platform, revision), @@ -374,7 +379,7 @@ export class BrowserFetcher { ); else throw new Error('Unsupported platform: ' + this._platform); } else if (this._product === 'firefox') { - if (this._platform === 'mac') + if (this._platform === 'mac' || this._platform === 'mac_arm') executablePath = path.join( folderPath, 'Firefox Nightly.app', diff --git a/src/node/Launcher.ts b/src/node/Launcher.ts index 402ae0ad0d7f1..cf1cb72212826 100644 --- a/src/node/Launcher.ts +++ b/src/node/Launcher.ts @@ -118,8 +118,7 @@ class ChromeLauncher implements ProductLauncher { chromeExecutable = executablePathForChannel(channel); } else if (!executablePath) { - // Use Intel x86 builds on Apple M1 until native macOS arm64 - // Chromium builds are available. + // Use system Chromium builds on Linux ARM devices if (os.platform() !== 'darwin' && os.arch() === 'arm64') { chromeExecutable = '/usr/bin/chromium-browser'; } else { diff --git a/src/node/install.ts b/src/node/install.ts index d7e751f44ef87..6789c6bbd01b5 100644 --- a/src/node/install.ts +++ b/src/node/install.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import os from 'os'; import https from 'https'; import ProgressBar from 'progress'; import puppeteer from '../node.js'; @@ -90,13 +89,9 @@ export async function downloadBrowser(): Promise { if (NPM_NO_PROXY) process.env.NO_PROXY = NPM_NO_PROXY; function onSuccess(localRevisions: string[]): void { - // Use Intel x86 builds on Apple M1 until native macOS arm64 - // Chromium builds are available. - if (os.platform() !== 'darwin' && os.arch() !== 'arm64') { - logPolitely( - `${supportedProducts[product]} (${revisionInfo.revision}) downloaded to ${revisionInfo.folderPath}` - ); - } + logPolitely( + `${supportedProducts[product]} (${revisionInfo.revision}) downloaded to ${revisionInfo.folderPath}` + ); localRevisions = localRevisions.filter( (revision) => revision !== revisionInfo.revision ); diff --git a/src/revisions.ts b/src/revisions.ts index 5c4694ea39032..288ffe4bb6b39 100644 --- a/src/revisions.ts +++ b/src/revisions.ts @@ -20,6 +20,6 @@ type Revisions = Readonly<{ }>; export const PUPPETEER_REVISIONS: Revisions = { - chromium: '901912', + chromium: '916291', firefox: 'latest', }; diff --git a/utils/check_availability.js b/utils/check_availability.js index fbc115c1a53f0..faedbb6868a4f 100755 --- a/utils/check_availability.js +++ b/utils/check_availability.js @@ -21,7 +21,7 @@ const https = require('https'); const BrowserFetcher = require('../lib/cjs/puppeteer/node/BrowserFetcher.js').BrowserFetcher; -const SUPPORTER_PLATFORMS = ['linux', 'mac', 'win32', 'win64']; +const SUPPORTER_PLATFORMS = ['linux', 'mac', 'mac_arm', 'win32', 'win64']; const fetchers = SUPPORTER_PLATFORMS.map( (platform) => new BrowserFetcher('', { platform }) ); @@ -139,6 +139,9 @@ async function checkOmahaProxyAvailability() { fetch( 'https://storage.googleapis.com/chromium-browser-snapshots/Mac/LAST_CHANGE' ), + fetch( + 'https://storage.googleapis.com/chromium-browser-snapshots/Mac_Arm/LAST_CHANGE' + ), fetch( 'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/LAST_CHANGE' ), @@ -191,7 +194,7 @@ async function checkRangeAvailability({ toRevision, stopWhenAllAvailable, }) { - const table = new Table([10, 7, 7, 7, 7]); + const table = new Table([10, 7, 7, 7, 7, 7]); table.drawRow([''].concat(SUPPORTER_PLATFORMS)); const inc = fromRevision < toRevision ? 1 : -1; From e8bea459350446d1e9f39c6c08287dd2d07dd0d1 Mon Sep 17 00:00:00 2001 From: Valentin Semirulnik Date: Wed, 8 Dec 2021 14:57:51 +0300 Subject: [PATCH 2/9] Remove unnecessary change --- src/node/install.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/install.ts b/src/node/install.ts index 409c6311c9d06..3f0c4854ab389 100644 --- a/src/node/install.ts +++ b/src/node/install.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import https, { RequestOptions } from 'https'; import os from 'os'; +import https, { RequestOptions } from 'https'; import ProgressBar from 'progress'; import URL from 'url'; import puppeteer from '../node.js'; From f7f22740f380a2ba4bf419a9bb919b3227491046 Mon Sep 17 00:00:00 2001 From: Valentin Semirulnik Date: Thu, 24 Feb 2022 13:56:17 +0300 Subject: [PATCH 3/9] use arm version only when PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM is set --- src/node/BrowserFetcher.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/node/BrowserFetcher.ts b/src/node/BrowserFetcher.ts index bfeae09328af0..fec616c8b2ecb 100644 --- a/src/node/BrowserFetcher.ts +++ b/src/node/BrowserFetcher.ts @@ -35,6 +35,8 @@ import createHttpsProxyAgent, { import { getProxyForUrl } from 'proxy-from-env'; import { assert } from '../common/assert.js'; +const { PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM } = process.env + const debugFetcher = debug('puppeteer:fetcher'); const downloadURLs = { @@ -219,7 +221,7 @@ export class BrowserFetcher { const platform = os.platform(); if (platform === 'darwin' && productFromOptions === 'chrome') - this._platform = os.arch() === 'arm64' ? 'mac_arm' : 'mac'; + this._platform = (os.arch() === 'arm64' && PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM) ? 'mac_arm' : 'mac'; else if (productFromOptions === 'firefox') this._platform = 'mac'; else if (platform === 'linux') this._platform = 'linux'; else if (platform === 'win32') From ffdf1a3810649823359e71c8dd1861259bd29a97 Mon Sep 17 00:00:00 2001 From: Valentin Semirulnik Date: Thu, 24 Feb 2022 14:57:57 +0300 Subject: [PATCH 4/9] fix prettier --- src/node/BrowserFetcher.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/node/BrowserFetcher.ts b/src/node/BrowserFetcher.ts index fec616c8b2ecb..6c0095211b4e7 100644 --- a/src/node/BrowserFetcher.ts +++ b/src/node/BrowserFetcher.ts @@ -221,7 +221,10 @@ export class BrowserFetcher { const platform = os.platform(); if (platform === 'darwin' && productFromOptions === 'chrome') - this._platform = (os.arch() === 'arm64' && PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM) ? 'mac_arm' : 'mac'; + this._platform = + os.arch() === 'arm64' && PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM + ? 'mac_arm' + : 'mac'; else if (productFromOptions === 'firefox') this._platform = 'mac'; else if (platform === 'linux') this._platform = 'linux'; else if (platform === 'win32') From 016b82433100e65089b1344371ab90ed5e33dfef Mon Sep 17 00:00:00 2001 From: Valentin Semirulnik Date: Sun, 1 May 2022 16:59:14 +0400 Subject: [PATCH 5/9] fix prettier --- src/node/BrowserFetcher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/BrowserFetcher.ts b/src/node/BrowserFetcher.ts index 68d692f1e32f6..d7ea74d28c2a1 100644 --- a/src/node/BrowserFetcher.ts +++ b/src/node/BrowserFetcher.ts @@ -35,7 +35,7 @@ import createHttpsProxyAgent, { import { getProxyForUrl } from 'proxy-from-env'; import { assert } from '../common/assert.js'; -const { PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM } = process.env +const { PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM } = process.env; const debugFetcher = debug('puppeteer:fetcher'); From 5d97ab9f863785607d5f659ccc070576a318d3b3 Mon Sep 17 00:00:00 2001 From: Valentin Semirulnik Date: Sun, 1 May 2022 17:01:05 +0400 Subject: [PATCH 6/9] fix platform detection for firefox --- src/node/BrowserFetcher.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/node/BrowserFetcher.ts b/src/node/BrowserFetcher.ts index d7ea74d28c2a1..2323860ecaea7 100644 --- a/src/node/BrowserFetcher.ts +++ b/src/node/BrowserFetcher.ts @@ -220,13 +220,16 @@ export class BrowserFetcher { } const platform = os.platform(); - if (platform === 'darwin' && productFromOptions === 'chrome') - this._platform = - os.arch() === 'arm64' && PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM - ? 'mac_arm' - : 'mac'; - else if (productFromOptions === 'firefox') this._platform = 'mac'; - else if (platform === 'linux') this._platform = 'linux'; + if (platform === 'darwin') { + if (productFromOptions === 'chrome') { + this._platform = + os.arch() === 'arm64' && PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM + ? 'mac_arm' + : 'mac'; + } else if (productFromOptions === 'firefox') { + this._platform = 'mac'; + } + } else if (platform === 'linux') this._platform = 'linux'; else if (platform === 'win32') this._platform = os.arch() === 'x64' ? 'win64' : 'win32'; else assert(this._platform, 'Unsupported platform: ' + platform); From 0004f4d4c41ec2f07ad3b18ec55887e6e3bd486e Mon Sep 17 00:00:00 2001 From: Valentin Semirulnik Date: Mon, 2 May 2022 14:12:51 +0400 Subject: [PATCH 7/9] remove useless os import --- src/node/install.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node/install.ts b/src/node/install.ts index 144487ceee31a..913001bb97a40 100644 --- a/src/node/install.ts +++ b/src/node/install.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import os from 'os'; import https, { RequestOptions } from 'https'; import ProgressBar from 'progress'; import URL from 'url'; From faef7e5bb4bd88237be5b88a6d9a0e256eca58a9 Mon Sep 17 00:00:00 2001 From: Valentin Semirulnik Date: Wed, 4 May 2022 21:14:11 +0300 Subject: [PATCH 8/9] Add info about PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM to api doc --- docs/api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api.md b/docs/api.md index 513f05d6244b8..b1abe4f90fcd3 100644 --- a/docs/api.md +++ b/docs/api.md @@ -487,6 +487,7 @@ If Puppeteer doesn't find them in the environment during the installation step, - `PUPPETEER_CHROMIUM_REVISION` - specify a certain version of Chromium you'd like Puppeteer to use. See [puppeteer.launch([options])](#puppeteerlaunchoptions) on how executable path is inferred. **BEWARE**: Puppeteer is only [guaranteed to work](https://github.com/puppeteer/puppeteer/#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk. - `PUPPETEER_EXECUTABLE_PATH` - specify an executable path to be used in `puppeteer.launch`. See [puppeteer.launch([options])](#puppeteerlaunchoptions) on how the executable path is inferred. **BEWARE**: Puppeteer is only [guaranteed to work](https://github.com/puppeteer/puppeteer/#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk. - `PUPPETEER_PRODUCT` - specify which browser you'd like Puppeteer to use. Must be one of `chrome` or `firefox`. This can also be used during installation to fetch the recommended browser binary. Setting `product` programmatically in [puppeteer.launch([options])](#puppeteerlaunchoptions) supersedes this environment variable. The product is exposed in [`puppeteer.product`](#puppeteerproduct) +- `PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM` — specify Puppeteer download Chromium for Apple M1. On Apple M1 devices Puppeteer by default downloads the version for Intel's processor which runs via Rosetta. It works without any problems, however, with this option, you should get more efficient resource usage (CPU and RAM) that could lead to a faster execution time. **BEWARE**: it's an experimental option that makes sense only if you have an Apple M1 device, use at your own risk. > **NOTE** `PUPPETEER_*` env variables are not accounted for in the [`puppeteer-core`](https://www.npmjs.com/package/puppeteer-core) package. From 8e6fa8c344af0ade04ea753a01d9f31a01c9b886 Mon Sep 17 00:00:00 2001 From: Valentin Semirulnik Date: Wed, 4 May 2022 21:19:14 +0300 Subject: [PATCH 9/9] Add info about PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM to contributing guide --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eda18d655e5f4..f4f18061394b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -253,6 +253,13 @@ PUPPETEER_PRODUCT=firefox node install.js PUPPETEER_PRODUCT=firefox npm run unit ``` +- To run experimental Chromium MacOS ARM tests, firstly ensure you have correct Chromium version installed locally (you only need to do this once, not on every test run) and then you can run the tests: + +```bash +PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM=1 node install.js +PUPPETEER_EXPERIMENTAL_CHROMIUM_MAC_ARM=1 npm run unit +``` + - To run tests with custom browser executable: ```bash