feat: detect docker version and Playwright version mismatch (#12806)

This patch prints a friendly instructions in case Docker image version
mismatches Playwright version and there are missing browser
dependencies.

With this patch, Playwright will yield the following error:

```
root@f0774d2b2097:~# node a.mjs
node:internal/process/promises:279
            triggerUncaughtException(err, true /* fromPromise */);
            ^

browserType.launch:
╔════════════════════════════════════════════════════════════════════════════════════════════╗
║ Host system is missing dependencies to run browsers.                                       ║
║ This is most likely due to docker image version not matching Playwright version:           ║
║ - Playwright: 1.22.0                                                                       ║
║ -     Docker: 1.21.0                                                                       ║
║                                                                                            ║
║ Either:                                                                                    ║
║ - (recommended) use docker image "mcr.microsoft.com/playwright:v1.22.0-focal"              ║
║ - (alternative 1) run the following command inside docker to install missing dependencies: ║
║                                                                                            ║
║     npx playwright install-deps                                                            ║
║                                                                                            ║
║ - (alternative 2) use Aptitude inside docker:                                              ║
║                                                                                            ║
║     apt-get install libgbm1                                                                ║
║                                                                                            ║
║ <3 Playwright Team                                                                         ║
╚════════════════════════════════════════════════════════════════════════════════════════════╝
    at file:///root/a.mjs:3:10 {
  name: 'Error'
}```

Fixes #12796

Co-authored-by: Dmitry Gozman <dgozman@gmail.com>
This commit is contained in:
Andrey Lushnikov 2022-03-25 15:45:53 -06:00 committed by GitHub
parent 6b81e76c2b
commit 4ab4c0bda1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 3 deletions

View File

@ -33,6 +33,7 @@ import { BrowserContextOptions, LaunchOptions } from '../client/types';
import { spawn } from 'child_process';
import { registry, Executable } from '../utils/registry';
import { spawnAsync, getPlaywrightVersion } from '../utils/utils';
import { writeDockerVersion } from '../utils/dependencies';
import { launchGridAgent } from '../grid/gridAgent';
import { GridServer, GridFactory } from '../grid/gridServer';
@ -42,6 +43,14 @@ program
.version('Version ' + (process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version))
.name(buildBasePlaywrightCLICommand(process.env.PW_LANG_NAME));
program
.command('mark-docker-image > [args...]', { hidden: true })
.description('mark docker image')
.allowUnknownOption(true)
.action(function(dockerImageNameTemplate) {
writeDockerVersion(dockerImageNameTemplate);
});
commandWithOpenOptions('open [url]', 'open page in browser specified via -b, --browser', [])
.action(function(url, options) {
open(options, url, language()).catch(logErrorAndExit);

View File

@ -24,6 +24,24 @@ import { deps } from './nativeDeps';
import { getUbuntuVersion } from './ubuntuVersion';
const BIN_DIRECTORY = path.join(__dirname, '..', '..', 'bin');
const packageJSON = require('../../package.json');
const dockerVersionFilePath = '/ms-playwright/.docker-info';
export async function writeDockerVersion(dockerImageNameTemplate: string) {
await fs.promises.mkdir(path.dirname(dockerVersionFilePath), { recursive: true });
await fs.promises.writeFile(dockerVersionFilePath, JSON.stringify({
driverVersion: packageJSON.version,
dockerImageName: dockerImageNameTemplate.replace('%version%', packageJSON.version),
}, null, 2), 'utf8');
// Make sure version file is globally accessible.
await fs.promises.chmod(dockerVersionFilePath, 0o777);
}
async function readDockerVersion(): Promise<null | { driverVersion: string, dockerImageName: string }> {
return await fs.promises.readFile(dockerVersionFilePath, 'utf8')
.then(text => JSON.parse(text))
.catch(e => null);
}
const checkExecutable = (filePath: string) => fs.promises.access(filePath, fs.constants.X_OK).then(() => true).catch(e => false);
@ -183,11 +201,36 @@ export async function validateDependenciesLinux(sdkLanguage: string, linuxLddDir
}
const maybeSudo = (process.getuid() !== 0) && os.platform() !== 'win32' ? 'sudo ' : '';
const dockerInfo = await readDockerVersion();
const errorLines = [
`Host system is missing dependencies to run browsers.`,
];
if (missingPackages.size && !missingDeps.size) {
// Happy path: known dependencies are missing for browsers.
// Ignore patch versions when comparing docker container version and Playwright version:
// we **NEVER** roll browsers in patch releases, so native dependencies do not change.
if (dockerInfo && !dockerInfo.driverVersion.startsWith(utils.getPlaywrightVersion(true /* majorMinorOnly */) + '.')) {
// We are running in a docker container with unmatching version.
// In this case, we know how to install dependencies in it.
const pwVersion = utils.getPlaywrightVersion();
const requiredDockerImage = dockerInfo.dockerImageName.replace(dockerInfo.driverVersion, pwVersion);
errorLines.push(...[
`This is most likely due to docker image version not matching Playwright version:`,
`- Playwright: ${pwVersion}`,
`- Docker: ${dockerInfo.driverVersion}`,
``,
`Either:`,
`- (recommended) use docker image "${requiredDockerImage}"`,
`- (alternative 1) run the following command inside docker to install missing dependencies:`,
``,
` ${maybeSudo}${buildPlaywrightCLICommand(sdkLanguage, 'install-deps')}`,
``,
`- (alternative 2) use Aptitude inside docker:`,
``,
` ${maybeSudo}apt-get install ${[...missingPackages].join('\\\n ')}`,
``,
`<3 Playwright Team`,
]);
} else if (missingPackages.size && !missingDeps.size) {
// Only known dependencies are missing for browsers.
// Suggest installation with a Playwright CLI.
errorLines.push(...[
`Please install them with the following command:`,

View File

@ -2,6 +2,7 @@ FROM ubuntu:bionic
ARG DEBIAN_FRONTEND=noninteractive
ARG TZ=America/Los_Angeles
ARG DOCKER_IMAGE_NAME_TEMPLATE="mcr.microsoft.com/playwright:v%version%-bionic"
# === INSTALL Node.js ===
@ -34,7 +35,9 @@ RUN mkdir /ms-playwright && \
mkdir /ms-playwright-agent && \
cd /ms-playwright-agent && npm init -y && \
npm i /tmp/playwright-core.tar.gz && \
npx playwright mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
npx playwright install --with-deps && rm -rf /var/lib/apt/lists/* && \
rm /tmp/playwright-core.tar.gz && \
rm -rf /ms-playwright-agent && \
chmod -R 777 /ms-playwright

View File

@ -2,6 +2,7 @@ FROM ubuntu:focal
ARG DEBIAN_FRONTEND=noninteractive
ARG TZ=America/Los_Angeles
ARG DOCKER_IMAGE_NAME_TEMPLATE="mcr.microsoft.com/playwright:v%version%-focal"
# === INSTALL Node.js ===
@ -34,6 +35,7 @@ RUN mkdir /ms-playwright && \
mkdir /ms-playwright-agent && \
cd /ms-playwright-agent && npm init -y && \
npm i /tmp/playwright-core.tar.gz && \
npx playwright mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
npx playwright install --with-deps && rm -rf /var/lib/apt/lists/* && \
rm /tmp/playwright-core.tar.gz && \
rm -rf /ms-playwright-agent && \

View File

@ -7,7 +7,7 @@ trap "cd $(pwd -P)" EXIT
cd "$(dirname "$0")"
MCR_IMAGE_NAME="playwright"
PW_VERSION=$(node -e 'console.log(require("../../package.json").version)')
PW_VERSION=$(node ../../utils/workspace.js --get-version)
RELEASE_CHANNEL="$1"
if [[ "${RELEASE_CHANNEL}" == "stable" ]]; then

View File

@ -56,6 +56,11 @@ class Workspace {
return this._packages;
}
async version() {
const workspacePackageJSON = await readJSON(path.join(this._rootDir, 'package.json'));
return workspacePackageJSON.version;
}
/**
* @param {string} version
*/
@ -210,6 +215,9 @@ async function parseCLI() {
console.log(pkg.path);
}
},
'--get-version': async (version) => {
console.log(await workspace.version());
},
'--set-version': async (version) => {
if (!version)
die('ERROR: Please specify version! e.g. --set-version 1.99.2');