From 9cded172058da9dfa6b3227d46ff30ea2023698a Mon Sep 17 00:00:00 2001 From: Konstantin Alekseev Date: Sun, 8 Mar 2026 17:53:58 +0200 Subject: [PATCH] playwright: 1.57.0 -> 1.58.2 --- pkgs/by-name/pl/playwright-mcp/package.nix | 10 +- .../python-modules/playwright/default.nix | 6 +- .../web/playwright/browser-downloads-test.js | 91 ++++++++ .../web/playwright/browser-downloads-test.nix | 35 +++ .../web/playwright/browser-downloads.nix | 56 +++++ pkgs/development/web/playwright/browsers.json | 28 +-- .../playwright/chromium-headless-shell.nix | 22 +- pkgs/development/web/playwright/chromium.nix | 21 +- pkgs/development/web/playwright/driver.nix | 50 ++-- pkgs/development/web/playwright/ffmpeg.nix | 11 +- pkgs/development/web/playwright/firefox.nix | 23 +- pkgs/development/web/playwright/update.sh | 220 +++++++++++------- pkgs/development/web/playwright/webkit.nix | 27 +-- 13 files changed, 415 insertions(+), 185 deletions(-) create mode 100644 pkgs/development/web/playwright/browser-downloads-test.js create mode 100644 pkgs/development/web/playwright/browser-downloads-test.nix create mode 100644 pkgs/development/web/playwright/browser-downloads.nix diff --git a/pkgs/by-name/pl/playwright-mcp/package.nix b/pkgs/by-name/pl/playwright-mcp/package.nix index 4c4d1727d401..78120415c44a 100644 --- a/pkgs/by-name/pl/playwright-mcp/package.nix +++ b/pkgs/by-name/pl/playwright-mcp/package.nix @@ -18,11 +18,13 @@ buildNpmPackage rec { npmDepsHash = "sha256-Qsln4llNpfXYXhSEfHnvdsFIF7adHKEyC1eGHtVY2Qk="; + # Codex MCP smoke test (after `codex mcp add playwright-nix --env DISPLAY=:0 -- $out/bin/mcp-server-playwright --headless --isolated`): + # timeout 45s codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "Use only playwright-nix MCP tools. Navigate to https://example.com and return only the page title." postInstall = '' - rm -r $out/lib/node_modules/@playwright/mcp/node_modules/playwright - rm -r $out/lib/node_modules/@playwright/mcp/node_modules/playwright-core - ln -s ${playwright-test}/lib/node_modules/playwright $out/lib/node_modules/@playwright/mcp/node_modules/playwright - ln -s ${playwright-test}/lib/node_modules/playwright-core $out/lib/node_modules/@playwright/mcp/node_modules/playwright-core + rm -rf "$out/lib/node_modules/@playwright/mcp/node_modules/playwright" + rm -rf "$out/lib/node_modules/@playwright/mcp/node_modules/playwright-core" + ln -s ${playwright-test}/lib/node_modules/playwright "$out/lib/node_modules/@playwright/mcp/node_modules/playwright" + ln -s ${playwright-test}/lib/node_modules/playwright-core "$out/lib/node_modules/@playwright/mcp/node_modules/playwright-core" wrapProgram $out/bin/mcp-server-playwright \ --set PLAYWRIGHT_BROWSERS_PATH ${playwright-driver.browsers} \ diff --git a/pkgs/development/python-modules/playwright/default.nix b/pkgs/development/python-modules/playwright/default.nix index e1e19a40643b..50989eddc3e9 100644 --- a/pkgs/development/python-modules/playwright/default.nix +++ b/pkgs/development/python-modules/playwright/default.nix @@ -7,6 +7,7 @@ fetchFromGitHub, pyee, python, + pythonOlder, setuptools, setuptools-scm, playwright-driver, @@ -20,14 +21,15 @@ in buildPythonPackage rec { pname = "playwright"; # run ./pkgs/development/web/playwright/update.sh to update - version = "1.57.0"; + version = "1.58.0"; pyproject = true; + disabled = pythonOlder "3.9"; src = fetchFromGitHub { owner = "microsoft"; repo = "playwright-python"; tag = "v${version}"; - hash = "sha256-LXTMVC6ytjo7l0QDkNPxtoMTYjmYMjCVqHz61BgAn6A="; + hash = "sha256-gK19pjB8TDy/kK+fb4pjwlGZlUyY26p+CNxunvIMrrY="; }; patches = [ diff --git a/pkgs/development/web/playwright/browser-downloads-test.js b/pkgs/development/web/playwright/browser-downloads-test.js new file mode 100644 index 000000000000..eac7f57ef585 --- /dev/null +++ b/pkgs/development/web/playwright/browser-downloads-test.js @@ -0,0 +1,91 @@ +"use strict"; + +const childProcess = require("child_process"); +const fs = require("fs"); + +const [playwrightCorePath, expectedConfigPath] = process.argv.slice(2); + +if (!playwrightCorePath || !expectedConfigPath) { + throw new Error("usage: browser-downloads-test.js "); +} + +const browserDownloads = JSON.parse(fs.readFileSync(expectedConfigPath, "utf8")); +const browserNames = Object.keys(browserDownloads); + +const hostPlatformBySystem = { + "x86_64-linux": "ubuntu22.04-x64", + "aarch64-linux": "ubuntu22.04-arm64", + "x86_64-darwin": "mac15", + "aarch64-darwin": "mac15-arm64", +}; + +function getRegistryDownloadURLs(hostPlatform) { + const script = ` + const path = require("path"); + const { registry } = require(path.join(process.env.PLAYWRIGHT_CORE_PATH, "lib/server/registry/index.js")); + const browserNames = JSON.parse(process.env.PLAYWRIGHT_BROWSER_NAMES); + const downloadURLs = Object.fromEntries( + browserNames.map((name) => [name, registry.findExecutable(name).downloadURLs]), + ); + process.stdout.write(JSON.stringify(downloadURLs)); + `; + + return JSON.parse( + childProcess.execFileSync(process.execPath, ["-e", script], { + encoding: "utf8", + env: { + ...process.env, + PLAYWRIGHT_BROWSER_NAMES: JSON.stringify(browserNames), + PLAYWRIGHT_CORE_PATH: playwrightCorePath, + PLAYWRIGHT_HOST_PLATFORM_OVERRIDE: hostPlatform, + }, + }), + ); +} + +const failures = []; +const systems = Object.keys(browserDownloads[browserNames[0]]); + +for (const system of systems) { + const hostPlatform = hostPlatformBySystem[system]; + const registryDownloadURLs = getRegistryDownloadURLs(hostPlatform); + + if (!hostPlatform) { + throw new Error(`unsupported system: ${system}`); + } + + for (const name of browserNames) { + const expectedDownload = browserDownloads[name][system]; + const actualDownloadURLs = registryDownloadURLs[name]; + + if (!expectedDownload) { + failures.push(`missing browser-downloads.nix entry for ${name} on ${system}`); + continue; + } + + if (!Array.isArray(actualDownloadURLs) || actualDownloadURLs.length === 0) { + failures.push(`missing registry download URLs for ${name} on ${system} (${hostPlatform})`); + continue; + } + + const actualDownloadURL = actualDownloadURLs[0]; + + if (expectedDownload.url !== actualDownloadURL) { + failures.push( + [ + `${name} on ${system} (${hostPlatform}) did not match Playwright's primary download URL`, + `expected: ${browserDownloads[name][system].url}`, + `registry: ${actualDownloadURL}`, + ].join("\n"), + ); + } + } +} + +if (failures.length > 0) { + console.error("Playwright browser download URL mismatches:"); + for (const failure of failures) { + console.error(`- ${failure}`); + } + process.exit(1); +} diff --git a/pkgs/development/web/playwright/browser-downloads-test.nix b/pkgs/development/web/playwright/browser-downloads-test.nix new file mode 100644 index 000000000000..e51ecd4a9a01 --- /dev/null +++ b/pkgs/development/web/playwright/browser-downloads-test.nix @@ -0,0 +1,35 @@ +{ + lib, + runCommand, + nodejs, + playwright-core, +}: +let + browserNames = [ + "chromium" + "chromium-headless-shell" + "firefox" + "webkit" + "ffmpeg" + ]; + browsersJSON = (lib.importJSON ./browsers.json).browsers; + browserDownloads = lib.genAttrs browserNames ( + name: + import ./browser-downloads.nix { + inherit name; + inherit (browsersJSON.${name}) revision; + browserVersion = browsersJSON.${name}.browserVersion or ""; + } + ); + browserDownloadsExpectedJSON = builtins.toFile "playwright-browser-downloads.json" ( + builtins.toJSON browserDownloads + ); +in +runCommand "playwright-browser-downloads-test" + { + nativeBuildInputs = [ nodejs ]; + } + '' + node ${./browser-downloads-test.js} ${playwright-core} ${browserDownloadsExpectedJSON} + touch $out + '' diff --git a/pkgs/development/web/playwright/browser-downloads.nix b/pkgs/development/web/playwright/browser-downloads.nix new file mode 100644 index 000000000000..4d83892e0531 --- /dev/null +++ b/pkgs/development/web/playwright/browser-downloads.nix @@ -0,0 +1,56 @@ +{ + name, + revision, + browserVersion ? "", +}: +let + cftUrl = + path: + assert browserVersion != ""; + "https://cdn.playwright.dev/builds/cft/${browserVersion}/${path}"; + + registryUrl = + browser: archive: + "https://cdn.playwright.dev/dbazure/download/playwright/builds/${browser}/${revision}/${archive}"; + + mk = url: stripRoot: { + inherit url stripRoot; + }; +in +{ + chromium = { + x86_64-linux = mk (cftUrl "linux64/chrome-linux64.zip") true; + aarch64-linux = mk (registryUrl "chromium" "chromium-linux-arm64.zip") true; + x86_64-darwin = mk (cftUrl "mac-x64/chrome-mac-x64.zip") false; + aarch64-darwin = mk (cftUrl "mac-arm64/chrome-mac-arm64.zip") false; + }; + + "chromium-headless-shell" = { + x86_64-linux = mk (cftUrl "linux64/chrome-headless-shell-linux64.zip") false; + aarch64-linux = mk (registryUrl "chromium" "chromium-headless-shell-linux-arm64.zip") false; + x86_64-darwin = mk (cftUrl "mac-x64/chrome-headless-shell-mac-x64.zip") false; + aarch64-darwin = mk (cftUrl "mac-arm64/chrome-headless-shell-mac-arm64.zip") false; + }; + + firefox = { + x86_64-linux = mk (registryUrl "firefox" "firefox-ubuntu-22.04.zip") true; + aarch64-linux = mk (registryUrl "firefox" "firefox-ubuntu-22.04-arm64.zip") true; + x86_64-darwin = mk (registryUrl "firefox" "firefox-mac.zip") false; + aarch64-darwin = mk (registryUrl "firefox" "firefox-mac-arm64.zip") false; + }; + + webkit = { + x86_64-linux = mk (registryUrl "webkit" "webkit-ubuntu-22.04.zip") false; + aarch64-linux = mk (registryUrl "webkit" "webkit-ubuntu-22.04-arm64.zip") false; + x86_64-darwin = mk (registryUrl "webkit" "webkit-mac-15.zip") false; + aarch64-darwin = mk (registryUrl "webkit" "webkit-mac-15-arm64.zip") false; + }; + + ffmpeg = { + x86_64-linux = mk (registryUrl "ffmpeg" "ffmpeg-linux.zip") false; + aarch64-linux = mk (registryUrl "ffmpeg" "ffmpeg-linux-arm64.zip") false; + x86_64-darwin = mk (registryUrl "ffmpeg" "ffmpeg-mac.zip") false; + aarch64-darwin = mk (registryUrl "ffmpeg" "ffmpeg-mac-arm64.zip") false; + }; +} +.${name} diff --git a/pkgs/development/web/playwright/browsers.json b/pkgs/development/web/playwright/browsers.json index bebcdade20d7..bccc346c7b66 100644 --- a/pkgs/development/web/playwright/browsers.json +++ b/pkgs/development/web/playwright/browsers.json @@ -2,34 +2,30 @@ "comment": "This file is kept up to date via update.sh", "browsers": { "chromium": { - "revision": "1200", - "browserVersion": "143.0.7499.4" + "revision": "1208", + "browserVersion": "145.0.7632.6", + "title": "Chrome for Testing" }, "chromium-headless-shell": { - "revision": "1200", - "browserVersion": "143.0.7499.4" + "revision": "1208", + "browserVersion": "145.0.7632.6", + "title": "Chrome Headless Shell" }, "firefox": { - "revision": "1497", - "browserVersion": "144.0.2" + "revision": "1509", + "browserVersion": "146.0.1", + "title": "Firefox" }, "webkit": { - "revision": "2227", + "revision": "2248", "revisionOverrides": { "debian11-x64": "2105", "debian11-arm64": "2105", - "mac10.14": "1446", - "mac10.15": "1616", - "mac11": "1816", - "mac11-arm64": "1816", - "mac12": "2009", - "mac12-arm64": "2009", - "mac13": "2140", - "mac13-arm64": "2140", "ubuntu20.04-x64": "2092", "ubuntu20.04-arm64": "2092" }, - "browserVersion": "26.0" + "browserVersion": "26.0", + "title": "WebKit" }, "ffmpeg": { "revision": "1011", diff --git a/pkgs/development/web/playwright/chromium-headless-shell.nix b/pkgs/development/web/playwright/chromium-headless-shell.nix index 4003d077365d..33d260bb84d1 100644 --- a/pkgs/development/web/playwright/chromium-headless-shell.nix +++ b/pkgs/development/web/playwright/chromium-headless-shell.nix @@ -1,7 +1,7 @@ { fetchzip, revision, - suffix, + browserVersion, system, throwSystem, stdenv, @@ -24,15 +24,20 @@ ... }: let + download = + (import ./browser-downloads.nix { + name = "chromium-headless-shell"; + inherit revision browserVersion; + }).${system} or throwSystem; + linux = stdenv.mkDerivation { name = "playwright-chromium-headless-shell"; src = fetchzip { - url = "https://playwright.azureedge.net/builds/chromium/${revision}/chromium-headless-shell-${suffix}.zip"; - stripRoot = false; + inherit (download) url stripRoot; hash = { - x86_64-linux = "sha256-4xPtmjRSbkWLmV2LzVClwjeQcmktZCvDS3gYo+FlkJc="; - aarch64-linux = "sha256-rnurwOiST8fdAC5kGC9uR+MRidGtIZCPQLrg+xZbuZQ="; + x86_64-linux = "sha256-/xskLzTc9tTZmu1lwkMpjV3QV7XjP92D/7zRcFuVWT8="; + aarch64-linux = "sha256-jckH5+eGJ4BhH1NAa5LIgf3/salKLAHW9XUOo5gob4c="; } .${system} or throwSystem; }; @@ -64,12 +69,11 @@ let }; darwin = fetchzip { - url = "https://playwright.azureedge.net/builds/chromium/${revision}/chromium-headless-shell-${suffix}.zip"; - stripRoot = false; + inherit (download) url stripRoot; hash = { - x86_64-darwin = "sha256-9MsBmUuaHq3P/eWxGcihzk09e1zuEr4dIMo6ZjSM8ZQ="; - aarch64-darwin = "sha256-i8L+C4p8DCcqb5C5B5q+JuX/fTPxhBva2dlFVDkdfQ0="; + x86_64-darwin = "sha256-bgU7lZhp9XUFfGu58pFdZyhXho3Jiy4MjljR+yk0M1c="; + aarch64-darwin = "sha256-45DjMIu0t7IEYdXOmIqpV/1/MKdEfx/8T7DWagh6Zhc="; } .${system} or throwSystem; }; diff --git a/pkgs/development/web/playwright/chromium.nix b/pkgs/development/web/playwright/chromium.nix index 17458292b824..e255b6b03a40 100644 --- a/pkgs/development/web/playwright/chromium.nix +++ b/pkgs/development/web/playwright/chromium.nix @@ -5,7 +5,7 @@ chromium, fetchzip, revision, - suffix, + browserVersion, system, throwSystem, lib, @@ -41,6 +41,12 @@ ... }: let + download = + (import ./browser-downloads.nix { + name = "chromium"; + inherit revision browserVersion; + }).${system} or throwSystem; + # Playwright expects different directory names for different architectures: # - linux-x64 expects: chrome-linux64 # - linux-arm64 expects: chrome-linux @@ -54,11 +60,11 @@ let chromium-linux = stdenv.mkDerivation { name = "playwright-chromium"; src = fetchzip { - url = "https://playwright.azureedge.net/builds/chromium/${revision}/chromium-${suffix}.zip"; + inherit (download) url stripRoot; hash = { - x86_64-linux = "sha256-r715GrQMPRIsM2/Z6SRyvo/6j4fbWXKfCCh//Cc2DGw="; - aarch64-linux = "sha256-bS8CstCia8dm2DG9vBKHjsfeoXkyBZStBefu0kD8c2o="; + x86_64-linux = "sha256-dJSO05xOzlSl/EwOWNQCeuSb+lhUU6NlGBnRu59irnM="; + aarch64-linux = "sha256-9DFLCPuc9WZjYLzlRW+Df2pb+mViPK3/IOkkUozELsw="; } .${system} or throwSystem; }; @@ -121,12 +127,11 @@ let ''; }; chromium-darwin = fetchzip { - url = "https://playwright.azureedge.net/builds/chromium/${revision}/chromium-${suffix}.zip"; - stripRoot = false; + inherit (download) url stripRoot; hash = { - x86_64-darwin = "sha256-kGHlIxS9Ti362XmBt+aepYV45cCZoBRqJ+YBsLasDp0="; - aarch64-darwin = "sha256-LwY25Ckh1ZY+L196shf8ydF4IHXUIeI83Yqp8KG+nc4="; + x86_64-darwin = "sha256-vQuBHM0jkk6S/Gco/bBqSPJqXi/CJt/+nkbGtFNpgwk="; + aarch64-darwin = "sha256-qXdgHeBS5IFIa4hZVmjq0+31v/uDPXHyc4aH7Wn2E7E="; } .${system} or throwSystem; }; diff --git a/pkgs/development/web/playwright/driver.nix b/pkgs/development/web/playwright/driver.nix index 32aa37f39449..058278509365 100644 --- a/pkgs/development/web/playwright/driver.nix +++ b/pkgs/development/web/playwright/driver.nix @@ -11,36 +11,28 @@ callPackage, makeFontsConf, makeWrapper, - runCommand, cacert, }: let inherit (stdenv.hostPlatform) system; throwSystem = throw "Unsupported system: ${system}"; - suffix = - { - x86_64-linux = "linux"; - aarch64-linux = "linux-arm64"; - x86_64-darwin = "mac"; - aarch64-darwin = "mac-arm64"; - } - .${system} or throwSystem; + browsersJSON = (lib.importJSON ./browsers.json).browsers; - version = "1.57.0"; + version = "1.58.2"; src = fetchFromGitHub { owner = "Microsoft"; repo = "playwright"; rev = "v${version}"; - hash = "sha256-1g8XCToVKWOjLpWS6g60FpoIphg9Rn/nv10oRa7oyDA="; + hash = "sha256-PRA3hjMlnHGVMidhEo371WXRPyVP2W0rle8ONUlKg9Y="; }; babel-bundle = buildNpmPackage { pname = "babel-bundle"; inherit version src; sourceRoot = "${src.name}/packages/playwright/bundles/babel"; - npmDepsHash = "sha256-ByCy4go8PM0ksDg+2DcJPyoKG7Z0uIqKM647ZQwYwAE="; + npmDepsHash = "sha256-MVMxYncmIA4k6h7mLirJaroOSNbCpvKSGxN3BGGqJ9w="; dontNpmBuild = true; installPhase = '' cp -r . "$out" @@ -60,7 +52,7 @@ let pname = "utils-bundle"; inherit version src; sourceRoot = "${src.name}/packages/playwright/bundles/utils"; - npmDepsHash = "sha256-InwWYRk6eRF62qI6qpVaPceIetSr3kPIBK4LdfeoJdo="; + npmDepsHash = "sha256-HzMu3xDb7MleJSsQ1+VvpIFSxcRfnVXniYIv/c5PHRg="; dontNpmBuild = true; installPhase = '' cp -r . "$out" @@ -70,7 +62,7 @@ let pname = "utils-bundle-core"; inherit version src; sourceRoot = "${src.name}/packages/playwright-core/bundles/utils"; - npmDepsHash = "sha256-lOwcHRpv7OKfdnwqHxvh+Gy5AE/Up3Vro4czedNiOpc="; + npmDepsHash = "sha256-/nxMK+gr4jmxeUazLRXd9LXdYYBVY9VnzbbXoxazX7c="; dontNpmBuild = true; installPhase = '' cp -r . "$out" @@ -92,7 +84,7 @@ let inherit version src; sourceRoot = "${src.name}"; # update.sh depends on sourceRoot presence - npmDepsHash = "sha256-69v+H3EQuJadma8b/l9rA/yMFCCb7wWiBGN/LoJLJM8="; + npmDepsHash = "sha256-kc77z9REuV7b+zHcUzBLf7F2+cbAhzD/kaGxa6xNnGo="; nativeBuildInputs = [ cacert @@ -164,7 +156,7 @@ let ''; passthru = { - browsersJSON = (lib.importJSON ./browsers.json).browsers; + inherit browsersJSON; selectBrowsers = browsers; browsers = browsers { }; browsers-chromium = browsers { @@ -172,6 +164,9 @@ let withWebkit = false; withChromiumHeadlessShell = false; }; + tests.browser-downloads = callPackage ./browser-downloads-test.nix { + playwright-core = finalAttrs.finalPackage; + }; inherit components; updateScript = ./update.sh; }; @@ -204,27 +199,27 @@ let components = { chromium = callPackage ./chromium.nix { - inherit suffix system throwSystem; - inherit (playwright-core.passthru.browsersJSON.chromium) revision; + inherit system throwSystem; + inherit (browsersJSON.chromium) revision browserVersion; fontconfig_file = makeFontsConf { fontDirectories = [ ]; }; }; chromium-headless-shell = callPackage ./chromium-headless-shell.nix { - inherit suffix system throwSystem; - inherit (playwright-core.passthru.browsersJSON.chromium) revision; + inherit system throwSystem; + inherit (browsersJSON."chromium-headless-shell") revision browserVersion; }; firefox = callPackage ./firefox.nix { - inherit suffix system throwSystem; - inherit (playwright-core.passthru.browsersJSON.firefox) revision; + inherit system throwSystem; + inherit (browsersJSON.firefox) revision; }; webkit = callPackage ./webkit.nix { - inherit suffix system throwSystem; - inherit (playwright-core.passthru.browsersJSON.webkit) revision; + inherit system throwSystem; + inherit (browsersJSON.webkit) revision; }; ffmpeg = callPackage ./ffmpeg.nix { - inherit suffix system throwSystem; - inherit (playwright-core.passthru.browsersJSON.ffmpeg) revision; + inherit system throwSystem; + inherit (browsersJSON.ffmpeg) revision; }; }; @@ -252,8 +247,7 @@ let map ( name: let - revName = if name == "chromium-headless-shell" then "chromium" else name; - value = playwright-core.passthru.browsersJSON.${revName}; + value = browsersJSON.${name}; in lib.nameValuePair # TODO check platform for revisionOverrides diff --git a/pkgs/development/web/playwright/ffmpeg.nix b/pkgs/development/web/playwright/ffmpeg.nix index 331541e1e405..f6ea2d06b130 100644 --- a/pkgs/development/web/playwright/ffmpeg.nix +++ b/pkgs/development/web/playwright/ffmpeg.nix @@ -1,13 +1,18 @@ { fetchzip, - suffix, revision, system, throwSystem, }: +let + download = + (import ./browser-downloads.nix { + name = "ffmpeg"; + inherit revision; + }).${system} or throwSystem; +in fetchzip { - url = "https://playwright.azureedge.net/builds/ffmpeg/${revision}/ffmpeg-${suffix}.zip"; - stripRoot = false; + inherit (download) url stripRoot; hash = { x86_64-linux = "sha256-AWTiui+ccKHxsIaQSgc5gWCJT5gYwIWzAEqSuKgVqZU="; diff --git a/pkgs/development/web/playwright/firefox.nix b/pkgs/development/web/playwright/firefox.nix index 6d081c637f5c..bde369168cf0 100644 --- a/pkgs/development/web/playwright/firefox.nix +++ b/pkgs/development/web/playwright/firefox.nix @@ -1,24 +1,26 @@ { - lib, stdenv, fetchzip, firefox-bin, - suffix, revision, system, throwSystem, }: let + download = + (import ./browser-downloads.nix { + name = "firefox"; + inherit revision; + }).${system} or throwSystem; + firefox-linux = stdenv.mkDerivation { name = "playwright-firefox"; src = fetchzip { - url = "https://playwright.azureedge.net/builds/firefox/${revision}/firefox-${ - "ubuntu-22.04" + (lib.removePrefix "linux" suffix) - }.zip"; + inherit (download) url stripRoot; hash = { - x86_64-linux = "sha256-PGTFoSjU2FFRmH7qfWkJsgnGvg3AI+eGrNUwyxRU8PM="; - aarch64-linux = "sha256-P5aHwaNZ0KfAVgRVugvAl6FsQu2/7sphwulOVyXiTgg="; + x86_64-linux = "sha256-HBmCV6AFdQ2qTuCIa7WaCiSHU8Am7S+xhpmEWU6DL/U="; + aarch64-linux = "sha256-peP032z4GOoUp1WAszvzyeXnROw6RwRffG+KRe1Qf9k="; } .${system} or throwSystem; }; @@ -37,12 +39,11 @@ let ''; }; firefox-darwin = fetchzip { - url = "https://playwright.azureedge.net/builds/firefox/${revision}/firefox-${suffix}.zip"; - stripRoot = false; + inherit (download) url stripRoot; hash = { - x86_64-darwin = "sha256-MI4HfXLmVEwcoL2jlcrDkfiClEN5rI8YcJiMqzE1xYs="; - aarch64-darwin = "sha256-Il7bTQoP6ZlqizduV4iCTrVsymy33RbGQtVSnVXnhTc="; + x86_64-darwin = "sha256-uxPOq2U1D9gFXqvNzclctLbHx4fZ9O92GtDQjCRYGiM="; + aarch64-darwin = "sha256-ZFRmtlNrzrjB3ELXmGC67XO1IhfWsWZ7rXaUQ6If65s="; } .${system} or throwSystem; }; diff --git a/pkgs/development/web/playwright/update.sh b/pkgs/development/web/playwright/update.sh index 4534aea5d6c3..fd8bc354beef 100755 --- a/pkgs/development/web/playwright/update.sh +++ b/pkgs/development/web/playwright/update.sh @@ -1,90 +1,146 @@ #!/usr/bin/env nix-shell #!nix-shell -i bash -p curl gnused common-updater-scripts jq prefetch-npm-deps unzip nix-prefetch +# shellcheck shell=bash set -euo pipefail root="$(dirname "$(readlink -f "$0")")" +repo_root="$(git -C "$root" rev-parse --show-toplevel)" +cd "$repo_root" -version=$(curl ${GITHUB_TOKEN:+" -u \":$GITHUB_TOKEN\""} -s https://api.github.com/repos/microsoft/playwright-python/releases/latest | jq -r '.tag_name | sub("^v"; "")') +github_api_curl_args=() +if [ -n "${GITHUB_TOKEN:-}" ]; then + github_api_curl_args=(-u ":$GITHUB_TOKEN") +fi + +playwright_browsers_file="$root/browsers.json" +playwright_driver_file="$root/driver.nix" +playwright_raw_repo_url="https://raw.githubusercontent.com/microsoft/playwright" +playwright_mcp_package_file="$root/../../../by-name/pl/playwright-mcp/package.nix" +browser_names=(chromium chromium-headless-shell firefox webkit ffmpeg) +browser_systems=(x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin) + +github_api_get() { + curl "${github_api_curl_args[@]}" -fsSL "$1" +} + +major_minor() { + echo "${1%.*}" +} + +python_version=$(github_api_get https://api.github.com/repos/microsoft/playwright-python/releases/latest | jq -r '.tag_name | sub("^v"; "")') # Most of the time, this should be the latest stable release of the Node-based -# Playwright version, but that isn't a guarantee, so this needs to be specified -# as well: -setup_py_url="https://github.com/microsoft/playwright-python/raw/v${version}/setup.py" -driver_version=$(curl -Ls "$setup_py_url" | grep '^driver_version =' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') +# Playwright version, but upstream occasionally ships additional npm-only patch +# releases. Resolve the latest patch in the same major.minor series. +setup_py_url="https://github.com/microsoft/playwright-python/raw/v${python_version}/setup.py" +python_driver_version=$(curl -fsSL "$setup_py_url" | grep '^driver_version =' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') +python_major_minor=$(major_minor "$python_driver_version") +resolve_driver_version_latest_patch() { + local mm_escaped + mm_escaped=${python_major_minor//./\\.} + curl -fsSL "https://registry.npmjs.org/playwright" \ + | jq -r '.versions | keys[]' \ + | grep -E "^${mm_escaped}\.[0-9]+$" \ + | sort -V \ + | tail -n1 +} +driver_version="$(resolve_driver_version_latest_patch)" +: "${driver_version:?failed to resolve driver version from npm for python major.minor ${python_major_minor}}" +: "${python_driver_version:?failed to resolve driver_version from ${setup_py_url}}" # TODO: skip if update-source-version reported the same version update-source-version playwright-driver "$driver_version" -update-source-version python3Packages.playwright "$version" - -driver_file="$root/driver.nix" -repo_url_prefix="https://github.com/microsoft/playwright/raw" +update-source-version python3Packages.playwright "$python_version" temp_dir=$(mktemp -d) trap 'rm -rf "$temp_dir"' EXIT # Update playwright-mcp package -mcp_version=$(curl ${GITHUB_TOKEN:+" -u \":$GITHUB_TOKEN\""} -s https://api.github.com/repos/microsoft/playwright-mcp/releases/latest | jq -r '.tag_name | sub("^v"; "")') +driver_major_minor=$(major_minor "$driver_version") +resolve_mcp_version() { + local releases_json + local tag_name + + releases_json=$(github_api_get "https://api.github.com/repos/microsoft/playwright-mcp/releases?per_page=100") + while IFS= read -r tag_name; do + local mcp_version_candidate mcp_npm_url mcp_playwright_dep mcp_major_minor + mcp_version_candidate=${tag_name#v} + mcp_npm_url="https://registry.npmjs.org/@playwright/mcp/${mcp_version_candidate}" + mcp_playwright_dep=$( + curl -fsSL "$mcp_npm_url" \ + | jq -r '.dependencies.playwright // .dependencies["playwright-core"] // empty' + ) || continue + mcp_major_minor=$(echo "$mcp_playwright_dep" | grep -Eo '[0-9]+\.[0-9]+' | head -n1 || true) + if [ "$mcp_major_minor" = "$driver_major_minor" ]; then + echo "$mcp_version_candidate" + return 0 + fi + done < <(echo "$releases_json" | jq -r '.[].tag_name') + return 1 +} +mcp_version="$(resolve_mcp_version)" || { + echo "Could not find a playwright-mcp release compatible with Playwright driver ${driver_version}" >&2 + exit 1 +} update-source-version playwright-mcp "$mcp_version" # Update npmDepsHash for playwright-mcp -pushd "$temp_dir" >/dev/null -curl -fsSL -o package-lock.json "https://raw.githubusercontent.com/microsoft/playwright-mcp/v${mcp_version}/package-lock.json" -mcp_npm_hash=$(prefetch-npm-deps package-lock.json) -rm -f package-lock.json -popd >/dev/null +mcp_lock_file="${temp_dir}/playwright-mcp-package-lock.json" +curl -fsSL -o "$mcp_lock_file" "https://raw.githubusercontent.com/microsoft/playwright-mcp/v${mcp_version}/package-lock.json" +mcp_npm_hash=$(prefetch-npm-deps "$mcp_lock_file") -mcp_package_file="$root/../../../by-name/pl/playwright-mcp/package.nix" -sed -E 's#\bnpmDepsHash = ".*?"#npmDepsHash = "'"$mcp_npm_hash"'"#' -i "$mcp_package_file" +sed -E -i 's#\bnpmDepsHash = "[^"]*"#npmDepsHash = "'"$mcp_npm_hash"'"#' "$playwright_mcp_package_file" # update binaries of browsers, used by playwright. replace_sha() { - sed -i "s|$2 = \".\{44,52\}\"|$2 = \"$3\"|" "$1" + local target_file="$1" + local attr_name="$2" + local new_hash="$3" + + sed -i "s|$attr_name = \".\{44,52\}\"|$attr_name = \"$new_hash\"|" "$target_file" } prefetch_browser() { - # nix-prefetch is used to obtain sha with `stripRoot = false` - # doesn't work on macOS https://github.com/msteen/nix-prefetch/issues/53 - nix-prefetch --option extra-experimental-features flakes -q "{ stdenv, fetchzip }: stdenv.mkDerivation { name=\"browser\"; src = fetchzip { url = \"$1\"; stripRoot = $2; }; }" + local url="$1" + local strip_root="$2" + + # nix-prefetch is used to obtain sha with `stripRoot = false` + # doesn't work on macOS https://github.com/msteen/nix-prefetch/issues/53 + nix-prefetch --option extra-experimental-features flakes -q "{ stdenv, fetchzip }: stdenv.mkDerivation { name=\"browser\"; src = fetchzip { url = \"$url\"; stripRoot = $strip_root; }; }" +} + +browser_download_info() { + local name="$1" + local revision="$2" + local browser_version="$3" + + nix-instantiate --eval --json --strict "$root/browser-downloads.nix" \ + --argstr name "$name" \ + --argstr revision "$revision" \ + --argstr browserVersion "$browser_version" } update_browser() { - name="$1" - platform="$2" - stripRoot="false" - if [ "$platform" = "darwin" ]; then - if [ "$name" = "webkit" ]; then - suffix="mac-14" - else - suffix="mac" - fi - else - if [ "$name" = "ffmpeg" ] || [ "$name" = "chromium-headless-shell" ]; then - suffix="linux" - elif [ "$name" = "chromium" ]; then - stripRoot="true" - suffix="linux" - elif [ "$name" = "firefox" ]; then - stripRoot="true" - suffix="ubuntu-22.04" - else - suffix="ubuntu-22.04" - fi - fi - aarch64_suffix="$suffix-arm64" - if [ "$name" = "chromium-headless-shell" ]; then - buildname="chromium"; - else - buildname="$name" - fi + local name="$1" + local revision + local browser_version + local download_info + local system + local url + local strip_root - revision="$(jq -r ".browsers[\"$buildname\"].revision" "$root/browsers.json")" - replace_sha "$root/$name.nix" "x86_64-$platform" \ - "$(prefetch_browser "https://playwright.azureedge.net/builds/$buildname/$revision/$name-$suffix.zip" $stripRoot)" - replace_sha "$root/$name.nix" "aarch64-$platform" \ - "$(prefetch_browser "https://playwright.azureedge.net/builds/$buildname/$revision/$name-$aarch64_suffix.zip" $stripRoot)" + revision="$(jq -r ".browsers[\"$name\"].revision" "$playwright_browsers_file")" + browser_version="$(jq -r ".browsers[\"$name\"].browserVersion // empty" "$playwright_browsers_file")" + download_info="$(browser_download_info "$name" "$revision" "$browser_version")" + + for system in "${browser_systems[@]}"; do + url="$(echo "$download_info" | jq -r --arg system "$system" '.[$system].url')" + strip_root="$(echo "$download_info" | jq -r --arg system "$system" '.[$system].stripRoot')" + replace_sha "$root/$name.nix" "$system" "$(prefetch_browser "$url" "$strip_root")" + done } -curl -fsSl \ +curl -fsSL \ "https://raw.githubusercontent.com/microsoft/playwright/v${driver_version}/packages/playwright-core/browsers.json" \ | jq ' .comment = "This file is kept up to date via update.sh" @@ -94,46 +150,34 @@ curl -fsSl \ | map({(.name): . | del(.name)}) | add ) - ' > "$root/browsers.json" + ' > "$playwright_browsers_file" -update_browser "chromium" "linux" -update_browser "chromium-headless-shell" "linux" -update_browser "firefox" "linux" -update_browser "webkit" "linux" -update_browser "ffmpeg" "linux" - -update_browser "chromium" "darwin" -update_browser "chromium-headless-shell" "darwin" -update_browser "firefox" "darwin" -update_browser "webkit" "darwin" -update_browser "ffmpeg" "darwin" +for browser in "${browser_names[@]}"; do + update_browser "$browser" +done # Update package-lock.json files for all npm deps that are built in playwright -# Function to download `package-lock.json` for a given source path and update hash +# Download `package-lock.json` for a given sourceRoot path and update its hash. update_hash() { local source_root_path="$1" - local existing_hash="$2" - - # Formulate download URL - local download_url="${repo_url_prefix}/v${driver_version}${source_root_path}/package-lock.json" - # Download package-lock.json to temporary directory - curl -fsSL -o "${temp_dir}/package-lock.json" "$download_url" - - # Calculate the new hash + local download_url + local lock_file local new_hash - new_hash=$(prefetch-npm-deps "${temp_dir}/package-lock.json") + local source_root_pattern - # Update npmDepsHash in the original file - sed -i "s|$existing_hash|${new_hash}|" "$driver_file" + download_url="${playwright_raw_repo_url}/v${driver_version}${source_root_path}/package-lock.json" + lock_file="${temp_dir}/$(echo "$source_root_path" | tr '/.' '__').package-lock.json" + curl -fsSL -o "$lock_file" "$download_url" + new_hash=$(prefetch-npm-deps "$lock_file") + + source_root_pattern=$(printf '%s\n' "$source_root_path" | sed 's/[][\\/.*^$+?(){}|]/\\&/g') + sed -E -i "/sourceRoot = \"\\\$\\{src.name\\}${source_root_pattern}\";/,/npmDepsHash = / s#npmDepsHash = \"[^\"]*\";#npmDepsHash = \"${new_hash}\";#" "$playwright_driver_file" } -while IFS= read -r source_root_line; do - [[ "$source_root_line" =~ sourceRoot ]] || continue - source_root_path=$(echo "$source_root_line" | sed -e 's/^.*"${src.name}\(.*\)";.*$/\1/') - # Extract the current npmDepsHash for this sourceRoot - existing_hash=$(grep -A1 "$source_root_line" "$driver_file" | grep 'npmDepsHash' | sed -e 's/^.*npmDepsHash = "\(.*\)";$/\1/') - - # Call the function to download and update the hash - update_hash "$source_root_path" "$existing_hash" -done < "$driver_file" +while IFS= read -r source_root_path; do + update_hash "$source_root_path" +done < <( + # shellcheck disable=SC2016 + sed -n 's#^[[:space:]]*sourceRoot = "${src.name}\(.*\)";.*$#\1#p' "$playwright_driver_file" +) diff --git a/pkgs/development/web/playwright/webkit.nix b/pkgs/development/web/playwright/webkit.nix index f818618295d4..6ba1f52cb216 100644 --- a/pkgs/development/web/playwright/webkit.nix +++ b/pkgs/development/web/playwright/webkit.nix @@ -46,19 +46,16 @@ wayland-scanner, woff2, zlib, - suffix, revision, system, throwSystem, }: let - suffix' = - if lib.hasPrefix "linux" suffix then - "ubuntu-22.04" + (lib.removePrefix "linux" suffix) - else if lib.hasPrefix "mac" suffix then - "mac-14" + (lib.removePrefix "mac" suffix) - else - suffix; + download = + (import ./browser-downloads.nix { + name = "webkit"; + inherit revision; + }).${system} or throwSystem; libvpx' = libvpx.overrideAttrs ( finalAttrs: previousAttrs: { version = "1.12.0"; @@ -133,12 +130,11 @@ let webkit-linux = stdenv.mkDerivation { name = "playwright-webkit"; src = fetchzip { - url = "https://playwright.azureedge.net/builds/webkit/${revision}/webkit-${suffix'}.zip"; - stripRoot = false; + inherit (download) url stripRoot; hash = { - x86_64-linux = "sha256-wSMObGeDoy1vW7TO20wL3vWOSpcDgFciK00SqZ15EjM="; - aarch64-linux = "sha256-IyZNAcAUxDpCgc/I2dWtIoRqKiZJ1ggmYqqdvUKg6IE="; + x86_64-linux = "sha256-h9dM6RR5WhUPHdZgn5yiJlM7QrhlDVaivnaHQCEZbXQ="; + aarch64-linux = "sha256-sOtPdOBmPLqApGGVhgH51OW3aoTv1Y40RfCSax7CyC4="; } .${system} or throwSystem; }; @@ -214,12 +210,11 @@ let ''; }; webkit-darwin = fetchzip { - url = "https://playwright.azureedge.net/builds/webkit/${revision}/webkit-${suffix'}.zip"; - stripRoot = false; + inherit (download) url stripRoot; hash = { - x86_64-darwin = "sha256-a8NKlNhKPEhtWREAgNosvTLE+zphmKYW+CtvW5jwFlQ="; - aarch64-darwin = "sha256-DvVTVAxuh4we2278xZFBomFAxcuqyEOCK7mPg3rnYpU="; + x86_64-darwin = "sha256-An3sdw8HltgHQ6YASsxyhoK1fd8PxZ0BBCMpnOORkv8="; + aarch64-darwin = "sha256-suXPCuXLMz3xoFxE5+Yjd9OXxLfNDDJiU6O1Ic0PsOI="; } .${system} or throwSystem; };