Skip to content

Commit 0098ac0

Browse files
authored
chore: added Docker image publishing (microsoft#1171)
1 parent b1696fc commit 0098ac0

File tree

8 files changed

+343
-9
lines changed

8 files changed

+343
-9
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: "publish canary docker"
2+
3+
on:
4+
workflow_dispatch:
5+
schedule:
6+
- cron: "10 0 * * *"
7+
8+
jobs:
9+
publish-canary:
10+
name: "Publish canary Docker"
11+
runs-on: ubuntu-20.04
12+
if: github.repository == 'microsoft/playwright-python'
13+
steps:
14+
- uses: actions/checkout@v2
15+
- name: Set up Python
16+
uses: actions/setup-python@v2
17+
with:
18+
python-version: "3.10"
19+
- name: Install dependencies & browsers
20+
run: |
21+
python -m pip install --upgrade pip wheel
22+
pip install -r local-requirements.txt
23+
pip install -e .
24+
- uses: azure/docker-login@v1
25+
with:
26+
login-server: playwright.azurecr.io
27+
username: playwright
28+
password: ${{ secrets.DOCKER_PASSWORD }}
29+
- name: Set up Docker QEMU for arm64 docker builds
30+
uses: docker/setup-qemu-action@v1
31+
with:
32+
platforms: arm64
33+
- name: publish docker canary
34+
run: ./utils/docker/publish_docker.sh canary
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: "publish release - Docker"
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
is_release:
7+
required: false
8+
type: boolean
9+
description: "Is this a release image?"
10+
11+
release:
12+
types: [published]
13+
14+
jobs:
15+
publish-docker-release:
16+
name: "publish to DockerHub"
17+
runs-on: ubuntu-20.04
18+
if: github.repository == 'microsoft/playwright-python'
19+
steps:
20+
- uses: actions/checkout@v2
21+
- name: Set up Python
22+
uses: actions/setup-python@v2
23+
with:
24+
python-version: "3.10"
25+
- uses: azure/docker-login@v1
26+
with:
27+
login-server: playwright.azurecr.io
28+
username: playwright
29+
password: ${{ secrets.DOCKER_PASSWORD }}
30+
- name: Set up Docker QEMU for arm64 docker builds
31+
uses: docker/setup-qemu-action@v1
32+
with:
33+
platforms: arm64
34+
- name: Install dependencies & browsers
35+
run: |
36+
python -m pip install --upgrade pip wheel
37+
pip install -r local-requirements.txt
38+
pip install -e .
39+
- run: ./utils/docker/publish_docker.sh stable
40+
if: (github.event_name != 'workflow_dispatch' && !github.event.release.prerelease) || (github.event_name == 'workflow_dispatch' && github.event.inputs.is_release == 'true')
41+
- run: ./utils/docker/publish_docker.sh canary
42+
if: (github.event_name != 'workflow_dispatch' && github.event.release.prerelease) || (github.event_name == 'workflow_dispatch' && github.event.inputs.is_release != 'true')

.github/workflows/test_docker.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Test Docker
2+
on:
3+
push:
4+
paths:
5+
- '.github/workflows/test_docker.yml'
6+
branches:
7+
- main
8+
pull_request:
9+
paths:
10+
- '.github/workflows/test_docker.yml'
11+
branches:
12+
- main
13+
jobs:
14+
build:
15+
timeout-minutes: 60
16+
runs-on: ubuntu-20.04
17+
steps:
18+
- uses: actions/checkout@v2
19+
- name: Set up Python
20+
uses: actions/setup-python@v2
21+
with:
22+
python-version: "3.10"
23+
- name: Install dependencies
24+
run: |
25+
python -m pip install --upgrade pip
26+
pip install -r local-requirements.txt
27+
pip install -e .
28+
- name: Build Docker image
29+
run: bash utils/docker/build.sh --amd64 focal playwright-python:localbuild-focal
30+
- name: Test
31+
run: |
32+
CONTAINER_ID="$(docker run --rm -v $(pwd):/root/playwright --name playwright-docker-test --workdir /root/playwright/ -d -t playwright-python:localbuild-focal /bin/bash)"
33+
docker exec "${CONTAINER_ID}" pip install -r local-requirements.txt
34+
docker exec "${CONTAINER_ID}" pip install -e .
35+
docker exec "${CONTAINER_ID}" python setup.py bdist_wheel
36+
docker exec "${CONTAINER_ID}" xvfb-run pytest -vv tests/sync/
37+
docker exec "${CONTAINER_ID}" xvfb-run pytest -vv tests/async/

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ _repo_version.py
1515
coverage.xml
1616
junit/
1717
htmldocs/
18+
utils/docker/dist/

tests/async/test_queryselector.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from playwright.async_api import Error, Page
44

55

6-
async def test_selectors_register_should_work(selectors, browser):
6+
async def test_selectors_register_should_work(selectors, browser, browser_name):
77
tag_selector = """
88
{
99
create(root, target) {
@@ -17,23 +17,40 @@ async def test_selectors_register_should_work(selectors, browser):
1717
}
1818
}"""
1919

20+
selector_name = f"tag_{browser_name}"
21+
selector2_name = f"tag2_{browser_name}"
22+
2023
# Register one engine before creating context.
21-
await selectors.register("tag", tag_selector)
24+
await selectors.register(selector_name, tag_selector)
2225

2326
context = await browser.new_context()
2427
# Register another engine after creating context.
25-
await selectors.register("tag2", tag_selector)
28+
await selectors.register(selector2_name, tag_selector)
2629

2730
page = await context.new_page()
2831
await page.set_content("<div><span></span></div><div></div>")
2932

30-
assert await page.eval_on_selector("tag=DIV", "e => e.nodeName") == "DIV"
31-
assert await page.eval_on_selector("tag=SPAN", "e => e.nodeName") == "SPAN"
32-
assert await page.eval_on_selector_all("tag=DIV", "es => es.length") == 2
33+
assert (
34+
await page.eval_on_selector(f"{selector_name}=DIV", "e => e.nodeName") == "DIV"
35+
)
36+
assert (
37+
await page.eval_on_selector(f"{selector_name}=SPAN", "e => e.nodeName")
38+
== "SPAN"
39+
)
40+
assert (
41+
await page.eval_on_selector_all(f"{selector_name}=DIV", "es => es.length") == 2
42+
)
3343

34-
assert await page.eval_on_selector("tag2=DIV", "e => e.nodeName") == "DIV"
35-
assert await page.eval_on_selector("tag2=SPAN", "e => e.nodeName") == "SPAN"
36-
assert await page.eval_on_selector_all("tag2=DIV", "es => es.length") == 2
44+
assert (
45+
await page.eval_on_selector(f"{selector2_name}=DIV", "e => e.nodeName") == "DIV"
46+
)
47+
assert (
48+
await page.eval_on_selector(f"{selector2_name}=SPAN", "e => e.nodeName")
49+
== "SPAN"
50+
)
51+
assert (
52+
await page.eval_on_selector_all(f"{selector2_name}=DIV", "es => es.length") == 2
53+
)
3754

3855
# Selector names are case-sensitive.
3956
with pytest.raises(Error) as exc:

utils/docker/Dockerfile.focal

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
FROM ubuntu:focal
2+
3+
ARG DEBIAN_FRONTEND=noninteractive
4+
ARG TZ=America/Los_Angeles
5+
6+
# === INSTALL Python ===
7+
8+
RUN apt-get update && \
9+
# Install Python
10+
apt-get install -y python3 python3-distutils curl && \
11+
update-alternatives --install /usr/bin/python python /usr/bin/python3 1 && \
12+
curl -sSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
13+
python get-pip.py && \
14+
rm get-pip.py && \
15+
# Feature-parity with node.js base images.
16+
apt-get install -y --no-install-recommends git openssh-client && \
17+
# clean apt cache
18+
rm -rf /var/lib/apt/lists/* && \
19+
# Create the pwuser
20+
adduser pwuser
21+
22+
# === BAKE BROWSERS INTO IMAGE ===
23+
24+
ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
25+
26+
# 1. Add tip-of-tree Playwright package to install its browsers.
27+
# The package should be built beforehand from tip-of-tree Playwright.
28+
COPY ./dist/*-manylinux*.whl /tmp/
29+
30+
# 2. Bake in browsers & deps.
31+
# Browsers will be downloaded in `/ms-playwright`.
32+
# Note: make sure to set 777 to the registry so that any user can access
33+
# registry.
34+
RUN mkdir /ms-playwright && \
35+
mkdir /ms-playwright-agent && \
36+
cd /ms-playwright-agent && \
37+
# if its amd64 then install the manylinux1_x86_64 pip package
38+
if [ "$(uname -m)" = "x86_64" ]; then pip install /tmp/*manylinux1_x86_64*.whl; fi && \
39+
# if its arm64 then install the manylinux1_aarch64 pip package
40+
if [ "$(uname -m)" = "aarch64" ]; then pip install /tmp/*manylinux_2_17_aarch64*.whl; fi && \
41+
playwright install --with-deps && rm -rf /var/lib/apt/lists/* && \
42+
rm /tmp/*.whl && \
43+
rm -rf /ms-playwright-agent && \
44+
chmod -R 777 /ms-playwright

utils/docker/build.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/bash
2+
set -e
3+
set +x
4+
5+
if [[ ($1 == '--help') || ($1 == '-h') || ($1 == '') || ($2 == '') ]]; then
6+
echo "usage: $(basename $0) {--arm64,--amd64} {bionic,focal} playwright:localbuild-focal"
7+
echo
8+
echo "Build Playwright docker image and tag it as 'playwright:localbuild-focal'."
9+
echo "Once image is built, you can run it with"
10+
echo ""
11+
echo " docker run --rm -it playwright:localbuild-focal /bin/bash"
12+
echo ""
13+
echo "NOTE: this requires on Playwright PIP dependencies to be installed"
14+
echo ""
15+
exit 0
16+
fi
17+
18+
function cleanup() {
19+
rm -rf "dist/"
20+
}
21+
22+
trap "cleanup; cd $(pwd -P)" EXIT
23+
cd "$(dirname "$0")"
24+
25+
pushd ../../
26+
python setup.py bdist_wheel --all
27+
popd
28+
mkdir dist/
29+
cp ../../dist/*-manylinux*.whl dist/
30+
31+
PLATFORM=""
32+
if [[ "$1" == "--arm64" ]]; then
33+
PLATFORM="linux/arm64";
34+
elif [[ "$1" == "--amd64" ]]; then
35+
PLATFORM="linux/amd64"
36+
else
37+
echo "ERROR: unknown platform specifier - $1. Only --arm64 or --amd64 is supported"
38+
exit 1
39+
fi
40+
41+
docker build --platform "${PLATFORM}" -t "$3" -f "Dockerfile.$2" .

utils/docker/publish_docker.sh

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/bin/bash
2+
3+
set -e
4+
set +x
5+
6+
trap "cd $(pwd -P)" EXIT
7+
cd "$(dirname "$0")"
8+
9+
MCR_IMAGE_NAME="playwright-python"
10+
PW_VERSION=$(python -c "from playwright._repo_version import version;print(version)")
11+
12+
RELEASE_CHANNEL="$1"
13+
if [[ "${RELEASE_CHANNEL}" == "stable" ]]; then
14+
if [[ "${PW_VERSION}" == *post* ]]; then
15+
echo "ERROR: cannot publish stable docker with Playwright version '${PW_VERSION}'"
16+
exit 1
17+
fi
18+
elif [[ "${RELEASE_CHANNEL}" == "canary" ]]; then
19+
if [[ "${PW_VERSION}" != *post* ]]; then
20+
echo "ERROR: cannot publish canary docker with Playwright version '${PW_VERSION}'"
21+
exit 1
22+
fi
23+
else
24+
echo "ERROR: unknown release channel - ${RELEASE_CHANNEL}"
25+
echo "Must be either 'stable' or 'canary'"
26+
exit 1
27+
fi
28+
29+
if [[ -z "${GITHUB_SHA}" ]]; then
30+
echo "ERROR: GITHUB_SHA env variable must be specified"
31+
exit 1
32+
fi
33+
34+
BIONIC_TAGS=(
35+
"next-bionic"
36+
"v${PW_VERSION}-bionic"
37+
)
38+
if [[ "$RELEASE_CHANNEL" == "stable" ]]; then
39+
BIONIC_TAGS+=("bionic")
40+
fi
41+
42+
FOCAL_TAGS=(
43+
"next"
44+
"sha-${GITHUB_SHA}"
45+
"next-focal"
46+
"v${PW_VERSION}-focal"
47+
"v${PW_VERSION}"
48+
)
49+
50+
if [[ "$RELEASE_CHANNEL" == "stable" ]]; then
51+
FOCAL_TAGS+=("latest")
52+
FOCAL_TAGS+=("focal")
53+
fi
54+
55+
tag_and_push() {
56+
local source="$1"
57+
local target="$2"
58+
echo "-- tagging: $target"
59+
docker tag $source $target
60+
docker push $target
61+
}
62+
63+
publish_docker_images_with_arch_suffix() {
64+
local FLAVOR="$1"
65+
local TAGS=()
66+
if [[ "$FLAVOR" == "bionic" ]]; then
67+
TAGS=("${BIONIC_TAGS[@]}")
68+
elif [[ "$FLAVOR" == "focal" ]]; then
69+
TAGS=("${FOCAL_TAGS[@]}")
70+
else
71+
echo "ERROR: unknown flavor - $FLAVOR. Must be either 'bionic' or 'focal'"
72+
exit 1
73+
fi
74+
local ARCH="$2"
75+
if [[ "$ARCH" != "amd64" && "$ARCH" != "arm64" ]]; then
76+
echo "ERROR: unknown arch - $ARCH. Must be either 'amd64' or 'arm64'"
77+
exit 1
78+
fi
79+
# Prune docker images to avoid platform conflicts
80+
docker system prune -fa
81+
./build.sh "--${ARCH}" "${FLAVOR}" "${MCR_IMAGE_NAME}:localbuild"
82+
83+
for ((i = 0; i < ${#TAGS[@]}; i++)) do
84+
local TAG="${TAGS[$i]}"
85+
tag_and_push "${MCR_IMAGE_NAME}:localbuild" "playwright.azurecr.io/public/${MCR_IMAGE_NAME}:${TAG}-${ARCH}"
86+
done
87+
}
88+
89+
publish_docker_manifest () {
90+
local FLAVOR="$1"
91+
local TAGS=()
92+
if [[ "$FLAVOR" == "bionic" ]]; then
93+
TAGS=("${BIONIC_TAGS[@]}")
94+
elif [[ "$FLAVOR" == "focal" ]]; then
95+
TAGS=("${FOCAL_TAGS[@]}")
96+
else
97+
echo "ERROR: unknown flavor - $FLAVOR. Must be either 'bionic' or 'focal'"
98+
exit 1
99+
fi
100+
101+
for ((i = 0; i < ${#TAGS[@]}; i++)) do
102+
local TAG="${TAGS[$i]}"
103+
local BASE_IMAGE_TAG="playwright.azurecr.io/public/${MCR_IMAGE_NAME}:${TAG}"
104+
local IMAGE_NAMES=""
105+
if [[ "$2" == "arm64" || "$2" == "amd64" ]]; then
106+
IMAGE_NAMES="${IMAGE_NAMES} ${BASE_IMAGE_TAG}-$2"
107+
fi
108+
if [[ "$3" == "arm64" || "$3" == "amd64" ]]; then
109+
IMAGE_NAMES="${IMAGE_NAMES} ${BASE_IMAGE_TAG}-$3"
110+
fi
111+
docker manifest create "${BASE_IMAGE_TAG}" $IMAGE_NAMES
112+
docker manifest push "${BASE_IMAGE_TAG}"
113+
done
114+
}
115+
116+
publish_docker_images_with_arch_suffix focal amd64
117+
publish_docker_images_with_arch_suffix focal arm64
118+
publish_docker_manifest focal amd64 arm64

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy