From 22286255ced0c347a94243d129081168d571d8d6 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 15 Oct 2026 09:39:38 +0200 Subject: [PATCH 1/2] ci: Fail when a Playwright version drifts from the pinned one All packages and E2E apps must declare the exact `@playwright/test` version of `@sentry-internal/test-utils`. The check runs in the Lint job. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 2 + scripts/check-playwright-versions.mjs | 55 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 scripts/check-playwright-versions.mjs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ce53bcc737e0..cf919cb58576 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -346,6 +346,8 @@ jobs: run: pnpm run lint:es-compatibility - name: Lint types run: pnpm run lint:types + - name: Check Playwright versions + run: node ./scripts/check-playwright-versions.mjs job_check_lockfile: name: Check lockfile diff --git a/scripts/check-playwright-versions.mjs b/scripts/check-playwright-versions.mjs new file mode 100644 index 000000000000..87e0846ef712 --- /dev/null +++ b/scripts/check-playwright-versions.mjs @@ -0,0 +1,55 @@ +/* eslint-disable no-console */ +import { existsSync, readdirSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +// Every package, including each E2E app, must use this exact version. +// `install-playwright` installs browsers for the declared version, so a drifting app would test against other browsers. +const REFERENCE_PACKAGE_JSON = 'dev-packages/test-utils/package.json'; +const PLAYWRIGHT_PACKAGES = ['@playwright/test', 'playwright', 'playwright-core']; +const DEPENDENCY_FIELDS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']; +const PACKAGE_DIRS = ['packages', 'dev-packages', 'dev-packages/e2e-tests/test-applications']; + +const rootDir = join(import.meta.dirname, '..'); + +/** @param {string} path */ +function readPackageJson(path) { + return JSON.parse(readFileSync(join(rootDir, path), 'utf8')); +} + +const expected = readPackageJson(REFERENCE_PACKAGE_JSON).devDependencies['@playwright/test']; + +if (!/^\d+\.\d+\.\d+$/.test(expected)) { + console.error(`@playwright/test in ${REFERENCE_PACKAGE_JSON} must be an exact version, got "${expected}".`); + process.exit(1); +} + +const packageJsonPaths = ['package.json']; +for (const dir of PACKAGE_DIRS) { + for (const entry of readdirSync(join(rootDir, dir), { withFileTypes: true })) { + const path = join(dir, entry.name, 'package.json'); + if (entry.isDirectory() && existsSync(join(rootDir, path))) { + packageJsonPaths.push(path); + } + } +} + +const mismatches = []; +for (const path of packageJsonPaths) { + const packageJson = readPackageJson(path); + for (const field of DEPENDENCY_FIELDS) { + for (const name of PLAYWRIGHT_PACKAGES) { + const version = packageJson[field]?.[name]; + if (version !== undefined && version !== expected) { + mismatches.push(` ${path}: ${field}["${name}"] is "${version}"`); + } + } + } +} + +if (mismatches.length) { + console.error(`All Playwright dependencies must be pinned to "${expected}" (from ${REFERENCE_PACKAGE_JSON}):`); + console.error(mismatches.join('\n')); + process.exit(1); +} + +console.log(`All Playwright dependencies are pinned to ${expected}.`); From 2048658b46302153c63ffd81378eae34a9fe85b9 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Mon, 19 Oct 2026 11:28:33 +0200 Subject: [PATCH 2/2] ci: Replace Playwright version script with an inline git grep check Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 7 +++- scripts/check-playwright-versions.mjs | 55 --------------------------- 2 files changed, 6 insertions(+), 56 deletions(-) delete mode 100644 scripts/check-playwright-versions.mjs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cf919cb58576..7502ea53b9ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -347,7 +347,12 @@ jobs: - name: Lint types run: pnpm run lint:types - name: Check Playwright versions - run: node ./scripts/check-playwright-versions.mjs + run: | + version=$(node -p "require('./dev-packages/test-utils/package.json').devDependencies['@playwright/test']") + if git grep -n '"@playwright/test": "' -- '*package.json' | grep -vF "\"$version\""; then + echo "::error::All @playwright/test dependencies must be pinned to $version (from @sentry-internal/test-utils)." + exit 1 + fi job_check_lockfile: name: Check lockfile diff --git a/scripts/check-playwright-versions.mjs b/scripts/check-playwright-versions.mjs deleted file mode 100644 index 87e0846ef712..000000000000 --- a/scripts/check-playwright-versions.mjs +++ /dev/null @@ -1,55 +0,0 @@ -/* eslint-disable no-console */ -import { existsSync, readdirSync, readFileSync } from 'node:fs'; -import { join } from 'node:path'; - -// Every package, including each E2E app, must use this exact version. -// `install-playwright` installs browsers for the declared version, so a drifting app would test against other browsers. -const REFERENCE_PACKAGE_JSON = 'dev-packages/test-utils/package.json'; -const PLAYWRIGHT_PACKAGES = ['@playwright/test', 'playwright', 'playwright-core']; -const DEPENDENCY_FIELDS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']; -const PACKAGE_DIRS = ['packages', 'dev-packages', 'dev-packages/e2e-tests/test-applications']; - -const rootDir = join(import.meta.dirname, '..'); - -/** @param {string} path */ -function readPackageJson(path) { - return JSON.parse(readFileSync(join(rootDir, path), 'utf8')); -} - -const expected = readPackageJson(REFERENCE_PACKAGE_JSON).devDependencies['@playwright/test']; - -if (!/^\d+\.\d+\.\d+$/.test(expected)) { - console.error(`@playwright/test in ${REFERENCE_PACKAGE_JSON} must be an exact version, got "${expected}".`); - process.exit(1); -} - -const packageJsonPaths = ['package.json']; -for (const dir of PACKAGE_DIRS) { - for (const entry of readdirSync(join(rootDir, dir), { withFileTypes: true })) { - const path = join(dir, entry.name, 'package.json'); - if (entry.isDirectory() && existsSync(join(rootDir, path))) { - packageJsonPaths.push(path); - } - } -} - -const mismatches = []; -for (const path of packageJsonPaths) { - const packageJson = readPackageJson(path); - for (const field of DEPENDENCY_FIELDS) { - for (const name of PLAYWRIGHT_PACKAGES) { - const version = packageJson[field]?.[name]; - if (version !== undefined && version !== expected) { - mismatches.push(` ${path}: ${field}["${name}"] is "${version}"`); - } - } - } -} - -if (mismatches.length) { - console.error(`All Playwright dependencies must be pinned to "${expected}" (from ${REFERENCE_PACKAGE_JSON}):`); - console.error(mismatches.join('\n')); - process.exit(1); -} - -console.log(`All Playwright dependencies are pinned to ${expected}.`);