Skip to content

Commit

Permalink
fix: always use ENV executable path when present (#7985)
Browse files Browse the repository at this point in the history
Some recent changes to allow arm64 environments (including M1 macs) to
launch a chromium installation successfully before arm-compatible builds
were downloadable prevented the usage of PUPPETEER_EXECUTABLE_PATH in
some environments. Currently, when the platform is not darwin and the
arch is arm64, an executable cannot be specified using the environment
variable.

Generally speaking, environment variables have highest precedence for
options such as this since they depend on system configuration.

These change:

1. allow the ENV variable to always be used when defined and not
   specified in LaunchOptions (and when not puppeteer-core)
2. Retain the existing behavior of assuming /usr/bin/chromium-browser on
   platforms like Ubuntu (exact if-conditions preserved to avoid any
   breaking changes)
3. Add some tests for this particular portion of the code.
  • Loading branch information
jbielick committed Feb 11, 2022
1 parent 7e2b47c commit 6d6ea9b
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 17 deletions.
39 changes: 22 additions & 17 deletions src/node/Launcher.ts
Expand Up @@ -146,15 +146,9 @@ 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.
if (os.platform() !== 'darwin' && os.arch() === 'arm64') {
chromeExecutable = '/usr/bin/chromium-browser';
} else {
const { missingText, executablePath } = resolveExecutablePath(this);
if (missingText) throw new Error(missingText);
chromeExecutable = executablePath;
}
const { missingText, executablePath } = resolveExecutablePath(this);
if (missingText) throw new Error(missingText);
chromeExecutable = executablePath;
}

if (!chromeExecutable) {
Expand Down Expand Up @@ -799,9 +793,11 @@ function resolveExecutablePath(launcher: ChromeLauncher | FirefoxLauncher): {
executablePath: string;
missingText?: string;
} {
const { product, _isPuppeteerCore, _projectRoot, _preferredRevision } =
launcher;
let downloadPath: string | undefined;
// puppeteer-core doesn't take into account PUPPETEER_* env variables.
if (!launcher._isPuppeteerCore) {
if (!_isPuppeteerCore) {
const executablePath =
process.env.PUPPETEER_EXECUTABLE_PATH ||
process.env.npm_config_puppeteer_executable_path ||
Expand All @@ -813,22 +809,31 @@ function resolveExecutablePath(launcher: ChromeLauncher | FirefoxLauncher): {
: undefined;
return { executablePath, missingText };
}
const ubuntuChromiumPath = '/usr/bin/chromium-browser';
if (
product === 'chrome' &&
os.platform() !== 'darwin' &&
os.arch() === 'arm64' &&
fs.existsSync(ubuntuChromiumPath)
) {
return { executablePath: ubuntuChromiumPath, missingText: undefined };
}
downloadPath =
process.env.PUPPETEER_DOWNLOAD_PATH ||
process.env.npm_config_puppeteer_download_path ||
process.env.npm_package_config_puppeteer_download_path;
}
if (!launcher._projectRoot) {
if (!_projectRoot) {
throw new Error(
'_projectRoot is undefined. Unable to create a BrowserFetcher.'
);
}
const browserFetcher = new BrowserFetcher(launcher._projectRoot, {
product: launcher.product,
const browserFetcher = new BrowserFetcher(_projectRoot, {
product: product,
path: downloadPath,
});

if (!launcher._isPuppeteerCore && launcher.product === 'chrome') {
if (!_isPuppeteerCore && product === 'chrome') {
const revision = process.env['PUPPETEER_CHROMIUM_REVISION'];
if (revision) {
const revisionInfo = browserFetcher.revisionInfo(revision);
Expand All @@ -839,13 +844,13 @@ function resolveExecutablePath(launcher: ChromeLauncher | FirefoxLauncher): {
return { executablePath: revisionInfo.executablePath, missingText };
}
}
const revisionInfo = browserFetcher.revisionInfo(launcher._preferredRevision);
const revisionInfo = browserFetcher.revisionInfo(_preferredRevision);

const firefoxHelp = `Run \`PUPPETEER_PRODUCT=firefox npm install\` to download a supported Firefox browser binary.`;
const chromeHelp = `Run \`npm install\` to download the correct Chromium revision (${launcher._preferredRevision}).`;
const missingText = !revisionInfo.local
? `Could not find expected browser (${launcher.product}) locally. ${
launcher.product === 'chrome' ? chromeHelp : firefoxHelp
? `Could not find expected browser (${product}) locally. ${
product === 'chrome' ? chromeHelp : firefoxHelp
}`
: undefined;
return { executablePath: revisionInfo.executablePath, missingText };
Expand Down
82 changes: 82 additions & 0 deletions test/launcher.spec.ts
Expand Up @@ -733,6 +733,88 @@ describe('Launcher specs', function () {
const executablePath = puppeteer.executablePath('chrome');
expect(executablePath).toBeTruthy();
});
describe('when PUPPETEER_EXECUTABLE_PATH is set', () => {
const sandbox = sinon.createSandbox();

beforeEach(() => {
process.env.PUPPETEER_EXECUTABLE_PATH = '';
sandbox
.stub(process.env, 'PUPPETEER_EXECUTABLE_PATH')
.value('SOME_CUSTOM_EXECUTABLE');
});

afterEach(() => sandbox.restore());

it('its value is returned', async () => {
const { puppeteer } = getTestState();

const executablePath = puppeteer.executablePath();

expect(executablePath).toEqual('SOME_CUSTOM_EXECUTABLE');
});
});

describe('when the product is chrome, platform is not darwin, and arch is arm64', () => {
describe('and the executable exists', () => {
itChromeOnly('returns /usr/bin/chromium-browser', async () => {
const { puppeteer } = getTestState();
const osPlatformStub = sinon.stub(os, 'platform').returns('linux');
const osArchStub = sinon.stub(os, 'arch').returns('arm64');
const fsExistsStub = sinon.stub(fs, 'existsSync');
fsExistsStub.withArgs('/usr/bin/chromium-browser').returns(true);

const executablePath = puppeteer.executablePath();

expect(executablePath).toEqual('/usr/bin/chromium-browser');

osPlatformStub.restore();
osArchStub.restore();
fsExistsStub.restore();
});
describe('and PUPPETEER_EXECUTABLE_PATH is set', () => {
const sandbox = sinon.createSandbox();

beforeEach(() => {
process.env.PUPPETEER_EXECUTABLE_PATH = '';
sandbox
.stub(process.env, 'PUPPETEER_EXECUTABLE_PATH')
.value('SOME_CUSTOM_EXECUTABLE');
});

afterEach(() => sandbox.restore());

it('its value is returned', async () => {
const { puppeteer } = getTestState();

const executablePath = puppeteer.executablePath();

expect(executablePath).toEqual('SOME_CUSTOM_EXECUTABLE');
});
});
});
describe('and the executable does not exist', () => {
itChromeOnly(
'does not return /usr/bin/chromium-browser',
async () => {
const { puppeteer } = getTestState();
const osPlatformStub = sinon
.stub(os, 'platform')
.returns('linux');
const osArchStub = sinon.stub(os, 'arch').returns('arm64');
const fsExistsStub = sinon.stub(fs, 'existsSync');
fsExistsStub.withArgs('/usr/bin/chromium-browser').returns(false);

const executablePath = puppeteer.executablePath();

expect(executablePath).not.toEqual('/usr/bin/chromium-browser');

osPlatformStub.restore();
osArchStub.restore();
fsExistsStub.restore();
}
);
});
});
});
});

Expand Down

0 comments on commit 6d6ea9b

Please sign in to comment.