Skip to content

Commit c8e74a0

Browse files
Michael McNeesclaude
authored andcommitted
feat: custom pre-built Playwright Docker images (#32)
Closes #32 Adds packages/playwright/ with Dockerfile.node and Dockerfile.python pre-installing Playwright on top of the official Microsoft images, eliminating the npm install / pip install bootstrap step for browser/run power users. Also adds a GitHub Actions workflow that builds and pushes multi-arch (amd64/arm64) images to GHCR on every PLAYWRIGHT_VERSION change, with automated smoke tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2 parents 82f3db0 + 976467e commit c8e74a0

5 files changed

Lines changed: 123 additions & 2 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: Build Playwright Images
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- packages/playwright/PLAYWRIGHT_VERSION
8+
workflow_dispatch:
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
packages: write
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Read Playwright version
22+
id: version
23+
run: |
24+
VERSION=$(cat packages/playwright/PLAYWRIGHT_VERSION)
25+
echo "playwright_version=${VERSION}" >> "$GITHUB_OUTPUT"
26+
27+
- name: Compute image tags
28+
id: tags
29+
run: |
30+
VERSION="${{ steps.version.outputs.playwright_version }}"
31+
NODE_TAGS="ghcr.io/dvflw/mantle-playwright-node:${VERSION}"
32+
PY_TAGS="ghcr.io/dvflw/mantle-playwright-python:${VERSION}"
33+
if [[ "${VERSION}" != *-* ]]; then
34+
NODE_TAGS="${NODE_TAGS}"$'\n'"ghcr.io/dvflw/mantle-playwright-node:latest"
35+
PY_TAGS="${PY_TAGS}"$'\n'"ghcr.io/dvflw/mantle-playwright-python:latest"
36+
fi
37+
echo "node_tags<<EOF" >> "$GITHUB_OUTPUT"
38+
echo "${NODE_TAGS}" >> "$GITHUB_OUTPUT"
39+
echo "EOF" >> "$GITHUB_OUTPUT"
40+
echo "python_tags<<EOF" >> "$GITHUB_OUTPUT"
41+
echo "${PY_TAGS}" >> "$GITHUB_OUTPUT"
42+
echo "EOF" >> "$GITHUB_OUTPUT"
43+
44+
- name: Set up QEMU
45+
uses: docker/setup-qemu-action@v3
46+
47+
- name: Set up Docker Buildx
48+
uses: docker/setup-buildx-action@v3
49+
50+
- name: Log in to GHCR
51+
uses: docker/login-action@v3
52+
with:
53+
registry: ghcr.io
54+
username: ${{ github.actor }}
55+
password: ${{ secrets.GITHUB_TOKEN }}
56+
57+
- name: Build and push Node image
58+
uses: docker/build-push-action@v6
59+
with:
60+
context: packages/playwright/
61+
file: packages/playwright/Dockerfile.node
62+
platforms: linux/amd64,linux/arm64
63+
push: true
64+
build-args: |
65+
PLAYWRIGHT_VERSION=${{ steps.version.outputs.playwright_version }}
66+
tags: ${{ steps.tags.outputs.node_tags }}
67+
68+
- name: Build and push Python image
69+
uses: docker/build-push-action@v6
70+
with:
71+
context: packages/playwright/
72+
file: packages/playwright/Dockerfile.python
73+
platforms: linux/amd64,linux/arm64
74+
push: true
75+
build-args: |
76+
PLAYWRIGHT_VERSION=${{ steps.version.outputs.playwright_version }}
77+
tags: ${{ steps.tags.outputs.python_tags }}
78+
79+
- name: Smoke-test Node image
80+
run: |
81+
docker run --rm \
82+
ghcr.io/dvflw/mantle-playwright-node:${{ steps.version.outputs.playwright_version }} \
83+
node -e "require('playwright'); console.log('ok')"
84+
85+
- name: Smoke-test Python image
86+
run: |
87+
docker run --rm \
88+
ghcr.io/dvflw/mantle-playwright-python:${{ steps.version.outputs.playwright_version }} \
89+
python3 -c "import playwright; print('ok')"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ARG PLAYWRIGHT_VERSION
2+
FROM mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-noble
3+
ARG PLAYWRIGHT_VERSION
4+
RUN npm install --global --no-save playwright@${PLAYWRIGHT_VERSION}
5+
ENV NODE_PATH=/usr/lib/node_modules
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ARG PLAYWRIGHT_VERSION
2+
FROM mcr.microsoft.com/playwright/python:v${PLAYWRIGHT_VERSION}-noble
3+
ARG PLAYWRIGHT_VERSION
4+
RUN pip install --quiet playwright==${PLAYWRIGHT_VERSION}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.52.0

packages/site/src/content/docs/workflow-reference/connectors.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -745,8 +745,30 @@ Runs browser automation scripts (JavaScript, TypeScript, or Python) using Playwr
745745

746746
**Container Images:**
747747

748-
- **JavaScript/TypeScript:** `mcr.microsoft.com/playwright:v1.52.0-noble`
749-
- **Python:** `mcr.microsoft.com/playwright/python:v1.52.0-noble`
748+
By default, `browser/run` uses the official Microsoft Playwright images, which install Playwright inside the container on every invocation (adds ~30 s cold-start). To skip this step, use the pre-built Mantle images:
749+
750+
| Language | Default Image | Pre-built Image |
751+
|---|---|---|
752+
| JavaScript / TypeScript | `mcr.microsoft.com/playwright:v1.52.0-noble` | `ghcr.io/dvflw/mantle-playwright-node:1.52.0` |
753+
| Python | `mcr.microsoft.com/playwright/python:v1.52.0-noble` | `ghcr.io/dvflw/mantle-playwright-python:1.52.0` |
754+
755+
Pass a pre-built image via the `image` param:
756+
757+
```yaml
758+
steps:
759+
- name: run_browser
760+
action: browser/run
761+
timeout: "2m"
762+
params:
763+
image: ghcr.io/dvflw/mantle-playwright-node:1.52.0
764+
language: javascript
765+
script: |
766+
const page = await browser.newPage();
767+
await page.goto('https://example.com');
768+
console.log(await page.title());
769+
```
770+
771+
The `latest` tag tracks the most recent stable release. Images are rebuilt automatically when the Playwright version is updated.
750772

751773
**Artifacts:** Scripts can write files to `/mantle/artifacts/` directory for screenshots, PDFs, HAR files, and other outputs. Declare artifacts in the step to register them with the execution.
752774

0 commit comments

Comments
 (0)