From d5055f31bbf701649ebd92da3dccb69df8828537 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 10:03:02 +0200 Subject: [PATCH 01/11] chore(ci): Use Playwright Docker images instead of install-playwright action Replace the custom `install-playwright` composite action with official Playwright Docker images for browser integration tests, and inline `npx playwright install` for other jobs. **Browser integration tests** (playwright + loader): - Use `mcr.microsoft.com/playwright:v1.56.1-noble` container image - Browsers are pre-installed, no download or caching needed **Remix, E2E, canary, flaky-test-detector**: - Replace composite action with `npx playwright install --with-deps chromium` - These jobs have complex setups (Node version matrix, pnpm, Verdaccio) where a container adds unnecessary complexity This also removes the `actions/cache@v4` usage from the composite action, eliminating another source of Node.js 20 deprecation warnings. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/actions/install-playwright/action.yml | 52 ------------------- .github/workflows/build.yml | 34 +++++------- .github/workflows/canary.yml | 6 +-- .github/workflows/flaky-test-detector.yml | 4 +- 4 files changed, 16 insertions(+), 80 deletions(-) delete mode 100644 .github/actions/install-playwright/action.yml diff --git a/.github/actions/install-playwright/action.yml b/.github/actions/install-playwright/action.yml deleted file mode 100644 index 8ca47ce04081..000000000000 --- a/.github/actions/install-playwright/action.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: 'Install Playwright dependencies' -description: 'Installs Playwright dependencies and caches them.' -inputs: - browsers: - description: 'What browsers to install.' - default: 'chromium webkit firefox' - cwd: - description: 'The working directory to run Playwright in.' - default: '.' - -runs: - using: 'composite' - steps: - - name: Get Playwright version - id: playwright-version - run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> $GITHUB_OUTPUT - shell: bash - working-directory: ${{ inputs.cwd }} - - - name: Restore cached playwright binaries - uses: actions/cache/restore@v4 - id: playwright-cache - with: - path: | - ~/.cache/ms-playwright - # Bump the iteration when bumping runner images to use a new cache - key: playwright-${{ runner.os }}-iteration-1-${{ steps.playwright-version.outputs.version }} - - # We always install all browsers, if uncached - - name: Install Playwright dependencies (uncached) - run: npx playwright install chromium webkit firefox --with-deps - if: steps.playwright-cache.outputs.cache-hit != 'true' - shell: bash - working-directory: ${{ inputs.cwd }} - - - name: Install Playwright system dependencies only (cached) - env: - PLAYWRIGHT_BROWSERS: ${{ inputs.browsers || 'chromium webkit firefox' }} - run: npx playwright install-deps "$PLAYWRIGHT_BROWSERS" - if: steps.playwright-cache.outputs.cache-hit == 'true' - shell: bash - working-directory: ${{ inputs.cwd }} - - # Only store cache on develop branch - - name: Store cached playwright binaries - uses: actions/cache/save@v4 - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' - with: - path: | - ~/.cache/ms-playwright - # Bump the iteration when bumping runner images to use a new cache - key: playwright-${{ runner.os }}-iteration-1-${{ steps.playwright-version.outputs.version }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6032cee30fdc..de24dde10cb7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -478,6 +478,8 @@ jobs: needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-24.04-large-js + container: + image: mcr.microsoft.com/playwright:v1.56.1-noble timeout-minutes: 25 strategy: fail-fast: false @@ -535,16 +537,13 @@ jobs: uses: actions/setup-node@v6 with: node-version-file: 'package.json' + - name: Install yarn + run: npm install -g yarn@1.22.22 - name: Restore caches uses: ./.github/actions/restore-cache with: dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Install Playwright - uses: ./.github/actions/install-playwright - with: - browsers: ${{ matrix.project }} - - name: Run Playwright tests env: PW_BUNDLE: ${{ matrix.bundle }} @@ -581,6 +580,8 @@ jobs: needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-24.04 + container: + image: mcr.microsoft.com/playwright:v1.56.1-noble timeout-minutes: 15 strategy: fail-fast: false @@ -603,16 +604,13 @@ jobs: uses: actions/setup-node@v6 with: node-version-file: 'package.json' + - name: Install yarn + run: npm install -g yarn@1.22.22 - name: Restore caches uses: ./.github/actions/restore-cache with: dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Install Playwright - uses: ./.github/actions/install-playwright - with: - browsers: chromium - - name: Run Playwright Loader tests env: PW_BUNDLE: ${{ matrix.bundle }} @@ -794,9 +792,7 @@ jobs: dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Install Playwright - uses: ./.github/actions/install-playwright - with: - browsers: chromium + run: npx playwright install --with-deps chromium - name: Run integration tests env: @@ -957,10 +953,8 @@ jobs: SENTRY_E2E_WORKSPACE_ROOT: ${{ github.workspace }} - name: Install Playwright - uses: ./.github/actions/install-playwright - with: - browsers: chromium - cwd: ${{ runner.temp }}/test-application + run: npx playwright install --with-deps chromium + working-directory: ${{ runner.temp }}/test-application - name: Run E2E test working-directory: ${{ runner.temp }}/test-application @@ -1072,10 +1066,8 @@ jobs: run: ${{ matrix.build-command || 'pnpm test:build' }} - name: Install Playwright - uses: ./.github/actions/install-playwright - with: - browsers: chromium - cwd: ${{ runner.temp }}/test-application + run: npx playwright install --with-deps chromium + working-directory: ${{ runner.temp }}/test-application - name: Run E2E test working-directory: ${{ runner.temp }}/test-application diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index f9c74b5f344f..4f942e06d3d2 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -165,10 +165,8 @@ jobs: run: yarn ${{ matrix.build-command }} - name: Install Playwright - uses: ./.github/actions/install-playwright - with: - browsers: chromium - cwd: ${{ runner.temp }}/test-application + run: npx playwright install --with-deps chromium + working-directory: ${{ runner.temp }}/test-application - name: Run E2E test working-directory: ${{ runner.temp }}/test-application diff --git a/.github/workflows/flaky-test-detector.yml b/.github/workflows/flaky-test-detector.yml index c0a8f1f720b1..be4bd06de113 100644 --- a/.github/workflows/flaky-test-detector.yml +++ b/.github/workflows/flaky-test-detector.yml @@ -50,9 +50,7 @@ jobs: run: yarn build - name: Install Playwright - uses: ./.github/actions/install-playwright - with: - browsers: 'chromium' + run: npx playwright install --with-deps chromium - name: Determine changed tests uses: dorny/paths-filter@v4.0.1 From d873fcecbe97547f8267fe3e14a851791cec1957 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 10:20:30 +0200 Subject: [PATCH 02/11] chore(ci): Use custom GHCR Playwright image for all Playwright jobs Build a custom Docker image extending the official Playwright image with yarn pre-installed, push it to GHCR, and use it across all CI jobs that need Playwright browsers. - Add `.github/docker/playwright.Dockerfile` with yarn@1.22.22 - Add build workflow that pushes to GHCR on Dockerfile or Playwright version changes - All Playwright jobs (browser tests, loader tests, Remix, E2E, canary, flaky-test-detector) now use the GHCR container image - No more `npx playwright install` or browser caching logic anywhere When bumping @playwright/test, also update the PLAYWRIGHT_IMAGE env var in build.yml, canary.yml, and flaky-test-detector.yml. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/docker/playwright.Dockerfile | 5 +++ .../build-playwright-docker-image.yml | 37 +++++++++++++++++ .github/workflows/build.yml | 41 +++++++++++-------- .github/workflows/canary.yml | 10 +++-- .github/workflows/flaky-test-detector.yml | 9 ++-- 5 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 .github/docker/playwright.Dockerfile create mode 100644 .github/workflows/build-playwright-docker-image.yml diff --git a/.github/docker/playwright.Dockerfile b/.github/docker/playwright.Dockerfile new file mode 100644 index 000000000000..8c9d2b4ba5ea --- /dev/null +++ b/.github/docker/playwright.Dockerfile @@ -0,0 +1,5 @@ +ARG PLAYWRIGHT_VERSION=1.56.1 +FROM mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-noble + +# Install yarn (v1) for the monorepo +RUN npm install -g yarn@1.22.22 diff --git a/.github/workflows/build-playwright-docker-image.yml b/.github/workflows/build-playwright-docker-image.yml new file mode 100644 index 000000000000..7177f8e122be --- /dev/null +++ b/.github/workflows/build-playwright-docker-image.yml @@ -0,0 +1,37 @@ +name: Build Playwright Docker Image +on: + push: + branches: [develop] + paths: + - '.github/docker/playwright.Dockerfile' + - 'dev-packages/browser-integration-tests/package.json' + workflow_dispatch: + +jobs: + build-image: + runs-on: ubuntu-24.04 + permissions: + packages: write + steps: + - uses: actions/checkout@v6 + + - name: Get Playwright version + id: playwright + run: echo "version=$(node -p "require('./dev-packages/browser-integration-tests/package.json').devDependencies['@playwright/test'].replace('~', '')")" >> $GITHUB_OUTPUT + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + file: .github/docker/playwright.Dockerfile + push: true + build-args: | + PLAYWRIGHT_VERSION=${{ steps.playwright.outputs.version }} + tags: | + ghcr.io/${{ github.repository }}/playwright:v${{ steps.playwright.outputs.version }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de24dde10cb7..f24b5886ec2d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,7 @@ concurrency: env: HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} + PLAYWRIGHT_IMAGE: ghcr.io/getsentry/sentry-javascript/playwright:v1.56.1 # WARNING: this disables cross os caching as ~ and # github.workspace evaluate to differents paths @@ -479,7 +480,10 @@ jobs: if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-24.04-large-js container: - image: mcr.microsoft.com/playwright:v1.56.1-noble + image: ${{ env.PLAYWRIGHT_IMAGE }} + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 25 strategy: fail-fast: false @@ -537,8 +541,6 @@ jobs: uses: actions/setup-node@v6 with: node-version-file: 'package.json' - - name: Install yarn - run: npm install -g yarn@1.22.22 - name: Restore caches uses: ./.github/actions/restore-cache with: @@ -581,7 +583,10 @@ jobs: if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-24.04 container: - image: mcr.microsoft.com/playwright:v1.56.1-noble + image: ${{ env.PLAYWRIGHT_IMAGE }} + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 15 strategy: fail-fast: false @@ -604,8 +609,6 @@ jobs: uses: actions/setup-node@v6 with: node-version-file: 'package.json' - - name: Install yarn - run: npm install -g yarn@1.22.22 - name: Restore caches uses: ./.github/actions/restore-cache with: @@ -772,6 +775,11 @@ jobs: needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_remix == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-24.04 + container: + image: ${{ env.PLAYWRIGHT_IMAGE }} + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 15 strategy: fail-fast: false @@ -791,9 +799,6 @@ jobs: with: dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - - name: Install Playwright - run: npx playwright install --with-deps chromium - - name: Run integration tests env: NODE_VERSION: ${{ matrix.node }} @@ -871,6 +876,11 @@ jobs: always() && needs.job_e2e_prepare.result == 'success' && needs.job_e2e_prepare.outputs.matrix != '{"include":[]}' needs: [job_get_metadata, job_build, job_e2e_prepare] runs-on: ubuntu-24.04 + container: + image: ${{ env.PLAYWRIGHT_IMAGE }} + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 15 env: # We just use a dummy DSN here, only send to the tunnel anyhow @@ -952,10 +962,6 @@ jobs: env: SENTRY_E2E_WORKSPACE_ROOT: ${{ github.workspace }} - - name: Install Playwright - run: npx playwright install --with-deps chromium - working-directory: ${{ runner.temp }}/test-application - - name: Run E2E test working-directory: ${{ runner.temp }}/test-application timeout-minutes: 10 @@ -999,6 +1005,11 @@ jobs: github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' needs: [job_get_metadata, job_build, job_e2e_prepare] runs-on: ubuntu-24.04 + container: + image: ${{ env.PLAYWRIGHT_IMAGE }} + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 15 env: E2E_TEST_AUTH_TOKEN: ${{ secrets.E2E_TEST_AUTH_TOKEN }} @@ -1065,10 +1076,6 @@ jobs: timeout-minutes: 7 run: ${{ matrix.build-command || 'pnpm test:build' }} - - name: Install Playwright - run: npx playwright install --with-deps chromium - working-directory: ${{ runner.temp }}/test-application - - name: Run E2E test working-directory: ${{ runner.temp }}/test-application timeout-minutes: 10 diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index 4f942e06d3d2..e27d33429814 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -11,6 +11,7 @@ on: env: HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} + PLAYWRIGHT_IMAGE: ghcr.io/getsentry/sentry-javascript/playwright:v1.56.1 CACHED_BUILD_PATHS: | ${{ github.workspace }}/packages/*/*.tgz @@ -55,6 +56,11 @@ jobs: name: E2E ${{ matrix.label }} Test needs: [job_e2e_prepare] runs-on: ubuntu-24.04 + container: + image: ${{ env.PLAYWRIGHT_IMAGE }} + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 20 env: # We just use a dummy DSN here, only send to the tunnel anyhow @@ -164,10 +170,6 @@ jobs: timeout-minutes: 7 run: yarn ${{ matrix.build-command }} - - name: Install Playwright - run: npx playwright install --with-deps chromium - working-directory: ${{ runner.temp }}/test-application - - name: Run E2E test working-directory: ${{ runner.temp }}/test-application timeout-minutes: 15 diff --git a/.github/workflows/flaky-test-detector.yml b/.github/workflows/flaky-test-detector.yml index be4bd06de113..2c8ee4485006 100644 --- a/.github/workflows/flaky-test-detector.yml +++ b/.github/workflows/flaky-test-detector.yml @@ -9,6 +9,7 @@ on: env: HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} + PLAYWRIGHT_IMAGE: ghcr.io/getsentry/sentry-javascript/playwright:v1.56.1 NX_CACHE_RESTORE_KEYS: | nx-Linux-${{ github.ref }}-${{ github.event.inputs.commit || github.sha }} @@ -24,6 +25,11 @@ concurrency: jobs: flaky-detector: runs-on: ubuntu-24.04 + container: + image: ${{ env.PLAYWRIGHT_IMAGE }} + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} timeout-minutes: 60 name: 'Check tests for flakiness' # Also skip if PR is from master -> develop @@ -49,9 +55,6 @@ jobs: - name: Build packages run: yarn build - - name: Install Playwright - run: npx playwright install --with-deps chromium - - name: Determine changed tests uses: dorny/paths-filter@v4.0.1 id: changed From d341175090d4276ba513e20ad39ca48d82571fc7 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 10:23:09 +0200 Subject: [PATCH 03/11] chore(ci): Run Playwright image build on PRs, verify version consistency - Trigger the build workflow on PRs that change the Dockerfile, Playwright version, or any e2e test application package.json - Add a version consistency check that verifies all packages and e2e test applications use the same @playwright/test version as the canonical source (dev-packages/browser-integration-tests) - On PRs, the image is built (to verify the Dockerfile) but not pushed - On push to develop and workflow_dispatch, the image is built and pushed Co-Authored-By: Claude Opus 4.6 (1M context) --- .../build-playwright-docker-image.yml | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-playwright-docker-image.yml b/.github/workflows/build-playwright-docker-image.yml index 7177f8e122be..993301c8a9e4 100644 --- a/.github/workflows/build-playwright-docker-image.yml +++ b/.github/workflows/build-playwright-docker-image.yml @@ -5,10 +5,70 @@ on: paths: - '.github/docker/playwright.Dockerfile' - 'dev-packages/browser-integration-tests/package.json' + pull_request: + paths: + - '.github/docker/playwright.Dockerfile' + - 'dev-packages/browser-integration-tests/package.json' + - 'dev-packages/e2e-tests/test-applications/*/package.json' + - 'dev-packages/test-utils/package.json' workflow_dispatch: jobs: + verify-playwright-versions: + name: Verify Playwright version consistency + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + + - name: Check all @playwright/test versions match + run: | + # Get the canonical version from browser-integration-tests + CANONICAL=$(node -p "require('./dev-packages/browser-integration-tests/package.json').devDependencies['@playwright/test']") + echo "Canonical @playwright/test version: $CANONICAL" + + MISMATCHES=0 + + # Check test-utils + for pkg in dev-packages/test-utils/package.json; do + VERSION=$(node -p " + const pkg = require('./$pkg'); + (pkg.dependencies || {})['@playwright/test'] || + (pkg.devDependencies || {})['@playwright/test'] || + (pkg.peerDependencies || {})['@playwright/test'] || + 'not found' + ") + if [ "$VERSION" != "not found" ] && [ "$VERSION" != "$CANONICAL" ]; then + echo "::error file=$pkg::@playwright/test version mismatch: $VERSION (expected $CANONICAL)" + MISMATCHES=$((MISMATCHES + 1)) + fi + done + + # Check all e2e test applications + for pkg in dev-packages/e2e-tests/test-applications/*/package.json; do + VERSION=$(node -p " + const pkg = require('./$pkg'); + (pkg.dependencies || {})['@playwright/test'] || + (pkg.devDependencies || {})['@playwright/test'] || + 'not found' + ") + if [ "$VERSION" != "not found" ] && [ "$VERSION" != "$CANONICAL" ]; then + echo "::error file=$pkg::@playwright/test version mismatch: $VERSION (expected $CANONICAL)" + MISMATCHES=$((MISMATCHES + 1)) + fi + done + + if [ "$MISMATCHES" -gt 0 ]; then + echo "" + echo "Found $MISMATCHES package(s) with mismatched @playwright/test versions." + echo "All packages must use the same version as dev-packages/browser-integration-tests ($CANONICAL)." + exit 1 + fi + + echo "All @playwright/test versions are consistent ($CANONICAL)" + build-image: + name: Build Playwright Docker Image + needs: [verify-playwright-versions] runs-on: ubuntu-24.04 permissions: packages: write @@ -30,7 +90,7 @@ jobs: uses: docker/build-push-action@v6 with: file: .github/docker/playwright.Dockerfile - push: true + push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} build-args: | PLAYWRIGHT_VERSION=${{ steps.playwright.outputs.version }} tags: | From ea7b1c2a52b2525e5f6861fe259598b17cd8d619 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 10:26:40 +0200 Subject: [PATCH 04/11] chore(ci): Extract ensure-playwright-image composite action, remove standalone workflow Replace the standalone build-playwright-docker-image workflow with a composite action that runs as a prerequisite job in each workflow: - Verifies all @playwright/test versions are consistent across the repo - Checks if the GHCR image already exists (noop if so) - Builds and pushes only when the image is missing Each workflow (build, canary, flaky-test-detector) now has a small `job_playwright_image` prerequisite job that runs the composite action and outputs the image reference for downstream container jobs. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ensure-playwright-image/action.yml | 98 +++++++++++++++++++ .../build-playwright-docker-image.yml | 97 ------------------ .github/workflows/build.yml | 46 ++++++--- .github/workflows/canary.yml | 26 ++++- .github/workflows/flaky-test-detector.yml | 25 ++++- 5 files changed, 179 insertions(+), 113 deletions(-) create mode 100644 .github/actions/ensure-playwright-image/action.yml delete mode 100644 .github/workflows/build-playwright-docker-image.yml diff --git a/.github/actions/ensure-playwright-image/action.yml b/.github/actions/ensure-playwright-image/action.yml new file mode 100644 index 000000000000..9c7531ba8259 --- /dev/null +++ b/.github/actions/ensure-playwright-image/action.yml @@ -0,0 +1,98 @@ +name: 'Ensure Playwright Docker Image' +description: 'Checks if the custom Playwright Docker image exists in GHCR. Builds and pushes it if missing.' + +inputs: + github_token: + description: 'GitHub token with packages:write permission' + required: true + +outputs: + image: + description: 'The full Docker image reference (e.g. ghcr.io/owner/repo/playwright:v1.56.0)' + value: ${{ steps.image.outputs.ref }} + +runs: + using: 'composite' + steps: + - name: Get Playwright version + id: playwright + run: echo "version=$(node -p "require('./dev-packages/browser-integration-tests/package.json').devDependencies['@playwright/test'].replace('~', '')")" >> $GITHUB_OUTPUT + shell: bash + + - name: Verify @playwright/test version consistency + shell: bash + run: | + CANONICAL=$(node -p "require('./dev-packages/browser-integration-tests/package.json').devDependencies['@playwright/test']") + MISMATCHES=0 + + for pkg in dev-packages/test-utils/package.json; do + VERSION=$(node -p " + const pkg = require('./$pkg'); + (pkg.dependencies || {})['@playwright/test'] || + (pkg.devDependencies || {})['@playwright/test'] || + (pkg.peerDependencies || {})['@playwright/test'] || + 'not found' + ") + if [ "$VERSION" != "not found" ] && [ "$VERSION" != "$CANONICAL" ]; then + echo "::error file=$pkg::@playwright/test version mismatch: $VERSION (expected $CANONICAL)" + MISMATCHES=$((MISMATCHES + 1)) + fi + done + + for pkg in dev-packages/e2e-tests/test-applications/*/package.json; do + VERSION=$(node -p " + const pkg = require('./$pkg'); + (pkg.dependencies || {})['@playwright/test'] || + (pkg.devDependencies || {})['@playwright/test'] || + 'not found' + ") + if [ "$VERSION" != "not found" ] && [ "$VERSION" != "$CANONICAL" ]; then + echo "::error file=$pkg::@playwright/test version mismatch: $VERSION (expected $CANONICAL)" + MISMATCHES=$((MISMATCHES + 1)) + fi + done + + if [ "$MISMATCHES" -gt 0 ]; then + echo "Found $MISMATCHES package(s) with mismatched @playwright/test versions." + echo "All packages must use the same version as dev-packages/browser-integration-tests ($CANONICAL)." + exit 1 + fi + echo "All @playwright/test versions are consistent ($CANONICAL)" + + - name: Set image reference + id: image + run: echo "ref=ghcr.io/${{ github.repository }}/playwright:v${{ steps.playwright.outputs.version }}" >> $GITHUB_OUTPUT + shell: bash + + - name: Check if image already exists + id: check + shell: bash + run: | + if docker manifest inspect "${{ steps.image.outputs.ref }}" > /dev/null 2>&1; then + echo "exists=true" >> $GITHUB_OUTPUT + echo "Image ${{ steps.image.outputs.ref }} already exists, skipping build." + else + echo "exists=false" >> $GITHUB_OUTPUT + echo "Image ${{ steps.image.outputs.ref }} not found, will build." + fi + env: + DOCKER_CLI_EXPERIMENTAL: enabled + + - name: Log in to GHCR + if: steps.check.outputs.exists == 'false' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ inputs.github_token }} + + - name: Build and push + if: steps.check.outputs.exists == 'false' + uses: docker/build-push-action@v6 + with: + file: .github/docker/playwright.Dockerfile + push: true + build-args: | + PLAYWRIGHT_VERSION=${{ steps.playwright.outputs.version }} + tags: | + ${{ steps.image.outputs.ref }} diff --git a/.github/workflows/build-playwright-docker-image.yml b/.github/workflows/build-playwright-docker-image.yml deleted file mode 100644 index 993301c8a9e4..000000000000 --- a/.github/workflows/build-playwright-docker-image.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: Build Playwright Docker Image -on: - push: - branches: [develop] - paths: - - '.github/docker/playwright.Dockerfile' - - 'dev-packages/browser-integration-tests/package.json' - pull_request: - paths: - - '.github/docker/playwright.Dockerfile' - - 'dev-packages/browser-integration-tests/package.json' - - 'dev-packages/e2e-tests/test-applications/*/package.json' - - 'dev-packages/test-utils/package.json' - workflow_dispatch: - -jobs: - verify-playwright-versions: - name: Verify Playwright version consistency - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - - name: Check all @playwright/test versions match - run: | - # Get the canonical version from browser-integration-tests - CANONICAL=$(node -p "require('./dev-packages/browser-integration-tests/package.json').devDependencies['@playwright/test']") - echo "Canonical @playwright/test version: $CANONICAL" - - MISMATCHES=0 - - # Check test-utils - for pkg in dev-packages/test-utils/package.json; do - VERSION=$(node -p " - const pkg = require('./$pkg'); - (pkg.dependencies || {})['@playwright/test'] || - (pkg.devDependencies || {})['@playwright/test'] || - (pkg.peerDependencies || {})['@playwright/test'] || - 'not found' - ") - if [ "$VERSION" != "not found" ] && [ "$VERSION" != "$CANONICAL" ]; then - echo "::error file=$pkg::@playwright/test version mismatch: $VERSION (expected $CANONICAL)" - MISMATCHES=$((MISMATCHES + 1)) - fi - done - - # Check all e2e test applications - for pkg in dev-packages/e2e-tests/test-applications/*/package.json; do - VERSION=$(node -p " - const pkg = require('./$pkg'); - (pkg.dependencies || {})['@playwright/test'] || - (pkg.devDependencies || {})['@playwright/test'] || - 'not found' - ") - if [ "$VERSION" != "not found" ] && [ "$VERSION" != "$CANONICAL" ]; then - echo "::error file=$pkg::@playwright/test version mismatch: $VERSION (expected $CANONICAL)" - MISMATCHES=$((MISMATCHES + 1)) - fi - done - - if [ "$MISMATCHES" -gt 0 ]; then - echo "" - echo "Found $MISMATCHES package(s) with mismatched @playwright/test versions." - echo "All packages must use the same version as dev-packages/browser-integration-tests ($CANONICAL)." - exit 1 - fi - - echo "All @playwright/test versions are consistent ($CANONICAL)" - - build-image: - name: Build Playwright Docker Image - needs: [verify-playwright-versions] - runs-on: ubuntu-24.04 - permissions: - packages: write - steps: - - uses: actions/checkout@v6 - - - name: Get Playwright version - id: playwright - run: echo "version=$(node -p "require('./dev-packages/browser-integration-tests/package.json').devDependencies['@playwright/test'].replace('~', '')")" >> $GITHUB_OUTPUT - - - name: Log in to GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v6 - with: - file: .github/docker/playwright.Dockerfile - push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} - build-args: | - PLAYWRIGHT_VERSION=${{ steps.playwright.outputs.version }} - tags: | - ghcr.io/${{ github.repository }}/playwright:v${{ steps.playwright.outputs.version }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f24b5886ec2d..2c863714c2a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,6 @@ concurrency: env: HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} - PLAYWRIGHT_IMAGE: ghcr.io/getsentry/sentry-javascript/playwright:v1.56.1 # WARNING: this disables cross os caching as ~ and # github.workspace evaluate to differents paths @@ -72,6 +71,29 @@ jobs: permissions: pull-requests: read + job_playwright_image: + name: Ensure Playwright Image + runs-on: ubuntu-24.04 + permissions: + packages: write + outputs: + image: ${{ steps.ensure.outputs.image }} + steps: + - name: Check out current commit + uses: actions/checkout@v6 + with: + ref: ${{ env.HEAD_COMMIT }} + sparse-checkout: | + .github + dev-packages/browser-integration-tests/package.json + dev-packages/test-utils/package.json + dev-packages/e2e-tests/test-applications + - name: Ensure Playwright image + id: ensure + uses: ./.github/actions/ensure-playwright-image + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + job_build: name: Build needs: job_get_metadata @@ -476,11 +498,11 @@ jobs: name: Playwright ${{ matrix.bundle }}${{ matrix.project && matrix.project != 'chromium' && format(' {0}', matrix.project) || ''}}${{ matrix.shard && format(' ({0}/{1})', matrix.shard, matrix.shards) || ''}} Tests - needs: [job_get_metadata, job_build] + needs: [job_get_metadata, job_build, job_playwright_image] if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-24.04-large-js container: - image: ${{ env.PLAYWRIGHT_IMAGE }} + image: ${{ needs.job_playwright_image.outputs.image }} credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} @@ -579,11 +601,11 @@ jobs: job_browser_loader_tests: name: PW ${{ matrix.bundle }} Tests - needs: [job_get_metadata, job_build] + needs: [job_get_metadata, job_build, job_playwright_image] if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-24.04 container: - image: ${{ env.PLAYWRIGHT_IMAGE }} + image: ${{ needs.job_playwright_image.outputs.image }} credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} @@ -772,11 +794,11 @@ jobs: job_remix_integration_tests: name: Remix (Node ${{ matrix.node }}) Tests - needs: [job_get_metadata, job_build] + needs: [job_get_metadata, job_build, job_playwright_image] if: needs.job_build.outputs.changed_remix == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-24.04 container: - image: ${{ env.PLAYWRIGHT_IMAGE }} + image: ${{ needs.job_playwright_image.outputs.image }} credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} @@ -874,10 +896,11 @@ jobs: # See: https://github.com/actions/runner/issues/2205 if: always() && needs.job_e2e_prepare.result == 'success' && needs.job_e2e_prepare.outputs.matrix != '{"include":[]}' - needs: [job_get_metadata, job_build, job_e2e_prepare] + && needs.job_playwright_image.result == 'success' + needs: [job_get_metadata, job_build, job_e2e_prepare, job_playwright_image] runs-on: ubuntu-24.04 container: - image: ${{ env.PLAYWRIGHT_IMAGE }} + image: ${{ needs.job_playwright_image.outputs.image }} credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} @@ -1003,10 +1026,11 @@ jobs: always() && needs.job_get_metadata.outputs.is_release != 'true' && needs.job_e2e_prepare.result == 'success' && needs.job_e2e_prepare.outputs.matrix-optional != '{"include":[]}' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' - needs: [job_get_metadata, job_build, job_e2e_prepare] + && needs.job_playwright_image.result == 'success' + needs: [job_get_metadata, job_build, job_e2e_prepare, job_playwright_image] runs-on: ubuntu-24.04 container: - image: ${{ env.PLAYWRIGHT_IMAGE }} + image: ${{ needs.job_playwright_image.outputs.image }} credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index e27d33429814..5ebabab76873 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -11,7 +11,6 @@ on: env: HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} - PLAYWRIGHT_IMAGE: ghcr.io/getsentry/sentry-javascript/playwright:v1.56.1 CACHED_BUILD_PATHS: | ${{ github.workspace }}/packages/*/*.tgz @@ -24,8 +23,29 @@ env: permissions: contents: read issues: write + packages: write jobs: + job_playwright_image: + name: Ensure Playwright Image + runs-on: ubuntu-24.04 + outputs: + image: ${{ steps.ensure.outputs.image }} + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ env.HEAD_COMMIT }} + sparse-checkout: | + .github + dev-packages/browser-integration-tests/package.json + dev-packages/test-utils/package.json + dev-packages/e2e-tests/test-applications + - name: Ensure Playwright image + id: ensure + uses: ./.github/actions/ensure-playwright-image + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + job_e2e_prepare: name: Prepare E2E Canary tests runs-on: ubuntu-24.04 @@ -54,10 +74,10 @@ jobs: job_e2e_tests: name: E2E ${{ matrix.label }} Test - needs: [job_e2e_prepare] + needs: [job_e2e_prepare, job_playwright_image] runs-on: ubuntu-24.04 container: - image: ${{ env.PLAYWRIGHT_IMAGE }} + image: ${{ needs.job_playwright_image.outputs.image }} credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/flaky-test-detector.yml b/.github/workflows/flaky-test-detector.yml index 2c8ee4485006..a020a8bf93fe 100644 --- a/.github/workflows/flaky-test-detector.yml +++ b/.github/workflows/flaky-test-detector.yml @@ -9,7 +9,6 @@ on: env: HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} - PLAYWRIGHT_IMAGE: ghcr.io/getsentry/sentry-javascript/playwright:v1.56.1 NX_CACHE_RESTORE_KEYS: | nx-Linux-${{ github.ref }}-${{ github.event.inputs.commit || github.sha }} @@ -23,10 +22,32 @@ concurrency: cancel-in-progress: true jobs: + job_playwright_image: + name: Ensure Playwright Image + runs-on: ubuntu-24.04 + permissions: + packages: write + outputs: + image: ${{ steps.ensure.outputs.image }} + steps: + - uses: actions/checkout@v6 + with: + sparse-checkout: | + .github + dev-packages/browser-integration-tests/package.json + dev-packages/test-utils/package.json + dev-packages/e2e-tests/test-applications + - name: Ensure Playwright image + id: ensure + uses: ./.github/actions/ensure-playwright-image + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + flaky-detector: + needs: [job_playwright_image] runs-on: ubuntu-24.04 container: - image: ${{ env.PLAYWRIGHT_IMAGE }} + image: ${{ needs.job_playwright_image.outputs.image }} credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} From e80492658ae48108aea9033f9dfc48cedcc1cbb0 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 10:28:41 +0200 Subject: [PATCH 05/11] fix(ci): Remove hardcoded default from Playwright Dockerfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the default value for PLAYWRIGHT_VERSION ARG so the build fails if the arg isn't passed. The ensure-playwright-image action reads the version from browser-integration-tests/package.json and passes it as a build arg — that is the single source of truth. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/docker/playwright.Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/docker/playwright.Dockerfile b/.github/docker/playwright.Dockerfile index 8c9d2b4ba5ea..e08df49898e7 100644 --- a/.github/docker/playwright.Dockerfile +++ b/.github/docker/playwright.Dockerfile @@ -1,4 +1,6 @@ -ARG PLAYWRIGHT_VERSION=1.56.1 +# PLAYWRIGHT_VERSION is passed as a build arg by the ensure-playwright-image action. +# The canonical source is dev-packages/browser-integration-tests/package.json. +ARG PLAYWRIGHT_VERSION FROM mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-noble # Install yarn (v1) for the monorepo From 74de4ecfa9da0131bfc7b1b741d8425b573dbc6b Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 10:29:16 +0200 Subject: [PATCH 06/11] chore(ci): Remove sparse-checkout from Playwright image jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify the checkout steps — full checkout is fine here and avoids potential issues with missing files. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build.yml | 5 ----- .github/workflows/canary.yml | 5 ----- .github/workflows/flaky-test-detector.yml | 6 ------ 3 files changed, 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c863714c2a3..d5e7807bb4b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -83,11 +83,6 @@ jobs: uses: actions/checkout@v6 with: ref: ${{ env.HEAD_COMMIT }} - sparse-checkout: | - .github - dev-packages/browser-integration-tests/package.json - dev-packages/test-utils/package.json - dev-packages/e2e-tests/test-applications - name: Ensure Playwright image id: ensure uses: ./.github/actions/ensure-playwright-image diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index 5ebabab76873..4ba45e2f5982 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -35,11 +35,6 @@ jobs: - uses: actions/checkout@v6 with: ref: ${{ env.HEAD_COMMIT }} - sparse-checkout: | - .github - dev-packages/browser-integration-tests/package.json - dev-packages/test-utils/package.json - dev-packages/e2e-tests/test-applications - name: Ensure Playwright image id: ensure uses: ./.github/actions/ensure-playwright-image diff --git a/.github/workflows/flaky-test-detector.yml b/.github/workflows/flaky-test-detector.yml index a020a8bf93fe..d50c8834f9a7 100644 --- a/.github/workflows/flaky-test-detector.yml +++ b/.github/workflows/flaky-test-detector.yml @@ -31,12 +31,6 @@ jobs: image: ${{ steps.ensure.outputs.image }} steps: - uses: actions/checkout@v6 - with: - sparse-checkout: | - .github - dev-packages/browser-integration-tests/package.json - dev-packages/test-utils/package.json - dev-packages/e2e-tests/test-applications - name: Ensure Playwright image id: ensure uses: ./.github/actions/ensure-playwright-image From 0af56ad4129ac70f325098d9a892c9a0af7a4429 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 10:31:52 +0200 Subject: [PATCH 07/11] fix(ci): Fix Playwright version extraction in ensure-playwright-image @playwright/test is in `dependencies` (not `devDependencies`) in browser-integration-tests/package.json. Check both fields so the version is found regardless of which section it's declared in. Also cleaned up the version consistency check to use a shared function and log the canonical version for easier debugging. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ensure-playwright-image/action.yml | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/.github/actions/ensure-playwright-image/action.yml b/.github/actions/ensure-playwright-image/action.yml index 9c7531ba8259..ce05398c599e 100644 --- a/.github/actions/ensure-playwright-image/action.yml +++ b/.github/actions/ensure-playwright-image/action.yml @@ -16,40 +16,45 @@ runs: steps: - name: Get Playwright version id: playwright - run: echo "version=$(node -p "require('./dev-packages/browser-integration-tests/package.json').devDependencies['@playwright/test'].replace('~', '')")" >> $GITHUB_OUTPUT + run: | + VERSION=$(node -p " + const pkg = require('./dev-packages/browser-integration-tests/package.json'); + const v = (pkg.dependencies || {})['@playwright/test'] || (pkg.devDependencies || {})['@playwright/test']; + v.replace(/[~^]/, '') + ") + echo "version=$VERSION" >> $GITHUB_OUTPUT shell: bash - name: Verify @playwright/test version consistency shell: bash run: | - CANONICAL=$(node -p "require('./dev-packages/browser-integration-tests/package.json').devDependencies['@playwright/test']") + CANONICAL=$(node -p " + const pkg = require('./dev-packages/browser-integration-tests/package.json'); + (pkg.dependencies || {})['@playwright/test'] || (pkg.devDependencies || {})['@playwright/test'] + ") + echo "Canonical @playwright/test version: $CANONICAL" MISMATCHES=0 - for pkg in dev-packages/test-utils/package.json; do - VERSION=$(node -p " - const pkg = require('./$pkg'); + check_version() { + local file="$1" + local version + version=$(node -p " + const pkg = require('./$file'); (pkg.dependencies || {})['@playwright/test'] || (pkg.devDependencies || {})['@playwright/test'] || (pkg.peerDependencies || {})['@playwright/test'] || 'not found' ") - if [ "$VERSION" != "not found" ] && [ "$VERSION" != "$CANONICAL" ]; then - echo "::error file=$pkg::@playwright/test version mismatch: $VERSION (expected $CANONICAL)" + if [ "$version" != "not found" ] && [ "$version" != "$CANONICAL" ]; then + echo "::error file=$file::@playwright/test version mismatch: $version (expected $CANONICAL)" MISMATCHES=$((MISMATCHES + 1)) fi - done + } + + check_version "dev-packages/test-utils/package.json" for pkg in dev-packages/e2e-tests/test-applications/*/package.json; do - VERSION=$(node -p " - const pkg = require('./$pkg'); - (pkg.dependencies || {})['@playwright/test'] || - (pkg.devDependencies || {})['@playwright/test'] || - 'not found' - ") - if [ "$VERSION" != "not found" ] && [ "$VERSION" != "$CANONICAL" ]; then - echo "::error file=$pkg::@playwright/test version mismatch: $VERSION (expected $CANONICAL)" - MISMATCHES=$((MISMATCHES + 1)) - fi + check_version "$pkg" done if [ "$MISMATCHES" -gt 0 ]; then From 19ff1808390a163c11399726bee6fab11a1dc712 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 14:14:51 +0200 Subject: [PATCH 08/11] fix(ci): Add --ipc=host to Playwright container options Firefox fails with "Sandbox: CanCreateUserNamespace() clone() failure: EPERM" when running in a Docker container on GitHub Actions because user namespaces are restricted. Adding --ipc=host allows the browser sandbox to work correctly for all browsers (chromium, firefox, webkit). Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build.yml | 9 +++++++-- .github/workflows/canary.yml | 1 + .github/workflows/flaky-test-detector.yml | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d5e7807bb4b9..de4245f72991 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -501,6 +501,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + options: --ipc=host timeout-minutes: 25 strategy: fail-fast: false @@ -604,6 +605,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + options: --ipc=host timeout-minutes: 15 strategy: fail-fast: false @@ -797,6 +799,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + options: --ipc=host timeout-minutes: 15 strategy: fail-fast: false @@ -899,6 +902,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + options: --ipc=host timeout-minutes: 15 env: # We just use a dummy DSN here, only send to the tunnel anyhow @@ -1020,8 +1024,8 @@ jobs: if: always() && needs.job_get_metadata.outputs.is_release != 'true' && needs.job_e2e_prepare.result == 'success' && needs.job_e2e_prepare.outputs.matrix-optional != '{"include":[]}' && (github.event_name != 'pull_request' || - github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' - && needs.job_playwright_image.result == 'success' + github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' && + needs.job_playwright_image.result == 'success' needs: [job_get_metadata, job_build, job_e2e_prepare, job_playwright_image] runs-on: ubuntu-24.04 container: @@ -1029,6 +1033,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + options: --ipc=host timeout-minutes: 15 env: E2E_TEST_AUTH_TOKEN: ${{ secrets.E2E_TEST_AUTH_TOKEN }} diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index 4ba45e2f5982..02ced13b551e 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -76,6 +76,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + options: --ipc=host timeout-minutes: 20 env: # We just use a dummy DSN here, only send to the tunnel anyhow diff --git a/.github/workflows/flaky-test-detector.yml b/.github/workflows/flaky-test-detector.yml index d50c8834f9a7..c055f2a692c9 100644 --- a/.github/workflows/flaky-test-detector.yml +++ b/.github/workflows/flaky-test-detector.yml @@ -45,6 +45,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + options: --ipc=host timeout-minutes: 60 name: 'Check tests for flakiness' # Also skip if PR is from master -> develop From 2c9a23baa9a71a57751747ed42467e8fb0c480cb Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 14:15:33 +0200 Subject: [PATCH 09/11] chore: Format ensure-playwright-image action Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/actions/ensure-playwright-image/action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/ensure-playwright-image/action.yml b/.github/actions/ensure-playwright-image/action.yml index ce05398c599e..dfaa8ba6bcfa 100644 --- a/.github/actions/ensure-playwright-image/action.yml +++ b/.github/actions/ensure-playwright-image/action.yml @@ -66,7 +66,9 @@ runs: - name: Set image reference id: image - run: echo "ref=ghcr.io/${{ github.repository }}/playwright:v${{ steps.playwright.outputs.version }}" >> $GITHUB_OUTPUT + run: + echo "ref=ghcr.io/${{ github.repository }}/playwright:v${{ steps.playwright.outputs.version }}" >> + $GITHUB_OUTPUT shell: bash - name: Check if image already exists From 9c9a53bc9588640b8407be5ed361d5d3bf5c6a57 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 14:43:53 +0200 Subject: [PATCH 10/11] fix(ci): Include Dockerfile hash in Playwright image tag The image tag was only based on the Playwright version, so changes to the Dockerfile (e.g. adding yarn, system deps) didn't trigger a rebuild. Tag is now `v-<8-char-dockerfile-hash>`, e.g. `v1.56.0-a1b2c3d4`. Any change to the Dockerfile produces a new tag, which misses the GHCR cache and triggers a fresh build. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/actions/ensure-playwright-image/action.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/actions/ensure-playwright-image/action.yml b/.github/actions/ensure-playwright-image/action.yml index dfaa8ba6bcfa..fab5bb8b5d4c 100644 --- a/.github/actions/ensure-playwright-image/action.yml +++ b/.github/actions/ensure-playwright-image/action.yml @@ -66,10 +66,11 @@ runs: - name: Set image reference id: image - run: - echo "ref=ghcr.io/${{ github.repository }}/playwright:v${{ steps.playwright.outputs.version }}" >> - $GITHUB_OUTPUT shell: bash + run: | + DOCKERFILE_HASH=$(sha256sum .github/docker/playwright.Dockerfile | cut -c1-8) + TAG="v${{ steps.playwright.outputs.version }}-${DOCKERFILE_HASH}" + echo "ref=ghcr.io/${{ github.repository }}/playwright:${TAG}" >> $GITHUB_OUTPUT - name: Check if image already exists id: check From c5cca3400f471e1a41e4c0aec87ebe27943a9649 Mon Sep 17 00:00:00 2001 From: Francesco Gringl-Novy Date: Tue, 14 Apr 2026 14:44:39 +0200 Subject: [PATCH 11/11] fix(ci): Set HOME=/root for Playwright containers GitHub Actions runs containers as root but sets HOME=/github/home (owned by pwuser). Firefox refuses to launch as root in another user's home directory. Setting HOME=/root fixes this. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build.yml | 10 +++++----- .github/workflows/canary.yml | 2 +- .github/workflows/flaky-test-detector.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de4245f72991..638923a88087 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -501,7 +501,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - options: --ipc=host + options: --ipc=host -e HOME=/root timeout-minutes: 25 strategy: fail-fast: false @@ -605,7 +605,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - options: --ipc=host + options: --ipc=host -e HOME=/root timeout-minutes: 15 strategy: fail-fast: false @@ -799,7 +799,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - options: --ipc=host + options: --ipc=host -e HOME=/root timeout-minutes: 15 strategy: fail-fast: false @@ -902,7 +902,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - options: --ipc=host + options: --ipc=host -e HOME=/root timeout-minutes: 15 env: # We just use a dummy DSN here, only send to the tunnel anyhow @@ -1033,7 +1033,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - options: --ipc=host + options: --ipc=host -e HOME=/root timeout-minutes: 15 env: E2E_TEST_AUTH_TOKEN: ${{ secrets.E2E_TEST_AUTH_TOKEN }} diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index 02ced13b551e..364f56cf6c6b 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -76,7 +76,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - options: --ipc=host + options: --ipc=host -e HOME=/root timeout-minutes: 20 env: # We just use a dummy DSN here, only send to the tunnel anyhow diff --git a/.github/workflows/flaky-test-detector.yml b/.github/workflows/flaky-test-detector.yml index c055f2a692c9..6737ccea13b5 100644 --- a/.github/workflows/flaky-test-detector.yml +++ b/.github/workflows/flaky-test-detector.yml @@ -45,7 +45,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - options: --ipc=host + options: --ipc=host -e HOME=/root timeout-minutes: 60 name: 'Check tests for flakiness' # Also skip if PR is from master -> develop