diff --git a/packages/playwright-core/src/cli/cli.ts b/packages/playwright-core/src/cli/cli.ts index 8d4833b960..7287ee8fcd 100755 --- a/packages/playwright-core/src/cli/cli.ts +++ b/packages/playwright-core/src/cli/cli.ts @@ -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); diff --git a/packages/playwright-core/src/utils/dependencies.ts b/packages/playwright-core/src/utils/dependencies.ts index 9740c533bb..b05d0ee738 100644 --- a/packages/playwright-core/src/utils/dependencies.ts +++ b/packages/playwright-core/src/utils/dependencies.ts @@ -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 { + 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:`, diff --git a/utils/docker/Dockerfile.bionic b/utils/docker/Dockerfile.bionic index bbb8f9bce5..98aa6cb411 100644 --- a/utils/docker/Dockerfile.bionic +++ b/utils/docker/Dockerfile.bionic @@ -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 + diff --git a/utils/docker/Dockerfile.focal b/utils/docker/Dockerfile.focal index 0e4b7af89b..2fd268d132 100644 --- a/utils/docker/Dockerfile.focal +++ b/utils/docker/Dockerfile.focal @@ -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 && \ diff --git a/utils/docker/publish_docker.sh b/utils/docker/publish_docker.sh index 7dba94bd8b..336b02226f 100755 --- a/utils/docker/publish_docker.sh +++ b/utils/docker/publish_docker.sh @@ -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 diff --git a/utils/workspace.js b/utils/workspace.js index 7322a5304f..4deea974bb 100755 --- a/utils/workspace.js +++ b/utils/workspace.js @@ -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');