diff --git a/packages/playwright-core/src/containers/build_docker_image.sh b/packages/playwright-core/bin/container_install_deps.sh old mode 100644 new mode 100755 similarity index 76% rename from packages/playwright-core/src/containers/build_docker_image.sh rename to packages/playwright-core/bin/container_install_deps.sh index 89eef09023..35a37d0562 --- a/packages/playwright-core/src/containers/build_docker_image.sh +++ b/packages/playwright-core/bin/container_install_deps.sh @@ -88,38 +88,3 @@ $center $full|/ms-playwright-agent/node_modules/playwright-core/lib/server/chrom $center $full|/ms-playwright-agent/node_modules/playwright-core/lib/server/chromium/appIcon.png||:99.0 EOF -# Create entrypoint.sh -cat <<'EOF' > /entrypoint.sh -#!/bin/bash -set -e -SCREEN_WIDTH=1360 -SCREEN_HEIGHT=1020 -SCREEN_DEPTH=24 -SCREEN_DPI=96 -GEOMETRY="$SCREEN_WIDTH""x""$SCREEN_HEIGHT""x""$SCREEN_DEPTH" - -nohup /usr/bin/xvfb-run --server-num=$DISPLAY_NUM \ - --listen-tcp \ - --server-args="-screen 0 "$GEOMETRY" -fbdir /var/tmp -dpi "$SCREEN_DPI" -listen tcp -noreset -ac +extension RANDR" \ - /usr/bin/fluxbox -display "$DISPLAY" >/dev/null 2>&1 & - -for i in $(seq 1 500); do - if xdpyinfo -display $DISPLAY >/dev/null 2>&1; then - break - fi - echo "Waiting for Xvfb..." - sleep 0.2 -done - -nohup x11vnc -noprimary -nosetprimary -forever -shared -rfbport 5900 -rfbportv6 5900 -display "$DISPLAY" >/dev/null 2>&1 & -nohup /opt/bin/noVNC/utils/novnc_proxy --listen 7900 --vnc localhost:5900 >/dev/null 2>&1 & - -cd /ms-playwright-agent - -NOVNC_UUID=$(cat /proc/sys/kernel/random/uuid) -echo "novnc is listening on http://127.0.0.1:7900?path=$NOVNC_UUID&resize=scale&autoconnect=1" - -PW_UUID=$(cat /proc/sys/kernel/random/uuid) -npx playwright run-server --port=5400 --path=/$PW_UUID -EOF -chmod 755 /entrypoint.sh diff --git a/packages/playwright-core/bin/container_run_server.sh b/packages/playwright-core/bin/container_run_server.sh new file mode 100755 index 0000000000..2bfef162f5 --- /dev/null +++ b/packages/playwright-core/bin/container_run_server.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e +SCREEN_WIDTH=1360 +SCREEN_HEIGHT=1020 +SCREEN_DEPTH=24 +SCREEN_DPI=96 +GEOMETRY="$SCREEN_WIDTH""x""$SCREEN_HEIGHT""x""$SCREEN_DEPTH" + +nohup /usr/bin/xvfb-run --server-num=$DISPLAY_NUM \ + --listen-tcp \ + --server-args="-screen 0 "$GEOMETRY" -fbdir /var/tmp -dpi "$SCREEN_DPI" -listen tcp -noreset -ac +extension RANDR" \ + /usr/bin/fluxbox -display "$DISPLAY" >/dev/null 2>&1 & + +for i in $(seq 1 500); do + if xdpyinfo -display $DISPLAY >/dev/null 2>&1; then + break + fi + echo "Waiting for Xvfb..." + sleep 0.2 +done + +nohup x11vnc -noprimary -nosetprimary -forever -shared -rfbport 5900 -rfbportv6 5900 -display "$DISPLAY" >/dev/null 2>&1 & +nohup /opt/bin/noVNC/utils/novnc_proxy --listen 7900 --vnc localhost:5900 >/dev/null 2>&1 & + +cd /ms-playwright-agent + +NOVNC_UUID=$(cat /proc/sys/kernel/random/uuid) +echo "novnc is listening on http://127.0.0.1:7900?path=$NOVNC_UUID&resize=scale&autoconnect=1" + +PW_UUID=$(cat /proc/sys/kernel/random/uuid) +npx playwright run-server --port=5400 --path=/$PW_UUID diff --git a/packages/playwright-core/src/containers/docker.ts b/packages/playwright-core/src/containers/docker.ts index 580fdac0fd..17e393040e 100644 --- a/packages/playwright-core/src/containers/docker.ts +++ b/packages/playwright-core/src/containers/docker.ts @@ -16,7 +16,6 @@ /* eslint-disable no-console */ import path from 'path'; -import fs from 'fs'; import { spawnAsync } from '../utils/spawnAsync'; import * as utils from '../utils'; import { getPlaywrightVersion } from '../common/userAgent'; @@ -102,13 +101,15 @@ async function buildPlaywrightImage() { const dockerImage = await findDockerImage(baseImageName); if (!dockerImage) throw new Error(`Failed to pull ${baseImageName}`); - // 3. Launch container and install VNC in it + // 3. Delete previous build of the playwright image to avoid untagged images. + await deletePlaywrightImage(); + // 4. Launch container and install VNC in it console.log(`Building ${VRT_IMAGE_NAME}...`); - const buildScriptText = await fs.promises.readFile(path.join(__dirname, 'build_docker_image.sh'), 'utf8'); const containerId = await dockerApi.launchContainer({ imageId: dockerImage.imageId, autoRemove: false, - command: ['/bin/bash', '-c', buildScriptText], + workingDir: '/ms-playwright-agent', + command: ['npx', 'playwright', 'docker', 'install-server-deps'], waitUntil: 'not-running', }); @@ -118,7 +119,8 @@ async function buildPlaywrightImage() { containerId, repo: vrtRepo, tag: vrtTag, - entrypoint: '/entrypoint.sh', + workingDir: '/ms-playwright-agent', + entrypoint: ['npx', 'playwright', 'docker', 'run-server'], env: { 'DISPLAY_NUM': '99', 'DISPLAY': ':99', @@ -317,6 +319,20 @@ export function addDockerCLI(program: Command) { } }); + dockerCommand.command('install-server-deps', { hidden: true }) + .description('delete docker image, if any') + .action(async function() { + const { code } = await spawnAsync('bash', [path.join(__dirname, '..', '..', 'bin', 'container_install_deps.sh')], { stdio: 'inherit' }); + if (code !== 0) + throw new Error('Failed to install server dependencies!'); + }); + + dockerCommand.command('run-server', { hidden: true }) + .description('delete docker image, if any') + .action(async function() { + await spawnAsync('bash', [path.join(__dirname, '..', '..', 'bin', 'container_run_server.sh')], { stdio: 'inherit' }); + }); + dockerCommand.command('print-status-json', { hidden: true }) .description('print docker status') .action(async function(options) { diff --git a/packages/playwright-core/src/containers/dockerApi.ts b/packages/playwright-core/src/containers/dockerApi.ts index 2bbd59988b..f084bb193b 100644 --- a/packages/playwright-core/src/containers/dockerApi.ts +++ b/packages/playwright-core/src/containers/dockerApi.ts @@ -65,6 +65,7 @@ interface LaunchContainerOptions { labels?: Record; ports?: Number[]; name?: string; + workingDir?: string; waitUntil?: 'not-running' | 'next-exit' | 'removed'; } @@ -77,6 +78,7 @@ export async function launchContainer(options: LaunchContainerOptions): Promise< } const container = await postJSON(`/containers/create` + (options.name ? '?name=' + options.name : ''), { Cmd: options.command, + WorkingDir: options.workingDir, Labels: options.labels ?? {}, AttachStdout: true, AttachStderr: true, @@ -134,7 +136,8 @@ interface CommitContainerOptions { containerId: string, repo: string, tag: string, - entrypoint?: string, + entrypoint?: string[], + workingDir?: string, env?: {[key: string]: string | number | boolean | undefined}, } @@ -143,7 +146,8 @@ export async function commitContainer(options: CommitContainerOptions) { for (const [key, value] of Object.entries(options.env ?? {})) Env.push(`${key}=${value}`); await postJSON(`/commit?container=${options.containerId}&repo=${options.repo}&tag=${options.tag}`, { - Entrypoint: options.entrypoint ?? '', + Entrypoint: options.entrypoint, + WorkingDir: options.workingDir, Env, }); } diff --git a/utils/build/build.js b/utils/build/build.js index 6e4632264e..3f11ceee84 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -301,7 +301,7 @@ copyFiles.push({ // Babel doesn't touch JS files, so copy them manually. // For example: diff_match_patch.js copyFiles.push({ - files: 'packages/playwright-core/src/**/*.(js|sh)', + files: 'packages/playwright-core/src/**/*.js', from: 'packages/playwright-core/src', to: 'packages/playwright-core/lib', ignored: ['**/.eslintrc.js', '**/webpack*.config.js', '**/injected/**/*']