├── .gitattributes ├── .github └── workflows │ ├── build.yml │ └── test.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── scripts ├── build-light.sh ├── build-server.sh ├── build.sh ├── local.sh ├── tag.sh └── update.js ├── server ├── index.mjs └── package.json └── test ├── .gitignore ├── light.js ├── normal.js ├── package.json ├── test.js └── test.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | build-normal-base: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Repository 13 | uses: actions/checkout@v4 14 | 15 | - name: Set up QEMU 16 | uses: docker/setup-qemu-action@v3 17 | 18 | - name: Set up Docker Buildx 19 | uses: docker/setup-buildx-action@v3 20 | 21 | - name: Login to Docker Hub 22 | uses: docker/login-action@v3 23 | with: 24 | username: ${{ secrets.DOCKERHUB_USERNAME }} 25 | password: ${{ secrets.DOCKERHUB_TOKEN }} 26 | 27 | - name: Build and push 28 | uses: docker/build-push-action@v5 29 | with: 30 | context: . 31 | push: true 32 | tags: jacoblincool/playwright:base 33 | target: base 34 | platforms: linux/amd64,linux/arm64/v8 35 | cache-from: type=gha 36 | cache-to: type=gha,mode=max 37 | 38 | build-normal: 39 | needs: 40 | - build-normal-base 41 | strategy: 42 | matrix: 43 | target: 44 | - chromium 45 | - firefox 46 | - webkit 47 | - chrome 48 | - msedge 49 | runs-on: ubuntu-latest 50 | steps: 51 | - name: Checkout Repository 52 | uses: actions/checkout@v4 53 | 54 | - name: Set up QEMU 55 | uses: docker/setup-qemu-action@v3 56 | 57 | - name: Set up Docker Buildx 58 | uses: docker/setup-buildx-action@v3 59 | 60 | - name: Login to Docker Hub 61 | uses: docker/login-action@v3 62 | with: 63 | username: ${{ secrets.DOCKERHUB_USERNAME }} 64 | password: ${{ secrets.DOCKERHUB_TOKEN }} 65 | 66 | - name: Build and push 67 | uses: docker/build-push-action@v5 68 | with: 69 | context: . 70 | push: true 71 | tags: jacoblincool/playwright:${{ matrix.target }} 72 | target: ${{ matrix.target }} 73 | platforms: linux/amd64,linux/arm64/v8 74 | cache-from: type=gha 75 | cache-to: type=gha,mode=max 76 | 77 | 78 | build-normal-all: 79 | needs: 80 | - build-normal 81 | runs-on: ubuntu-latest 82 | steps: 83 | - name: Checkout Repository 84 | uses: actions/checkout@v4 85 | 86 | - name: Set up QEMU 87 | uses: docker/setup-qemu-action@v3 88 | 89 | - name: Set up Docker Buildx 90 | uses: docker/setup-buildx-action@v3 91 | 92 | - name: Login to Docker Hub 93 | uses: docker/login-action@v3 94 | with: 95 | username: ${{ secrets.DOCKERHUB_USERNAME }} 96 | password: ${{ secrets.DOCKERHUB_TOKEN }} 97 | 98 | - name: Build and push 99 | uses: docker/build-push-action@v5 100 | with: 101 | context: . 102 | push: true 103 | tags: jacoblincool/playwright:all 104 | target: all 105 | platforms: linux/amd64,linux/arm64/v8 106 | cache-from: type=gha 107 | cache-to: type=gha,mode=max 108 | 109 | build-light: 110 | name: Build Light Docker Images 111 | runs-on: ubuntu-latest 112 | steps: 113 | - name: Checkout Repository 114 | uses: actions/checkout@v4 115 | 116 | - name: Set up QEMU 117 | uses: docker/setup-qemu-action@v3 118 | 119 | - name: Set up Docker Buildx 120 | uses: docker/setup-buildx-action@v3 121 | 122 | - name: Login to Docker Hub 123 | uses: docker/login-action@v3 124 | with: 125 | username: ${{ secrets.DOCKERHUB_USERNAME }} 126 | password: ${{ secrets.DOCKERHUB_TOKEN }} 127 | 128 | - name: Build and push 129 | uses: docker/build-push-action@v5 130 | with: 131 | context: . 132 | push: true 133 | tags: jacoblincool/playwright:base-light 134 | target: base-light 135 | platforms: linux/amd64,linux/arm64/v8 136 | cache-from: type=gha 137 | cache-to: type=gha,mode=max 138 | 139 | - name: Build and push 140 | uses: docker/build-push-action@v5 141 | with: 142 | context: . 143 | push: true 144 | tags: jacoblincool/playwright:chromium-light 145 | target: chromium-light 146 | platforms: linux/amd64,linux/arm64/v8 147 | cache-from: type=gha 148 | cache-to: type=gha,mode=max 149 | 150 | build-server: 151 | needs: 152 | - build-normal 153 | - build-light 154 | strategy: 155 | matrix: 156 | target: 157 | - chromium-server 158 | - firefox-server 159 | - webkit-server 160 | - chrome-server 161 | - msedge-server 162 | - chromium-light-server 163 | runs-on: ubuntu-latest 164 | steps: 165 | - name: Checkout Repository 166 | uses: actions/checkout@v4 167 | 168 | - name: Set up QEMU 169 | uses: docker/setup-qemu-action@v3 170 | 171 | - name: Set up Docker Buildx 172 | uses: docker/setup-buildx-action@v3 173 | 174 | - name: Login to Docker Hub 175 | uses: docker/login-action@v3 176 | with: 177 | username: ${{ secrets.DOCKERHUB_USERNAME }} 178 | password: ${{ secrets.DOCKERHUB_TOKEN }} 179 | 180 | - name: Build and push 181 | uses: docker/build-push-action@v5 182 | with: 183 | context: . 184 | push: true 185 | tags: jacoblincool/playwright:${{ matrix.target }} 186 | target: ${{ matrix.target }} 187 | platforms: linux/amd64,linux/arm64/v8 188 | cache-from: type=gha 189 | cache-to: type=gha,mode=max 190 | 191 | update-readme: 192 | name: Update Docker Readme 193 | runs-on: ubuntu-latest 194 | needs: 195 | - build-normal-all 196 | - build-server 197 | steps: 198 | - name: Checkout Repository 199 | uses: actions/checkout@v4 200 | 201 | - name: Login to Docker Hub 202 | uses: docker/login-action@v3 203 | with: 204 | username: ${{ secrets.DOCKERHUB_USERNAME }} 205 | password: ${{ secrets.DOCKERHUB_TOKEN }} 206 | 207 | - name: Tag Image Alias 208 | run: ./scripts/tag.sh 209 | env: 210 | DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} 211 | DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} 212 | 213 | - name: Update Readme 214 | run: node ./scripts/update.js 215 | 216 | - name: Docker Hub Description 217 | uses: peter-evans/dockerhub-description@v4 218 | with: 219 | username: ${{ secrets.DOCKERHUB_USERNAME }} 220 | password: ${{ secrets.DOCKERHUB_TOKEN }} 221 | repository: jacoblincool/playwright 222 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: 7 | - main 8 | push: 9 | branches: 10 | - main 11 | paths-ignore: 12 | - "**/*.md" 13 | 14 | jobs: 15 | test: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Setup PNPM 22 | uses: pnpm/action-setup@v3 23 | with: 24 | version: latest 25 | run_install: false 26 | 27 | - name: Install Test Dependencies 28 | run: cd test && pnpm install 29 | 30 | - name: Set up QEMU 31 | uses: docker/setup-qemu-action@v3 32 | 33 | - name: Set up Docker Buildx 34 | uses: docker/setup-buildx-action@v3 35 | 36 | - name: Build Images 37 | run: docker buildx bake --load base chrome chromium firefox webkit all chromium-light 38 | 39 | - name: Check Versions 40 | run: | 41 | docker run --rm jacoblincool/playwright:base 42 | docker run --rm jacoblincool/playwright:chrome 43 | docker run --rm jacoblincool/playwright:chromium 44 | docker run --rm jacoblincool/playwright:firefox 45 | docker run --rm jacoblincool/playwright:webkit 46 | docker run --rm jacoblincool/playwright:all 47 | docker run --rm jacoblincool/playwright:chromium-light 48 | 49 | - name: Test 50 | run: ./test/test.sh 51 | 52 | - name: Save Screenshots 53 | uses: actions/upload-artifact@v4 54 | with: 55 | name: screenshots 56 | path: | 57 | test/artifacts/*.png 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Serverless directories 108 | .serverless/ 109 | 110 | # FuseBox cache 111 | .fusebox/ 112 | 113 | # DynamoDB Local files 114 | .dynamodb/ 115 | 116 | # TernJS port file 117 | .tern-port 118 | 119 | # Stores VSCode versions used for testing VSCode extensions 120 | .vscode-test 121 | 122 | # yarn v2 123 | .yarn/cache 124 | .yarn/unplugged 125 | .yarn/build-state.yml 126 | .yarn/install-state.gz 127 | .pnp.* 128 | 129 | **/.DS_Store 130 | 131 | test/pnpm-lock.yaml 132 | server/pnpm-lock.yaml 133 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:noble AS node 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV NVM_DIR "/root/.nvm" 5 | ENV NVM_VERSION "0.39.7" 6 | ENV NODE_VERSION "22.14.0" 7 | ENV NODE_PATH "$NVM_DIR/v$NODE_VERSION/lib/node_modules" 8 | ENV PATH "$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH" 9 | 10 | RUN rm /bin/sh && ln -s /bin/bash /bin/sh 11 | RUN apt update && apt -y install curl libatomic1 ffmpeg make python3 gcc g++ && apt-get clean 12 | RUN curl -o- "https://raw.githubusercontent.com/nvm-sh/nvm/v$NVM_VERSION/install.sh" | bash && rm -rf "$NVM_DIR/.cache" 13 | 14 | FROM node AS base 15 | 16 | ARG PLAYWRIGHT_VERSION "latest" 17 | 18 | RUN npm i -g playwright-core@$PLAYWRIGHT_VERSION && rm -rf /root/.npm 19 | 20 | ENV IMAGE_INFO="$(lsb_release -ds), Node $(node -v), Playwright $(playwright-core -V)" 21 | CMD eval echo $IMAGE_INFO 22 | 23 | FROM base AS chromium 24 | 25 | RUN playwright-core install --with-deps chromium 26 | 27 | ENV IMAGE_INFO="$IMAGE_INFO, $($(echo /root/.cache/ms-playwright/chromium-*/chrome-linux/chrome) --version)" 28 | 29 | FROM base AS firefox 30 | 31 | RUN playwright-core install --with-deps firefox 32 | 33 | ENV IMAGE_INFO="$IMAGE_INFO, $($(echo /root/.cache/ms-playwright/firefox-*/firefox/firefox) --version)" 34 | 35 | FROM base AS webkit 36 | 37 | RUN [ $(arch) == "armv7l" ] || playwright-core install --with-deps webkit 38 | 39 | ENV IMAGE_INFO="$IMAGE_INFO, $($(echo /root/.cache/ms-playwright/webkit-*/minibrowser-wpe/MiniBrowser) --version)" 40 | 41 | FROM base AS chrome 42 | 43 | RUN [ $(arch) == "armv7l" ] || [ $(arch) == "aarch64" ] || playwright-core install --with-deps chrome 44 | 45 | ENV IMAGE_INFO="$IMAGE_INFO, $(/usr/bin/google-chrome --version)" 46 | 47 | FROM base AS msedge 48 | 49 | RUN apt update && apt -y install gnupg && apt-get clean 50 | RUN [ $(arch) == "armv7l" ] || [ $(arch) == "aarch64" ] || playwright-core install --with-deps msedge 51 | 52 | ENV IMAGE_INFO="$IMAGE_INFO, $(/usr/bin/microsoft-edge --version)" 53 | 54 | FROM chromium AS all 55 | 56 | ENV IMAGE_INFO="$IMAGE_INFO, $($(echo /root/.cache/ms-playwright/firefox-*/firefox/firefox) --version)" 57 | ENV IMAGE_INFO="$IMAGE_INFO, $($(echo /root/.cache/ms-playwright/webkit-*/minibrowser-wpe/MiniBrowser) --version)" 58 | ENV IMAGE_INFO="$IMAGE_INFO, $(/usr/bin/google-chrome --version)" 59 | ENV IMAGE_INFO="$IMAGE_INFO, $(/usr/bin/microsoft-edge --version)" 60 | 61 | RUN apt update && apt -y install gnupg && apt-get clean 62 | RUN playwright-core install --with-deps firefox 63 | RUN [ $(arch) == "armv7l" ] || playwright-core install --with-deps webkit 64 | RUN [ $(arch) == "armv7l" ] || [ $(arch) == "aarch64" ] || playwright-core install --with-deps chrome 65 | RUN [ $(arch) == "armv7l" ] || [ $(arch) == "aarch64" ] || playwright-core install --with-deps msedge 66 | 67 | ### Lightweight Playwright ### 68 | 69 | FROM node:alpine3.21 AS base-light 70 | 71 | ARG PLAYWRIGHT_VERSION "latest" 72 | 73 | RUN npm i -g playwright-core@$PLAYWRIGHT_VERSION && rm -rf /root/.npm 74 | CMD eval echo $IMAGE_INFO 75 | 76 | ENV IMAGE_INFO="Alpine $(cat /etc/alpine-release), Node $(node -v), Playwright $(playwright-core -V)" 77 | 78 | FROM base-light AS chromium-light 79 | 80 | RUN apk update && apk add --no-cache chromium 81 | 82 | ENV IMAGE_INFO="$IMAGE_INFO, $(/usr/bin/chromium --version)" 83 | 84 | ### Playwright Server ### 85 | 86 | FROM chromium AS chromium-server 87 | 88 | WORKDIR /server 89 | 90 | COPY server/ . 91 | RUN npm link playwright-core && npm install 92 | 93 | CMD ["node", "index.mjs"] 94 | 95 | FROM firefox AS firefox-server 96 | 97 | WORKDIR /server 98 | 99 | COPY server/ . 100 | RUN npm link playwright-core && npm install 101 | 102 | CMD ["node", "index.mjs"] 103 | 104 | FROM webkit AS webkit-server 105 | 106 | WORKDIR /server 107 | 108 | COPY server/ . 109 | RUN npm link playwright-core && npm install 110 | 111 | CMD ["node", "index.mjs"] 112 | 113 | FROM chrome AS chrome-server 114 | 115 | WORKDIR /server 116 | 117 | COPY server/ . 118 | RUN npm link playwright-core && npm install 119 | 120 | CMD ["node", "index.mjs"] 121 | 122 | FROM msedge AS msedge-server 123 | 124 | WORKDIR /server 125 | 126 | COPY server/ . 127 | RUN npm link playwright-core && npm install 128 | 129 | CMD ["node", "index.mjs"] 130 | 131 | FROM chromium-light AS chromium-light-server 132 | 133 | WORKDIR /server 134 | 135 | COPY server/ . 136 | RUN npm link playwright-core && npm install 137 | 138 | CMD ["node", "index.mjs"] 139 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 JacobLinCool 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Playwright Docker Images 2 | 3 | multi-arch x multi-browser 4 | 5 | View on Docker Hub: [https://hub.docker.com/r/jacoblincool/playwright/](https://hub.docker.com/r/jacoblincool/playwright/) 6 | 7 | ## Tags 8 | 9 | - `jacoblincool/playwright:base` - Ubuntu Jammy, Node 20, Playwright 10 | - `jacoblincool/playwright:chromium` - Ubuntu Jammy, Node 20, Playwright, Chromium 11 | - `jacoblincool/playwright:firefox` - Ubuntu Jammy, Node 20, Playwright, Firefox 12 | - `jacoblincool/playwright:webkit` - Ubuntu Jammy, Node 20, Playwright, WebKit 13 | - `jacoblincool/playwright:chrome` - Ubuntu Jammy, Node 20, Playwright, Chrome 14 | - `jacoblincool/playwright:msedge` - Ubuntu Jammy, Node 20, Playwright, Edge 15 | - `jacoblincool/playwright:all` - Ubuntu Jammy, Node 20, Playwright, All Browsers 16 | 17 | ### Lightweight Images 18 | 19 | - `jacoblincool/playwright:base-light` - Alpine 3.20, Node 23, Playwright 20 | - `jacoblincool/playwright:chromium-light` - Alpine 3.20, Node 23, Playwright, Chromium 21 | 22 | ### Playwright Servers 23 | 24 | Those images are running Playwright Server and expose the WebSocket endpoint, see [./server](./server) for more details. 25 | 26 | - `jacoblincool/playwright:chromium-server` 27 | - `jacoblincool/playwright:firefox-server` 28 | - `jacoblincool/playwright:webkit-server` 29 | - `jacoblincool/playwright:chrome-server` 30 | - `jacoblincool/playwright:msedge-server` 31 | - `jacoblincool/playwright:chromium-light-server` 32 | 33 | The default endpoint is `ws://localhost:53333/playwright`, you can override it by setting the `BROWSER_PORT` and `BROWSER_WS_ENDPOINT` environment variables. 34 | 35 | They can be run using the following command: 36 | 37 | ```sh 38 | docker run --rm -p 53333:53333 jacoblincool/playwright:chromium-light-server 39 | ``` 40 | 41 | #### Connect to server using Playwright 42 | 43 | ##### Javascript 44 | 45 | ```javascript 46 | import { chromium } from "playwright"; 47 | const browser = await chromium.connect("ws://localhost:53333/playwright"); 48 | ``` 49 | 50 | ##### Python 51 | 52 | In [examples](https://playwright.dev/python/docs/api/class-playwright) replace (`BrowserType` method) `launch` with `connect` 53 | 54 | ```python 55 | import asyncio 56 | import playwright.async_api as playwright 57 | 58 | 59 | async def main(): 60 | async with playwright.async_playwright() as playwright: 61 | browser: playwright.Browser = playwright.chromium.connect("ws://localhost:53333/playwright") 62 | 63 | 64 | asyncio.run(main()) 65 | ``` 66 | 67 | ## Supported Architectures 68 | 69 | | Browser | ARMv8 (`aarch64`) | AMD64 (`x86_64`) | 70 | | -------- | :---------------: | :--------------: | 71 | | Chromium | ✅ | ✅ | 72 | | Firefox | ✅ | ✅ | 73 | | WebKit | ✅ | ✅ | 74 | | Chrome | ❌ | ✅ | 75 | | Edge | ❌ | ✅ | 76 | 77 | ### Lightweight Image Architectures 78 | 79 | | Browser | ARMv8 (`aarch64`) | AMD64 (`x86_64`) | 80 | | -------- | :---------------: | :--------------: | 81 | | Chromium | ✅ | ✅ | 82 | 83 | ## Sources 84 | 85 | GitHub: 86 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | base: 3 | build: 4 | context: . 5 | target: base 6 | image: jacoblincool/playwright:base 7 | chromium: 8 | build: 9 | context: . 10 | target: chromium 11 | image: jacoblincool/playwright:chromium 12 | firefox: 13 | build: 14 | context: . 15 | target: firefox 16 | image: jacoblincool/playwright:firefox 17 | webkit: 18 | build: 19 | context: . 20 | target: webkit 21 | image: jacoblincool/playwright:webkit 22 | chrome: 23 | build: 24 | context: . 25 | target: chrome 26 | image: jacoblincool/playwright:chrome 27 | msedge: 28 | build: 29 | context: . 30 | target: msedge 31 | image: jacoblincool/playwright:msedge 32 | all: 33 | build: 34 | context: . 35 | target: all 36 | image: jacoblincool/playwright:all 37 | base-light: 38 | build: 39 | context: . 40 | target: base-light 41 | image: jacoblincool/playwright:base-light 42 | chromium-light: 43 | build: 44 | context: . 45 | target: chromium-light 46 | image: jacoblincool/playwright:chromium-light 47 | 48 | chromium-server: 49 | build: 50 | context: . 51 | target: chromium-server 52 | image: jacoblincool/playwright:chromium-server 53 | firefox-server: 54 | build: 55 | context: . 56 | target: firefox-server 57 | image: jacoblincool/playwright:firefox-server 58 | webkit-server: 59 | build: 60 | context: . 61 | target: webkit-server 62 | image: jacoblincool/playwright:webkit-server 63 | chrome-server: 64 | build: 65 | context: . 66 | target: chrome-server 67 | image: jacoblincool/playwright:chrome-server 68 | msedge-server: 69 | build: 70 | context: . 71 | target: msedge-server 72 | image: jacoblincool/playwright:msedge-server 73 | chromium-light-server: 74 | build: 75 | context: . 76 | target: chromium-light-server 77 | image: jacoblincool/playwright:chromium-light-server 78 | -------------------------------------------------------------------------------- /scripts/build-light.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker buildx bake --push --set "*.platform=linux/arm64/v8,linux/amd64" base-light chromium-light 3 | -------------------------------------------------------------------------------- /scripts/build-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker buildx bake --push --set "*.platform=linux/arm64/v8,linux/amd64" chromium-server firefox-server webkit-server chrome-server msedge-server chromium-light-server 3 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker buildx bake --push --set "*.platform=linux/arm64/v8,linux/amd64" base chromium firefox webkit chrome msedge all 3 | -------------------------------------------------------------------------------- /scripts/local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker buildx bake --load 3 | -------------------------------------------------------------------------------- /scripts/tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | npm i -g regctl 3 | regctl registry login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_TOKEN 4 | 5 | function get_version() { 6 | echo $1 | cut -d' ' -f2 7 | } 8 | 9 | regctl image copy jacoblincool/playwright:all jacoblincool/playwright:latest 10 | 11 | tags=("base" "chromium" "firefox" "webkit" "chrome" "msedge" "all" "base-light" "chromium-light" "chromium-server" "firefox-server" "webkit-server" "chrome-server" "msedge-server" "chromium-light-server") 12 | for tag in "${tags[@]}"; do 13 | docker pull jacoblincool/playwright:$tag 14 | ver=$(get_version "$(docker run --rm jacoblincool/playwright:$tag playwright-core --version)") 15 | regctl image copy jacoblincool/playwright:$tag jacoblincool/playwright:$tag-$ver 16 | done 17 | 18 | regctl registry logout 19 | -------------------------------------------------------------------------------- /scripts/update.js: -------------------------------------------------------------------------------- 1 | const fs = require("node:fs"); 2 | const path = require("node:path"); 3 | const { execSync } = require("node:child_process"); 4 | 5 | const readme = fs.readFileSync(path.join(__dirname, "..", "README.md"), "utf8"); 6 | let new_readme = `See https://github.com/JacobLinCool/playwright-docker\n\n---\n\n` + readme; 7 | 8 | const tag_regex = /`([a-z0-9/:-]+)` - ([^`\n]+)/g; 9 | 10 | const matches = readme.matchAll(tag_regex); 11 | if (matches) { 12 | for (const match of matches) { 13 | const [, tag, description] = match; 14 | 15 | execSync(`docker pull ${tag.trim()}`, { stdio: "inherit" }); 16 | const info = execSync(`docker run --rm ${tag}`).toString().trim().replace("\n", " "); 17 | console.log(tag.trim(), info); 18 | 19 | new_readme = new_readme.replace(match[0], `\`${tag}\` - ${info}`); 20 | } 21 | } 22 | 23 | fs.writeFileSync(path.join(__dirname, "..", "README.md"), new_readme); 24 | -------------------------------------------------------------------------------- /server/index.mjs: -------------------------------------------------------------------------------- 1 | import { chromium, firefox, webkit } from "playwright-core"; 2 | import { existsSync } from "node:fs"; 3 | import shellQuoteParse from "shell-quote/parse.js"; 4 | 5 | main(); 6 | 7 | async function main() { 8 | const server = await launch(); 9 | console.log(server.wsEndpoint()); 10 | } 11 | 12 | async function launch() { 13 | const info = (process.env.IMAGE_INFO || "").toLowerCase(); 14 | 15 | // https://playwright.dev/docs/api/class-browsertype#browser-type-launch-server 16 | const options = { 17 | port: parseInt(process.env.BROWSER_PORT || 53333), 18 | wsPath: process.env.BROWSER_WS_ENDPOINT || "/playwright", 19 | channel: process.env.BROWSER_CHANNEL, 20 | } 21 | if (process.env.BROWSER_TIMEOUT) { 22 | options.timeout = parseInt(process.env.BROWSER_TIMEOUT) 23 | } 24 | if (process.env.BROWSER_PROXY_SERVER) { 25 | options.proxy = { 26 | server: process.env.BROWSER_PROXY_SERVER, 27 | bypass: process.env.BROWSER_PROXY_BYPASS, 28 | username: process.env.BROWSER_PROXY_USERNAME, 29 | password: process.env.BROWSER_PROXY_PASSWORD, 30 | } 31 | } 32 | if (process.env.BROWSER_ARGS) { 33 | options.args = shellQuoteParse(process.env.BROWSER_ARGS) 34 | } 35 | 36 | if (info.includes("chromium")) { 37 | return existsSync("/etc/alpine-release") 38 | ? await chromium.launchServer({ executablePath: "/usr/bin/chromium", ...options }) 39 | : await chromium.launchServer(options); 40 | } else if (info.includes("firefox")) { 41 | return await firefox.launchServer(options); 42 | } else if (info.includes("webkit")) { 43 | return await webkit.launchServer(options); 44 | } else if (info.includes("chrome")) { 45 | return await chromium.launchServer({ executablePath: "/usr/bin/google-chrome", ...options }); 46 | } else if (info.includes("msedge")) { 47 | return await chromium.launchServer({ executablePath: "/usr/bin/microsoft-edge", ...options }); 48 | } else { 49 | throw new Error(`Unknown browser info: ${info}`); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "server", 4 | "scripts": { 5 | "test": "echo \"Error: no test specified\" && exit 1" 6 | }, 7 | "keywords": [], 8 | "author": "JacobLinCool (https://github.com/JacobLinCool)", 9 | "license": "MIT", 10 | "dependencies": { 11 | "playwright-core": "^1.41.1", 12 | "shell-quote": "^1.8.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | artifacts/ 2 | -------------------------------------------------------------------------------- /test/light.js: -------------------------------------------------------------------------------- 1 | import { test } from "./test.js"; 2 | 3 | const TESTS = { 4 | arm64: ["chromium"], 5 | x64: ["chromium"], 6 | }; 7 | 8 | test(TESTS); 9 | -------------------------------------------------------------------------------- /test/normal.js: -------------------------------------------------------------------------------- 1 | import { test } from "./test.js"; 2 | 3 | const TESTS = { 4 | // arm: ["chromium", "firefox"], 5 | arm64: ["chromium", "firefox", "webkit"], 6 | x64: ["chromium", "firefox", "webkit", "chrome", "msedge"], 7 | }; 8 | 9 | test(TESTS); 10 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "test", 4 | "description": "", 5 | "version": "0.1.0", 6 | "main": "test.js", 7 | "type": "module", 8 | "scripts": { 9 | "test": "node normal.js", 10 | "light": "node light.js" 11 | }, 12 | "keywords": [], 13 | "author": "JacobLinCool (https://github.com/JacobLinCool)", 14 | "license": "MIT", 15 | "dependencies": { 16 | "html-to-md": "^0.8.5", 17 | "playwright-core": "^1.41.1" 18 | }, 19 | "packageManager": "pnpm@8.15.1" 20 | } 21 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | import { existsSync, writeFileSync } from "fs"; 2 | import { chromium, firefox, webkit } from "playwright-core"; 3 | import html2md from "html-to-md"; 4 | 5 | export async function test(TESTS) { 6 | if (TESTS[process.arch] === undefined) { 7 | console.log(`Skipping tests for ${process.arch}`); 8 | } 9 | 10 | for (const type of TESTS[process.arch]) { 11 | const browser = await launch(type); 12 | const page = await browser.newPage(); 13 | 14 | await page.goto("https://html5test.opensuse.org/"); 15 | await page.waitForSelector("#score strong"); 16 | 17 | await page.screenshot({ path: `artifacts/${process.arch}-${type}.png`, fullPage: true }); 18 | const html = await page.locator("#results").innerHTML(); 19 | const md = html2md(html); 20 | if (process.env.GITHUB_STEP_SUMMARY) { 21 | writeFileSync(process.env.GITHUB_STEP_SUMMARY, md); 22 | } else { 23 | console.log(md); 24 | } 25 | 26 | await page.close(); 27 | await browser.close(); 28 | } 29 | } 30 | 31 | async function launch(type) { 32 | switch (type) { 33 | case "chromium": 34 | return existsSync("/etc/alpine-release") ? await chromium.launch({ executablePath: "/usr/bin/chromium" }) : await chromium.launch(); 35 | case "firefox": 36 | return await firefox.launch(); 37 | case "webkit": 38 | return await webkit.launch(); 39 | case "chrome": 40 | return await chromium.launch({ executablePath: "/usr/bin/google-chrome" }); 41 | case "msedge": 42 | return await chromium.launch({ executablePath: "/usr/bin/microsoft-edge" }); 43 | default: 44 | throw new Error(`Unknown browser type: ${type}`); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd $(dirname $0) 3 | docker run --rm -v "$(pwd):/test" -w "/test" jacoblincool/playwright:all node normal.js 4 | docker run --rm -v "$(pwd):/test" -w "/test" jacoblincool/playwright:chromium-light node light.js 5 | --------------------------------------------------------------------------------