nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1#!/usr/bin/env nix-shell
2#!nix-shell -i bash -p curl gnused common-updater-scripts jq prefetch-npm-deps unzip nix-prefetch
3# shellcheck shell=bash
4set -euo pipefail
5
6root="$(dirname "$(readlink -f "$0")")"
7repo_root="$(git -C "$root" rev-parse --show-toplevel)"
8cd "$repo_root"
9
10github_api_curl_args=()
11if [ -n "${GITHUB_TOKEN:-}" ]; then
12 github_api_curl_args=(-u ":$GITHUB_TOKEN")
13fi
14
15playwright_browsers_file="$root/browsers.json"
16playwright_driver_file="$root/driver.nix"
17playwright_raw_repo_url="https://raw.githubusercontent.com/microsoft/playwright"
18playwright_mcp_package_file="$root/../../../by-name/pl/playwright-mcp/package.nix"
19browser_names=(chromium chromium-headless-shell firefox webkit ffmpeg)
20browser_systems=(x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin)
21
22github_api_get() {
23 curl "${github_api_curl_args[@]}" -fsSL "$1"
24}
25
26major_minor() {
27 echo "${1%.*}"
28}
29
30python_version=$(github_api_get https://api.github.com/repos/microsoft/playwright-python/releases/latest | jq -r '.tag_name | sub("^v"; "")')
31# Most of the time, this should be the latest stable release of the Node-based
32# Playwright version, but upstream occasionally ships additional npm-only patch
33# releases. Resolve the latest patch in the same major.minor series.
34setup_py_url="https://github.com/microsoft/playwright-python/raw/v${python_version}/setup.py"
35python_driver_version=$(curl -fsSL "$setup_py_url" | grep '^driver_version =' | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')
36python_major_minor=$(major_minor "$python_driver_version")
37resolve_driver_version_latest_patch() {
38 local mm_escaped
39 mm_escaped=${python_major_minor//./\\.}
40 curl -fsSL "https://registry.npmjs.org/playwright" \
41 | jq -r '.versions | keys[]' \
42 | grep -E "^${mm_escaped}\.[0-9]+$" \
43 | sort -V \
44 | tail -n1
45}
46driver_version="$(resolve_driver_version_latest_patch)"
47: "${driver_version:?failed to resolve driver version from npm for python major.minor ${python_major_minor}}"
48: "${python_driver_version:?failed to resolve driver_version from ${setup_py_url}}"
49
50# TODO: skip if update-source-version reported the same version
51update-source-version playwright-driver "$driver_version"
52update-source-version python3Packages.playwright "$python_version"
53
54temp_dir=$(mktemp -d)
55trap 'rm -rf "$temp_dir"' EXIT
56
57# Update playwright-mcp package
58driver_major_minor=$(major_minor "$driver_version")
59resolve_mcp_version() {
60 local releases_json
61 local tag_name
62
63 releases_json=$(github_api_get "https://api.github.com/repos/microsoft/playwright-mcp/releases?per_page=100")
64 while IFS= read -r tag_name; do
65 local mcp_version_candidate mcp_npm_url mcp_playwright_dep mcp_major_minor
66 mcp_version_candidate=${tag_name#v}
67 mcp_npm_url="https://registry.npmjs.org/@playwright/mcp/${mcp_version_candidate}"
68 mcp_playwright_dep=$(
69 curl -fsSL "$mcp_npm_url" \
70 | jq -r '.dependencies.playwright // .dependencies["playwright-core"] // empty'
71 ) || continue
72 mcp_major_minor=$(echo "$mcp_playwright_dep" | grep -Eo '[0-9]+\.[0-9]+' | head -n1 || true)
73 if [ "$mcp_major_minor" = "$driver_major_minor" ]; then
74 echo "$mcp_version_candidate"
75 return 0
76 fi
77 done < <(echo "$releases_json" | jq -r '.[].tag_name')
78 return 1
79}
80mcp_version="$(resolve_mcp_version)" || {
81 echo "Could not find a playwright-mcp release compatible with Playwright driver ${driver_version}" >&2
82 exit 1
83}
84update-source-version playwright-mcp "$mcp_version"
85
86# Update npmDepsHash for playwright-mcp
87mcp_lock_file="${temp_dir}/playwright-mcp-package-lock.json"
88curl -fsSL -o "$mcp_lock_file" "https://raw.githubusercontent.com/microsoft/playwright-mcp/v${mcp_version}/package-lock.json"
89mcp_npm_hash=$(prefetch-npm-deps "$mcp_lock_file")
90
91sed -E -i 's#\bnpmDepsHash = "[^"]*"#npmDepsHash = "'"$mcp_npm_hash"'"#' "$playwright_mcp_package_file"
92
93
94# update binaries of browsers, used by playwright.
95replace_sha() {
96 local target_file="$1"
97 local attr_name="$2"
98 local new_hash="$3"
99
100 sed -i "s|$attr_name = \".\{44,52\}\"|$attr_name = \"$new_hash\"|" "$target_file"
101}
102
103prefetch_browser() {
104 local url="$1"
105 local strip_root="$2"
106
107 # nix-prefetch is used to obtain sha with `stripRoot = false`
108 # doesn't work on macOS https://github.com/msteen/nix-prefetch/issues/53
109 nix-prefetch --option extra-experimental-features flakes -q "{ stdenv, fetchzip }: stdenv.mkDerivation { name=\"browser\"; src = fetchzip { url = \"$url\"; stripRoot = $strip_root; }; }"
110}
111
112browser_download_info() {
113 local name="$1"
114 local revision="$2"
115 local browser_version="$3"
116
117 nix-instantiate --eval --json --strict "$root/browser-downloads.nix" \
118 --argstr name "$name" \
119 --argstr revision "$revision" \
120 --argstr browserVersion "$browser_version"
121}
122
123update_browser() {
124 local name="$1"
125 local revision
126 local browser_version
127 local download_info
128 local system
129 local url
130 local strip_root
131
132 revision="$(jq -r ".browsers[\"$name\"].revision" "$playwright_browsers_file")"
133 browser_version="$(jq -r ".browsers[\"$name\"].browserVersion // empty" "$playwright_browsers_file")"
134 download_info="$(browser_download_info "$name" "$revision" "$browser_version")"
135
136 for system in "${browser_systems[@]}"; do
137 url="$(echo "$download_info" | jq -r --arg system "$system" '.[$system].url')"
138 strip_root="$(echo "$download_info" | jq -r --arg system "$system" '.[$system].stripRoot')"
139 replace_sha "$root/$name.nix" "$system" "$(prefetch_browser "$url" "$strip_root")"
140 done
141}
142
143curl -fsSL \
144 "https://raw.githubusercontent.com/microsoft/playwright/v${driver_version}/packages/playwright-core/browsers.json" \
145 | jq '
146 .comment = "This file is kept up to date via update.sh"
147 | .browsers |= (
148 [.[]
149 | select(.installByDefault) | del(.installByDefault)]
150 | map({(.name): . | del(.name)})
151 | add
152 )
153 ' > "$playwright_browsers_file"
154
155for browser in "${browser_names[@]}"; do
156 update_browser "$browser"
157done
158
159# Update package-lock.json files for all npm deps that are built in playwright
160
161# Download `package-lock.json` for a given sourceRoot path and update its hash.
162update_hash() {
163 local source_root_path="$1"
164 local download_url
165 local lock_file
166 local new_hash
167 local source_root_pattern
168
169 download_url="${playwright_raw_repo_url}/v${driver_version}${source_root_path}/package-lock.json"
170 lock_file="${temp_dir}/$(echo "$source_root_path" | tr '/.' '__').package-lock.json"
171 curl -fsSL -o "$lock_file" "$download_url"
172 new_hash=$(prefetch-npm-deps "$lock_file")
173
174 source_root_pattern=$(printf '%s\n' "$source_root_path" | sed 's/[][\\/.*^$+?(){}|]/\\&/g')
175 sed -E -i "/sourceRoot = \"\\\$\\{src.name\\}${source_root_pattern}\";/,/npmDepsHash = / s#npmDepsHash = \"[^\"]*\";#npmDepsHash = \"${new_hash}\";#" "$playwright_driver_file"
176}
177
178while IFS= read -r source_root_path; do
179 update_hash "$source_root_path"
180done < <(
181 # shellcheck disable=SC2016
182 sed -n 's#^[[:space:]]*sourceRoot = "${src.name}\(.*\)";.*$#\1#p' "$playwright_driver_file"
183)