Skip to content

Commit 90bcc6a

Browse files
authored
devops: move docker publishing to Azure DevOps (#1986)
1 parent f4eacb0 commit 90bcc6a

10 files changed

Lines changed: 254 additions & 90 deletions

File tree

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Trigger: any `v*` release tag (e.g. v1.40.0).
2+
# Can also be queued manually from the ADO UI.
3+
trigger:
4+
tags:
5+
include:
6+
- v*
7+
exclude:
8+
- v*-*
9+
10+
pr: none
11+
12+
parameters:
13+
- name: releaseChannel
14+
displayName: "IMPORTANT: set this to 'canary' when triggering manually"
15+
type: string
16+
default: stable
17+
values:
18+
- stable
19+
- canary
20+
21+
resources:
22+
repositories:
23+
- repository: 1esPipelines
24+
type: git
25+
name: 1ESPipelineTemplates/1ESPipelineTemplates
26+
ref: refs/tags/release
27+
28+
extends:
29+
template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines
30+
parameters:
31+
pool:
32+
name: DevDivPlaywrightAzurePipelinesUbuntu2204
33+
os: linux
34+
sdl:
35+
sourceAnalysisPool:
36+
name: DevDivPlaywrightAzurePipelinesWindows2022
37+
# The image must be windows-based due to restrictions of the SDL tools. See: https://aka.ms/AAo6v8e
38+
os: windows
39+
suppression:
40+
suppressionFile: $(Build.SourcesDirectory)\.azure-pipelines\guardian\SDL\.gdnsuppress
41+
stages:
42+
- stage: Publish
43+
jobs:
44+
- job: PublishDocker
45+
displayName: "Publish Docker images to ACR"
46+
# arm64 images are cross-built under QEMU emulation, which is slow.
47+
timeoutInMinutes: 360
48+
steps:
49+
- checkout: self
50+
displayName: "Checkout code"
51+
52+
- task: UseNode@1
53+
inputs:
54+
version: '26.x'
55+
displayName: "Install Node.js"
56+
57+
# Relocate the Docker data-root to the large /mnt volume: this job builds
58+
# 6 images (jammy/noble/resolute x amd64/arm64) and `docker system prune`s
59+
# between them, so the default disk fills up.
60+
- task: Bash@3
61+
displayName: "Setup docker"
62+
inputs:
63+
targetType: "inline"
64+
script: |
65+
set -x
66+
sudo service docker stop
67+
sudo mkdir -p /etc/docker
68+
echo '{ "data-root": "/mnt/docker" }' | sudo tee /etc/docker/daemon.json
69+
sudo service docker start
70+
71+
# Used by scripts/download_driver.sh on the host. The repository root is the
72+
# Docker build context, so .dockerignore keeps this file out of the images.
73+
- task: Bash@3
74+
displayName: "setup .npmrc"
75+
inputs:
76+
targetType: "inline"
77+
script: |
78+
echo "registry=https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/DevDiv_PublicPackages/npm/registry/" >> .npmrc
79+
80+
- task: npmAuthenticate@0
81+
displayName: "authenticate the private npm registry"
82+
inputs:
83+
workingFile: .npmrc
84+
85+
# Must run before MavenAuthenticate@0 so the credentials it injects into
86+
# ~/.m2/settings.xml are not overwritten by the copy.
87+
- bash: |
88+
mkdir -p ~/.m2
89+
cp .azure-pipelines/settings.xml ~/.m2/settings.xml
90+
displayName: 'Copy settings.xml (mirror Maven Central to DevDiv_PublicPackages)'
91+
92+
- task: MavenAuthenticate@0
93+
displayName: 'Authenticate to DevDiv_PublicPackages feed'
94+
inputs:
95+
artifactsFeeds: DevDiv_PublicPackages
96+
97+
- task: AzureCLI@2
98+
displayName: "Login to ACR via OIDC"
99+
inputs:
100+
azureSubscription: "Playwright-CDN"
101+
scriptType: "bash"
102+
scriptLocation: "inlineScript"
103+
inlineScript: "az acr login --name playwright"
104+
105+
- task: Bash@3
106+
displayName: "Register QEMU (binfmt) for arm64 cross-build"
107+
inputs:
108+
targetType: "inline"
109+
script: "docker run --rm --privileged ${ACR_CACHE_PREFIX}tonistiigi/binfmt --install arm64"
110+
env:
111+
ACR_CACHE_PREFIX: "playwright.azurecr.io/cached/"
112+
113+
- task: Bash@3
114+
displayName: "Build & publish Docker images"
115+
inputs:
116+
targetType: "inline"
117+
# The authenticated settings.xml is passed to `docker build` as a secret so
118+
# that Maven inside the image also resolves from DevDiv_PublicPackages.
119+
script: MAVEN_SETTINGS_SECRET="$HOME/.m2/settings.xml" ./utils/docker/publish_docker.sh ${{ parameters.releaseChannel }}
120+
env:
121+
ACR_CACHE_PREFIX: "playwright.azurecr.io/cached/"
122+
UBUNTU_MIRROR_PREFIX: "azure."

‎.azure-pipelines/settings.xml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
3-
Maven settings used only by the Azure Pipelines release build (publish.yml).
3+
Maven settings used only by the Azure Pipelines release builds (publish.yml and
4+
publish-docker.yml).
45
56
Routes all remote artifact and plugin resolution through the DevDiv_PublicPackages
67
Azure Artifacts feed instead of Maven Central, as required by SFI-ES4.2.4.

‎.claude/skills/playwright-java-release/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,4 @@ gh pr create --repo microsoft/playwright-java --head <user>:mark-v-1.X.0 --base
7373

7474
## 4. Publish
7575

76-
The user publishes the draft release manually once the `mark-v-1.X.0` PR is merged. After publishing, CI pushes the artifacts to Maven Central and runs the Docker workflow automatically: https://github.com/microsoft/playwright-java/actions.
76+
The user publishes the draft release manually once the `mark-v-1.X.0` PR is merged. After publishing, the `v1.X.0` tag triggers the Azure DevOps pipelines that push the artifacts to Maven Central (`.azure-pipelines/publish.yml`) and the Docker images to ACR (`.azure-pipelines/publish-docker.yml`).

‎.dockerignore‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# The repository root is the Docker build context (see utils/docker/build.sh).
2+
# Keep registry credentials written by the release pipeline out of the images.
3+
**/.npmrc
4+
# Downloaded by publish_docker.sh after the first push.
5+
utils/docker/oras

‎.github/workflows/publish_docker.yml‎

Lines changed: 0 additions & 30 deletions
This file was deleted.

‎utils/docker/Dockerfile.jammy‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
FROM ubuntu:jammy
1+
ARG ACR_CACHE_PREFIX
2+
FROM ${ACR_CACHE_PREFIX}ubuntu:jammy
23

34
ARG DEBIAN_FRONTEND=noninteractive
45
ARG TZ=America/Los_Angeles
56
ARG DOCKER_IMAGE_NAME_TEMPLATE="mcr.microsoft.com/playwright/java:v%version%-jammy"
7+
ARG UBUNTU_MIRROR_PREFIX
68

79
ENV LANG=C.UTF-8
810
ENV LC_ALL=C.UTF-8
911

1012
# === INSTALL JDK and Maven ===
1113

12-
RUN apt-get update && \
14+
RUN find /etc/apt -type f \( -name '*.list' -o -name '*.sources' \) \
15+
-exec sed -i "s|http://archive.ubuntu.com|http://${UBUNTU_MIRROR_PREFIX}archive.ubuntu.com|g; s|http://ports.ubuntu.com|http://${UBUNTU_MIRROR_PREFIX}ports.ubuntu.com|g" {} + && \
16+
apt-get update && \
1317
apt-get install -y --no-install-recommends openjdk-25-jdk \
1418
# Install utilities required for downloading browsers
1519
wget \
@@ -47,7 +51,10 @@ RUN mkdir /ms-playwright && \
4751

4852
COPY . /tmp/pw-java
4953

50-
RUN cd /tmp/pw-java && \
54+
# The optional Maven settings secret routes dependency resolution through a mirror
55+
# (see .azure-pipelines/publish-docker.yml).
56+
RUN --mount=type=secret,id=mavensettings,target=/root/.m2/settings.xml,required=false \
57+
cd /tmp/pw-java && \
5158
mvn install -D skipTests --no-transfer-progress && \
5259
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI \
5360
-D exec.args="install-driver" -f playwright/pom.xml --no-transfer-progress && \

‎utils/docker/Dockerfile.noble‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
FROM ubuntu:noble
1+
ARG ACR_CACHE_PREFIX
2+
FROM ${ACR_CACHE_PREFIX}ubuntu:noble
23

34
ARG DEBIAN_FRONTEND=noninteractive
45
ARG TZ=America/Los_Angeles
56
ARG DOCKER_IMAGE_NAME_TEMPLATE="mcr.microsoft.com/playwright/java:v%version%-noble"
7+
ARG UBUNTU_MIRROR_PREFIX
68

79
ENV LANG=C.UTF-8
810
ENV LC_ALL=C.UTF-8
911

1012
# === INSTALL JDK and Maven ===
1113

12-
RUN apt-get update && \
14+
RUN find /etc/apt -type f \( -name '*.list' -o -name '*.sources' \) \
15+
-exec sed -i "s|http://archive.ubuntu.com|http://${UBUNTU_MIRROR_PREFIX}archive.ubuntu.com|g; s|http://ports.ubuntu.com|http://${UBUNTU_MIRROR_PREFIX}ports.ubuntu.com|g" {} + && \
16+
apt-get update && \
1317
apt-get install -y --no-install-recommends openjdk-25-jdk \
1418
# Install utilities required for downloading browsers
1519
wget \
@@ -47,7 +51,10 @@ RUN mkdir /ms-playwright && \
4751

4852
COPY . /tmp/pw-java
4953

50-
RUN cd /tmp/pw-java && \
54+
# The optional Maven settings secret routes dependency resolution through a mirror
55+
# (see .azure-pipelines/publish-docker.yml).
56+
RUN --mount=type=secret,id=mavensettings,target=/root/.m2/settings.xml,required=false \
57+
cd /tmp/pw-java && \
5158
mvn install -D skipTests --no-transfer-progress && \
5259
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI \
5360
-D exec.args="install-driver" -f playwright/pom.xml --no-transfer-progress && \

‎utils/docker/Dockerfile.resolute‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
FROM ubuntu:resolute
1+
ARG ACR_CACHE_PREFIX
2+
FROM ${ACR_CACHE_PREFIX}ubuntu:resolute
23

34
ARG DEBIAN_FRONTEND=noninteractive
45
ARG TZ=America/Los_Angeles
56
ARG DOCKER_IMAGE_NAME_TEMPLATE="mcr.microsoft.com/playwright/java:v%version%-resolute"
7+
ARG UBUNTU_MIRROR_PREFIX
68

79
ENV LANG=C.UTF-8
810
ENV LC_ALL=C.UTF-8
911

1012
# === INSTALL JDK and Maven ===
1113

12-
RUN apt-get update && \
14+
RUN find /etc/apt -type f \( -name '*.list' -o -name '*.sources' \) \
15+
-exec sed -i "s|http://archive.ubuntu.com|http://${UBUNTU_MIRROR_PREFIX}archive.ubuntu.com|g; s|http://ports.ubuntu.com|http://${UBUNTU_MIRROR_PREFIX}ports.ubuntu.com|g" {} + && \
16+
apt-get update && \
1317
apt-get install -y --no-install-recommends openjdk-25-jdk \
1418
# Install utilities required for downloading browsers
1519
wget \
@@ -47,7 +51,10 @@ RUN mkdir /ms-playwright && \
4751

4852
COPY . /tmp/pw-java
4953

50-
RUN cd /tmp/pw-java && \
54+
# The optional Maven settings secret routes dependency resolution through a mirror
55+
# (see .azure-pipelines/publish-docker.yml).
56+
RUN --mount=type=secret,id=mavensettings,target=/root/.m2/settings.xml,required=false \
57+
cd /tmp/pw-java && \
5158
mvn install -D skipTests --no-transfer-progress && \
5259
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI \
5360
-D exec.args="install-driver" -f playwright/pom.xml --no-transfer-progress && \

‎utils/docker/build.sh‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,36 @@ PW_TARGET_ARCH=$(echo $1 | cut -c3-)
3838
# it up via `COPY . /tmp/pw-java`.
3939
../../scripts/download_driver.sh
4040

41-
docker build --platform "${PLATFORM}" --build-arg "PW_TARGET_ARCH=${PW_TARGET_ARCH}" -t "$3" -f "Dockerfile.$2" ../../
41+
SECRET_ARGS=()
42+
if [[ -n "${MAVEN_SETTINGS_SECRET:-}" ]]; then
43+
SECRET_ARGS+=(--secret "id=mavensettings,src=${MAVEN_SETTINGS_SECRET}")
44+
fi
45+
46+
# Keep each arch image a plain single-platform manifest without the unknown/unknown platform entry.
47+
export BUILDX_NO_DEFAULT_ATTESTATIONS=1
48+
49+
# arm64 images are cross-built under QEMU user-mode emulation, where Ubuntu 22.04's
50+
# ldconfig segfaults intermittently at startup (tonistiigi/binfmt#298, every binfmt
51+
# build since QEMU 8.1.4). apt's libc-bin trigger runs ldconfig, so a crash fails the
52+
# whole `docker build`. Retry: BuildKit keeps the layers that already succeeded, so a
53+
# retry re-runs only the failed RUN step.
54+
MAX_ATTEMPTS=1
55+
if [[ "${PLATFORM}" == "linux/arm64" ]]; then
56+
MAX_ATTEMPTS=3
57+
fi
58+
59+
for ((attempt = 1; attempt <= MAX_ATTEMPTS; attempt++)); do
60+
if docker build --platform "${PLATFORM}" \
61+
--build-arg "PW_TARGET_ARCH=${PW_TARGET_ARCH}" \
62+
--build-arg ACR_CACHE_PREFIX="${ACR_CACHE_PREFIX}" \
63+
--build-arg UBUNTU_MIRROR_PREFIX="${UBUNTU_MIRROR_PREFIX}" \
64+
"${SECRET_ARGS[@]}" \
65+
-t "$3" -f "Dockerfile.$2" ../../; then
66+
exit 0
67+
fi
68+
if (( attempt < MAX_ATTEMPTS )); then
69+
echo "docker build failed (attempt ${attempt}/${MAX_ATTEMPTS}), retrying..." >&2
70+
fi
71+
done
72+
echo "ERROR: docker build failed after ${MAX_ATTEMPTS} attempt(s)" >&2
73+
exit 1

0 commit comments

Comments
 (0)