From fbe1f7b0034b757117fa4a2d84aee3104d97d397 Mon Sep 17 00:00:00 2001 From: Tyler Breisacher Date: Wed, 11 Feb 2026 11:49:55 -0800 Subject: [PATCH 1/9] Run the rules_playwright tests on CI --- .bazelignore | 2 +- .github/workflows/tests.yml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.bazelignore b/.bazelignore index 3f2d04e..31286f4 100644 --- a/.bazelignore +++ b/.bazelignore @@ -1,4 +1,4 @@ target/ node_modules/ -examples/rules_js +examples/ gen/ \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8cbd7db..f84ffe7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,6 +27,9 @@ jobs: - name: Install Playwright system dependencies run: npx playwright install-deps + - name: Build and Test rules_playwright + run: bazelisk test //... + - name: Build and Test WORKSPACE example working-directory: examples/workspace run: bazelisk test //... From c4e6dc49b0a545640fa6f1698b0f0455a993be84 Mon Sep 17 00:00:00 2001 From: Tyler Breisacher Date: Wed, 11 Feb 2026 13:18:46 -0800 Subject: [PATCH 2/9] Switch to Bazel 8 --- .bazelversion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelversion b/.bazelversion index e0741a8..f9c71a5 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -8.5.1 \ No newline at end of file +8.5.1 From 4b8e52203335060681c7820846bfd3c8b56f152e Mon Sep 17 00:00:00 2001 From: Ben King <9087625+benfdking@users.noreply.github.com> Date: Sun, 17 May 2026 19:25:35 +0100 Subject: [PATCH 3/9] Pin .bazelversion to 9.1.0 --- .bazelversion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelversion b/.bazelversion index f9c71a5..47da986 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -8.5.1 +9.1.0 From 48f58f1bada2aaa2c13041e3deabb9c52d03ad2a Mon Sep 17 00:00:00 2001 From: Ben King <9087625+benfdking@users.noreply.github.com> Date: Sun, 17 May 2026 19:41:04 +0100 Subject: [PATCH 4/9] refactor: replace Rust CLI with Starlark implementation --- Cargo.lock | 810 ------------------ Cargo.toml | 14 - MODULE.bazel | 69 -- docs/starlark_no_rust_spike.md | 109 +++ playwright/BUILD.bazel | 2 + playwright/extensions.bzl | 32 +- playwright/private/BUILD.bazel | 9 + playwright/private/browser_targets.bzl | 196 +++++ playwright/private/cli/BUILD.bazel | 16 +- playwright/private/cli/Cargo.toml | 16 - playwright/private/cli/src/browser_targets.rs | 146 ---- playwright/private/cli/src/browsers.rs | 22 - playwright/private/cli/src/download_paths.rs | 85 -- playwright/private/cli/src/extract_zip.rs | 14 - playwright/private/cli/src/integrity_map.rs | 59 -- playwright/private/cli/src/main.rs | 71 -- playwright/private/cli/src/platform_groups.rs | 63 -- .../private/cli/src/templates/aliases.rs | 76 -- .../private/cli/src/templates/browsers.rs | 46 - playwright/private/cli/src/templates/mod.rs | 65 -- playwright/private/cli/src/templates/root.rs | 81 -- playwright/private/integrity_map.bzl | 57 +- playwright/private/unzip_browser.bzl | 16 +- playwright/private/util.bzl | 42 - playwright/repositories.bzl | 59 +- tools/release/BUILD.bazel | 69 +- tools/release/defs.bzl | 71 +- 27 files changed, 398 insertions(+), 1917 deletions(-) delete mode 100644 Cargo.lock delete mode 100644 Cargo.toml create mode 100644 docs/starlark_no_rust_spike.md create mode 100644 playwright/private/browser_targets.bzl delete mode 100644 playwright/private/cli/Cargo.toml delete mode 100644 playwright/private/cli/src/browser_targets.rs delete mode 100644 playwright/private/cli/src/browsers.rs delete mode 100644 playwright/private/cli/src/download_paths.rs delete mode 100644 playwright/private/cli/src/extract_zip.rs delete mode 100644 playwright/private/cli/src/integrity_map.rs delete mode 100644 playwright/private/cli/src/main.rs delete mode 100644 playwright/private/cli/src/platform_groups.rs delete mode 100644 playwright/private/cli/src/templates/aliases.rs delete mode 100644 playwright/private/cli/src/templates/browsers.rs delete mode 100644 playwright/private/cli/src/templates/mod.rs delete mode 100644 playwright/private/cli/src/templates/root.rs diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index f2fe395..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,810 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "askama" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" -dependencies = [ - "askama_derive", - "askama_escape", -] - -[[package]] -name = "askama_derive" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83" -dependencies = [ - "askama_parser", - "mime", - "mime_guess", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "askama_escape" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" - -[[package]] -name = "askama_parser" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0" -dependencies = [ - "nom", -] - -[[package]] -name = "bitflags" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bzip2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" -dependencies = [ - "bzip2-sys", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" -dependencies = [ - "cc", - "pkg-config", -] - -[[package]] -name = "cc" -version = "1.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" -dependencies = [ - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "cli" -version = "0.1.0" -dependencies = [ - "askama", - "serde", - "serde_json", - "sha2", - "xflags", - "zip", -] - -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "deflate64" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" - -[[package]] -name = "deranged" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "flate2" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "indexmap" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "generic-array", -] - -[[package]] -name = "itoa" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" - -[[package]] -name = "jobserver" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" -dependencies = [ - "getrandom", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "libc" -version = "0.2.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" - -[[package]] -name = "lockfree-object-pool" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" - -[[package]] -name = "log" -version = "0.4.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" - -[[package]] -name = "lzma-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" -dependencies = [ - "byteorder", - "crc", -] - -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" -dependencies = [ - "adler2", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "proc-macro2" -version = "1.0.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "serde" -version = "1.0.217" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.217" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.137" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.96" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "time" -version = "0.3.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" - -[[package]] -name = "typenum" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" - -[[package]] -name = "unicase" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" - -[[package]] -name = "unicode-ident" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" -dependencies = [ - "wit-bindgen-rt", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] - -[[package]] -name = "xflags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d9e15fbb3de55454b0106e314b28e671279009b363e6f1d8e39fdc3bf048944" -dependencies = [ - "xflags-macros", -] - -[[package]] -name = "xflags-macros" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "672423d4fea7ffa2f6c25ba60031ea13dc6258070556f125cc4d790007d4a155" - -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zip" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "febbe83a485467affa75a75d28dc7494acd2f819e549536c47d46b3089b56164" -dependencies = [ - "aes", - "arbitrary", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "deflate64", - "flate2", - "getrandom", - "hmac", - "indexmap", - "lzma-rs", - "memchr", - "pbkdf2", - "sha1", - "time", - "xz2", - "zeroize", - "zopfli", - "zstd", -] - -[[package]] -name = "zopfli" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" -dependencies = [ - "bumpalo", - "crc32fast", - "lockfree-object-pool", - "log", - "once_cell", - "simd-adler32", -] - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 631cf47..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[workspace] -resolver = "2" -members = ["playwright/private/cli"] - -[workspace.package] -edition = "2021" -rust-version = "1.84.0" -readme = "README.md" - -[profile.release] -opt-level = "z" # Optimize for size -strip = true # Automatically strip debug symbols from the binary -lto = true # Enable Link Time Optimization (LTO) -codegen-units = 1 # Reduce Parallel Code Generation Units to Increase Optimization diff --git a/MODULE.bazel b/MODULE.bazel index f442b8c..ddbe1a5 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -36,72 +36,3 @@ register_toolchains( ) bazel_dep(name = "buildifier_prebuilt", version = "6.1.2", dev_dependency = True) -bazel_dep( - name = "rules_rust", - version = "0.59.2", - dev_dependency = True, -) - -rust = use_extension( - "@rules_rust//rust:extensions.bzl", - "rust", - dev_dependency = True, -) -rust.toolchain( - edition = "2021", - extra_target_triples = { - "x86_64-apple-darwin": [ - "@platforms//cpu:x86_64", - "@platforms//os:macos", - ], - "aarch64-apple-darwin": [ - "@platforms//cpu:arm64", - "@platforms//os:macos", - ], - "aarch64-unknown-linux-musl": [ - "@platforms//cpu:arm64", - "@platforms//os:linux", - "@//tools/linkers:musl", - ], - "x86_64-unknown-linux-musl": [ - "@platforms//cpu:x86_64", - "@platforms//os:linux", - "@//tools/linkers:musl", - ], - }, - sha256s = { - "rustc-1.84.0-aarch64-apple-darwin.tar.xz": "7e2a1594c1e174f28b84adab25e96699feb8d0455dabe8dac1b2850945b400b1", - "clippy-1.84.0-aarch64-apple-darwin.tar.xz": "5317d98dc9d6994f8d087aabecc16627d1a202500acd09f752d722f5462587df", - "cargo-1.84.0-aarch64-apple-darwin.tar.xz": "0dc1cf2510c692f0d2ac254bf8474b3f96a719eeb5062b16dceb30361c0d235e", - "llvm-tools-1.84.0-aarch64-apple-darwin.tar.xz": "2600f7f19064157b3b4108a852a311c29bbe3353d1f24b284cc942b89693f3f6", - "rust-std-1.84.0-aarch64-apple-darwin.tar.xz": "c3ec73225e0f0228e4a8a9c650195b3f9e63f839954e65509d47ea58316f8f5d", - }, - versions = ["1.84.0"], -) -use_repo(rust, "rust_toolchains") - -register_toolchains( - "@rust_toolchains//:all", - dev_dependency = True, -) - -crate = use_extension( - "@rules_rust//crate_universe:extension.bzl", - "crate", - dev_dependency = True, -) -crate.from_cargo( - name = "crate_index", - cargo_lockfile = "//:Cargo.lock", - manifests = [ - "//:Cargo.toml", - "//playwright/private/cli:Cargo.toml", - ], - supported_platform_triples = [ - "x86_64-apple-darwin", - "aarch64-apple-darwin", - "aarch64-unknown-linux-gnu", - "x86_64-unknown-linux-gnu", - ], -) -use_repo(crate, "crate_index") diff --git a/docs/starlark_no_rust_spike.md b/docs/starlark_no_rust_spike.md new file mode 100644 index 0000000..f4e6aa4 --- /dev/null +++ b/docs/starlark_no_rust_spike.md @@ -0,0 +1,109 @@ +# Spike: Remove Rust CLI and implement in Starlark + +## Question +Can `rules_playwright` remove the Rust CLI and run fully in Starlark? + +## Short answer +Yes, with moderate refactor cost. There are no fundamental blockers. + +## What Rust does today +The Rust binary (used via `//tools/release:cli`) provides 4 subcommands: + +1. `workspace` +- Reads `browsers.json` + embedded `download_paths.json` +- Expands browser/platform matrix +- Generates repository BUILD files (`BUILD.bazel`, `aliases/BUILD.bazel`, `browsers/BUILD.bazel`) + +2. `http-files` +- Computes `{name, path}` entries for `http_file(...)` repositories + +3. `unzip` +- Extracts browser zip archive to output tree artifact + +4. `integrity-map` +- Computes SHA256 for browser archives and writes JSON map + +## Where Rust is wired in +- `playwright/extensions.bzl` calls CLI `http-files` +- `playwright/repositories.bzl` calls CLI `workspace` and `http-files` +- `playwright/private/unzip_browser.bzl` calls CLI `unzip` +- `playwright/private/integrity_map.bzl` calls CLI `integrity-map` + +## Feasibility by feature + +### 1) Replace `workspace` and `http-files` with pure Starlark +Feasible. + +Repository/module extension code already has everything needed: +- `json.decode` for `browsers.json` and `download_paths.json` +- string formatting/replacement for URL templates +- `ctx.file` / `module_ctx.file` to generate BUILD files + +Implementation approach: +- Add a Starlark helper module (e.g. `playwright/private/browser_targets.bzl`) implementing the same target expansion logic currently in `browser_targets.rs` +- Read `download_paths.json` as a normal source file instead of embedding it in a binary +- Return the browser/path structures directly in Starlark +- Write repository files directly from Starlark templates + +### 2) Replace `unzip` rule implementation +Feasible. + +Current Rust unzip can be replaced with: +- `ctx.actions.run_shell` calling `unzip` (less hermetic), or +- Bazel-provided zipper tool (preferred), if wired as an executable tool dependency. + +This is a straightforward rule rewrite. + +### 3) Replace `integrity-map` +Feasible. + +Can be rewritten as: +- `ctx.actions.run_shell` using `sha256sum`/`shasum` and JSON assembly, or +- small hermetic helper script/tool. + +This is the least elegant part in pure Starlark because hashing happens in an action, but it does not require Rust specifically. + +## Main risks/tradeoffs + +1. Re-implementing matrix logic correctly +- Must preserve behavior around: + - `-headless-shell` expansion + - revision overrides per platform + - platform aliases/groups + - deterministic sorting + +2. Hermeticity for unzip/hash actions +- Need to choose tools carefully to avoid host-dependent behavior. + +3. Cross-platform stability +- Existing Rust path is already cross-platform for these operations. +- New shell-based actions need explicit portability handling. + +## Benefits of removing Rust + +- Simpler contributor setup (no Rust toolchain/crate universe for this repo) +- Smaller release artifact surface (no prebuilt CLI binaries) +- Less moving parts in module lock stability across platforms + +## Costs + +- One-time refactor of repository generation logic and action tooling +- Need strong regression tests for generated targets and labels + +## Recommended migration path + +1. Phase 1 (low risk): +- Rewrite `workspace` + `http-files` in Starlark +- Keep Rust only for `unzip` and `integrity-map` + +2. Phase 2: +- Replace `unzip` with Starlark action using hermetic unzip tool + +3. Phase 3: +- Replace `integrity-map` hashing action + +4. Phase 4: +- Remove Rust module/toolchain, CLI sources, and prebuilt artifacts + +## Verdict +A fully Starlark implementation is practical and likely worth it if this fork is intended to be the long-term canonical version. The safest way is incremental replacement with parity tests after each phase. diff --git a/playwright/BUILD.bazel b/playwright/BUILD.bazel index 1953a7e..446bbee 100644 --- a/playwright/BUILD.bazel +++ b/playwright/BUILD.bazel @@ -38,6 +38,7 @@ bzl_library( ], deps = [ ":repositories", + "//playwright/private:browser_targets", "@bazel_tools//tools/build_defs/repo:cache.bzl", ], ) @@ -56,6 +57,7 @@ bzl_library( "//docs:__subpackages__", ], deps = [ + "//playwright/private:browser_targets", "//playwright/private:known_browsers", "//playwright/private:util", "@bazel_tools//tools/build_defs/repo:http.bzl", diff --git a/playwright/extensions.bzl b/playwright/extensions.bzl index ec5a1b8..b8910ec 100644 --- a/playwright/extensions.bzl +++ b/playwright/extensions.bzl @@ -11,8 +11,9 @@ effectively overriding the default named toolchain due to toolchain resolution p """ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") +load("//playwright/private:browser_targets.bzl", "compute_browser_targets") load("//playwright/private:known_browsers.bzl", "KNOWN_BROWSER_INTEGRITY") -load("//playwright/private:util.bzl", "get_all_cli_paths", "get_browsers_json_path", "get_cli_path") +load("//playwright/private:util.bzl", "get_browsers_json_path") load(":repositories.bzl", "playwright_repository") _DEFAULT_NAME = "playwright" @@ -58,30 +59,13 @@ def _extension_impl(module_ctx): One of playwright_version or browsers_json must be specified. """) - cli = get_cli_path(module_ctx) + browsers_json_path = get_browsers_json_path(module_ctx, repo.playwright_version, repo.browsers_json) + download_paths_json = module_ctx.read(Label("//playwright/private/cli:src/download_paths.json")) + browser_targets = compute_browser_targets(repo.name, module_ctx.read(browsers_json_path), download_paths_json) - # Watch all CLI binaries to ensure MODULE.bazel.lock remains consistent - # across platforms and detects changes when binaries are updated - for cli_path in get_all_cli_paths(module_ctx): - module_ctx.watch(cli_path) - - # Step 1: use module_ctx exec to get the list of browsers to iterate over and declare with http file - result = module_ctx.execute( - [ - cli, - "http-files", - "--browser-json-path", - get_browsers_json_path(module_ctx, repo.playwright_version, repo.browsers_json), - "--browsers-workspace-name-prefix", - repo.name, - ], - ) - if result.return_code != 0: - fail("http-files command failed", result.stdout, result.stderr) - - for http_file_json in json.decode(result.stdout): - browser_name = http_file_json["name"] - path = http_file_json["path"] + for browser_target in browser_targets: + browser_name = browser_target["http_file_workspace_name"] + path = browser_target["http_file_path"] integrity = repo.integrity_map.get(browser_name, None) if not integrity: integrity = repo.integrity_path_map.get(path, None) diff --git a/playwright/private/BUILD.bazel b/playwright/private/BUILD.bazel index 6b5efc8..1427dac 100644 --- a/playwright/private/BUILD.bazel +++ b/playwright/private/BUILD.bazel @@ -42,6 +42,15 @@ bzl_library( ], ) +bzl_library( + name = "browser_targets", + srcs = ["browser_targets.bzl"], + visibility = [ + "//docs:__subpackages__", + "//playwright:__subpackages__", + ], +) + bzl_library( name = "select_exec", srcs = ["select_exec.bzl"], diff --git a/playwright/private/browser_targets.bzl b/playwright/private/browser_targets.bzl new file mode 100644 index 0000000..9656f75 --- /dev/null +++ b/playwright/private/browser_targets.bzl @@ -0,0 +1,196 @@ +"""Shared browser target computation for repositories and module extensions.""" + +def _platform_group(platform): + if platform.endswith("-arm64"): + if platform.startswith("mac"): + return "macos_arm64" + return "linux_arm64" + if platform.startswith("mac"): + return "macos_x86_64" + return "linux_x86_64" + +def _base_platform(platform): + if platform.startswith("ubuntu18.04"): + return "ubuntu18.04" + if platform.startswith("ubuntu20.04"): + return "ubuntu20.04" + if platform.startswith("ubuntu22.04"): + return "ubuntu22.04" + if platform.startswith("ubuntu24.04"): + return "ubuntu24.04" + if platform.startswith("debian11"): + return "debian11" + if platform.startswith("debian12"): + return "debian12" + if platform.startswith("debian13"): + return "debian13" + if platform.startswith("mac10.13"): + return "mac10.13" + if platform.startswith("mac10.14"): + return "mac10.14" + if platform.startswith("mac10.15"): + return "mac10.15" + if platform.startswith("mac11"): + return "mac11" + if platform.startswith("mac12"): + return "mac12" + if platform.startswith("mac13"): + return "mac13" + if platform.startswith("mac14"): + return "mac14" + if platform.startswith("mac15"): + return "mac15" + return None + +def _expand_browsers(browsers): + has_headless = False + for browser in browsers: + if browser["name"].endswith("-headless-shell"): + has_headless = True + break + + expanded = [] + for browser in browsers: + expanded.append(browser) + if has_headless: + continue + if browser["name"] in ["chromium", "chromium-tip-of-tree"]: + copy = dict(browser) + copy["name"] = "{}-headless-shell".format(browser["name"]) + expanded.append(copy) + return expanded + +def compute_browser_targets(browsers_workspace_name_prefix, browsers_json, download_paths_json): + """Computes browser target metadata used by repositories and module extension.""" + download_paths = json.decode(download_paths_json) + decoded = json.decode(browsers_json) + browsers = _expand_browsers(decoded["browsers"]) + + targets = [] + for browser in browsers: + browser_name = browser["name"] + if browser_name not in download_paths: + continue + + revision = browser["revision"] + revision_overrides = browser.get("revisionOverrides", {}) + browser_version = browser.get("browserVersion", "") + + for platform, template in download_paths[browser_name].items(): + if platform == "" or platform.startswith("win") or template == None: + continue + + current_revision = revision_overrides.get(platform, revision) + has_revision_override = platform in revision_overrides + snake_case_browser_name = browser_name.replace("-", "_") + browser_directory_prefix = snake_case_browser_name + if has_revision_override: + browser_directory_prefix = "{}_{}_special".format(snake_case_browser_name, platform) + + path = template.replace("{revision}", current_revision) + path = path.replace("{browserVersion}", browser_version) + + targets.append({ + "http_file_workspace_name": "{}-{}-{}".format(browsers_workspace_name_prefix, browser_name, platform), + "http_file_path": path, + "label": "{}-{}".format(browser_name, platform), + "output_dir": "{}/{}-{}".format(platform, browser_directory_prefix, revision), + "browser": browser_name, + "platform": platform, + }) + + targets = sorted(targets, key = lambda t: t["label"]) + return targets + +def render_workspace_files(browser_targets, rules_playwright_cannonical_name): + """Renders workspace BUILD file contents.""" + browsers_lines = [ + "load(\"@{}//playwright:defs.bzl\", \"unzip_browser\")".format(rules_playwright_cannonical_name), + "", + "package(default_visibility = [\"//visibility:public\"])", + "", + ] + for target in browser_targets: + browsers_lines.extend([ + "unzip_browser(", + " name = \"{}\",".format(target["label"]), + " browser = \"@{}//file\",".format(target["http_file_workspace_name"]), + " output_dir = \"{}\",".format(target["output_dir"]), + " http_file_path = \"{}\",".format(target["http_file_path"]), + ")", + "", + ]) + + root_targets = {} + for target in browser_targets: + browser = target["browser"] + if browser not in root_targets: + root_targets[browser] = {} + group = _platform_group(target["platform"]) + if group not in root_targets[browser]: + root_targets[browser][group] = {} + root_targets[browser][group][target["platform"]] = target["label"] + + root_lines = [ + "load(\"@{}//playwright:defs.bzl\", \"select_exec\")".format(rules_playwright_cannonical_name), + "", + "package(default_visibility = [\"//visibility:public\"])", + "", + ] + aliases_lines = [ + "package(default_visibility = [\"//visibility:public\"])", + "", + ] + + for browser in sorted(root_targets.keys()): + groups = root_targets[browser] + root_lines.extend([ + "select_exec(", + " name = \"{}\",".format(browser), + " src = select({", + ]) + for group in sorted(groups.keys()): + root_lines.append( + " \"@{}//tools/platforms:{}\": \"//aliases:{}_{}\",".format( + rules_playwright_cannonical_name, + group, + browser, + group, + ), + ) + root_lines.extend([ + " }),", + ")", + "", + ]) + + for group in sorted(groups.keys()): + aliases_lines.extend([ + "alias(", + " name = \"{}_{}\",".format(browser, group), + " actual = select({", + ]) + platforms = groups[group] + for platform in sorted(platforms.keys()): + base = _base_platform(platform) + if base == None: + continue + aliases_lines.append( + " \"@{}//tools/platforms:{}\": \"//browsers:{}\",".format( + rules_playwright_cannonical_name, + base, + platforms[platform], + ), + ) + aliases_lines.extend([ + " }),", + ")", + "", + ]) + + return { + "browsers/BUILD.bazel": "\n".join(browsers_lines).strip() + "\n", + "BUILD.bazel": "\n".join(root_lines).strip() + "\n", + "aliases/BUILD.bazel": "\n".join(aliases_lines).strip() + "\n", + } + diff --git a/playwright/private/cli/BUILD.bazel b/playwright/private/cli/BUILD.bazel index 1653a61..2882ded 100644 --- a/playwright/private/cli/BUILD.bazel +++ b/playwright/private/cli/BUILD.bazel @@ -1,17 +1,3 @@ -load("@crate_index//:defs.bzl", "all_crate_deps") -load("//tools/release:defs.bzl", "rust_binary") - package(default_visibility = ["//visibility:public"]) -rust_binary( - name = "cli", - srcs = glob([ - "src/**/*.rs", - ]), - compile_data = [ - "src/download_paths.json", - ], - deps = all_crate_deps( - normal = True, - ), -) +exports_files(["src/download_paths.json"]) diff --git a/playwright/private/cli/Cargo.toml b/playwright/private/cli/Cargo.toml deleted file mode 100644 index 5dda24d..0000000 --- a/playwright/private/cli/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "cli" -version = "0.1.0" -edition = "2021" - -[dependencies] -serde_json = { version = "1.0.137", features = [ - "std", -], default-features = false } -askama = { version = "0.12.1", features = [], default-features = false } -serde = { version = "1.0.217", features = [ - "serde_derive", -], default-features = false } -xflags = { version = "0.3.2", features = [], default-features = false } -zip = "2.6.0" -sha2 = "0.10.8" diff --git a/playwright/private/cli/src/browser_targets.rs b/playwright/private/cli/src/browser_targets.rs deleted file mode 100644 index eea4dcb..0000000 --- a/playwright/private/cli/src/browser_targets.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::path::PathBuf; - -use serde::Serialize; - -use crate::{ - browsers::{BrowserData, Browsers}, - download_paths::{DownloadPaths, Platform}, -}; - -#[derive(Debug, Serialize, Clone)] -pub struct BrowserTarget { - pub http_file_workspace_name: String, - pub http_file_path: String, - pub label: String, - pub output_dir: String, - pub platform: Platform, - pub browser: String, - pub browser_name: String, -} - -#[derive(Debug, Serialize)] -pub struct HttpFile { - pub name: String, - pub path: String, -} - -impl From for HttpFile { - fn from(value: BrowserTarget) -> Self { - HttpFile { - name: value.http_file_workspace_name, - path: value.http_file_path, - } - } -} - -pub fn get_browser_rules( - browsers_workspace_name_prefix: &str, - browser_json_path: &PathBuf, -) -> std::io::Result> { - let browsers_json = std::fs::read_to_string(browser_json_path)?; - let browsers: Browsers = serde_json::from_str(&browsers_json)?; - - let download_paths_json = include_str!("download_paths.json"); - let download_paths: DownloadPaths = serde_json::from_str(download_paths_json)?; - - let has_headless = browsers - .browsers - .iter() - .any(|b| b.name.ends_with("-headless-shell")); - - let mut browser_rules: Vec = browsers - .browsers - .into_iter() - .flat_map(|browser| { - if has_headless { - return vec![browser]; - } - // Handle headless browser variants - match browser.name.as_str() { - "chromium" | "chromium-tip-of-tree" => vec![ - browser.clone(), - BrowserData { - name: format!("{}-headless-shell", browser.name), - ..browser - }, - ], - _ => vec![browser], - } - }) - .flat_map(|browser| { - let paths = download_paths.paths.get(&browser.name); - if paths.is_none() { - return vec![]; - } - let browser_rules: Vec = paths - .unwrap() - .paths - .iter() - .filter_map(|(platform, template)| { - if *platform == Platform::Unknown { - return None; - } - match ( - template, - serde_json::to_string(platform) - .map(|name| name.trim_matches('"').to_string()), - serde_json::to_string(&browser.name) - .map(|name| name.trim_matches('"').to_string()), - ) { - (Some(template), Ok(platform_str), Ok(browser_name)) => { - let has_revision_override = browser - .revision_overrides - .as_ref() - .and_then(|overrides| overrides.get(platform)) - .is_some(); - - let revision = browser - .revision_overrides - .as_ref() - .and_then(|overrides| overrides.get(platform)) - .unwrap_or(&browser.revision); - - let browser_version = if template.contains("{browserVersion}") { - browser.browser_version.as_ref().unwrap() - } else { - "" - }; - - let snake_case_browser_name = browser_name.replace("-", "_"); - let browser_directory_prefix = if has_revision_override { - format!( - "{}_{}_{}", - snake_case_browser_name, platform_str, "special" - ) - } else { - snake_case_browser_name - }; - - Some(BrowserTarget { - http_file_workspace_name: format!( - "{browsers_workspace_name_prefix}-{browser_name}-{platform_str}" - ), - http_file_path: template.replace("{revision}", revision).replace("{browserVersion}", &browser_version), - label: format!("{browser_name}-{platform_str}"), - output_dir: format!( - "{platform_str}/{}-{}", - browser_directory_prefix, browser.revision - ), - browser_name, - platform: platform.clone(), - browser: browser.name.clone(), - }) - } - _ => None, - } - }) - .collect(); - - browser_rules - }) - .collect(); - - browser_rules.sort_by(|a, b| a.label.cmp(&b.label)); - - Ok(browser_rules) -} diff --git a/playwright/private/cli/src/browsers.rs b/playwright/private/cli/src/browsers.rs deleted file mode 100644 index 4b60827..0000000 --- a/playwright/private/cli/src/browsers.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; - -use crate::download_paths::Platform; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Browsers { - pub browsers: Vec, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct BrowserData { - pub name: String, - pub revision: String, - pub install_by_default: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub browser_version: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub revision_overrides: Option>, -} diff --git a/playwright/private/cli/src/download_paths.rs b/playwright/private/cli/src/download_paths.rs deleted file mode 100644 index 55c118c..0000000 --- a/playwright/private/cli/src/download_paths.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; - -// Main structure representing the entire JSON -#[derive(Debug, Serialize, Deserialize)] -pub struct DownloadPaths { - #[serde(flatten)] - pub paths: HashMap, -} - -#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub enum Platform { - #[serde(rename = "ubuntu18.04-x64")] - Ubuntu1804X64, - #[serde(rename = "ubuntu20.04-x64")] - Ubuntu2004X64, - #[serde(rename = "ubuntu22.04-x64")] - Ubuntu2204X64, - #[serde(rename = "ubuntu24.04-x64")] - Ubuntu2404X64, - #[serde(rename = "ubuntu18.04-arm64")] - Ubuntu1804Arm64, - #[serde(rename = "ubuntu20.04-arm64")] - Ubuntu2004Arm64, - #[serde(rename = "ubuntu22.04-arm64")] - Ubuntu2204Arm64, - #[serde(rename = "ubuntu24.04-arm64")] - Ubuntu2404Arm64, - Debian11X64, - Debian11Arm64, - Debian12X64, - Debian12Arm64, - #[serde(rename = "mac10.13")] - Mac1013, - #[serde(rename = "mac10.14")] - Mac1014, - #[serde(rename = "mac10.15")] - Mac1015, - Mac11, - Mac11Arm64, - Mac12, - Mac12Arm64, - Mac13, - Mac13Arm64, - Mac14, - Mac14Arm64, - Mac15, - Mac15Arm64, - #[serde(rename = "", other)] - Unknown, -} - -pub trait PlatformBase { - fn base_name(&self) -> &str; -} - -impl PlatformBase for Platform { - fn base_name(&self) -> &str { - match self { - Platform::Ubuntu1804X64 | Platform::Ubuntu1804Arm64 => "ubuntu18.04", - Platform::Ubuntu2004X64 | Platform::Ubuntu2004Arm64 => "ubuntu20.04", - Platform::Ubuntu2204X64 | Platform::Ubuntu2204Arm64 => "ubuntu22.04", - Platform::Ubuntu2404X64 | Platform::Ubuntu2404Arm64 => "ubuntu24.04", - Platform::Debian11X64 | Platform::Debian11Arm64 => "debian11", - Platform::Debian12X64 | Platform::Debian12Arm64 => "debian12", - Platform::Mac1013 => "mac10.13", - Platform::Mac1014 => "mac10.14", - Platform::Mac1015 => "mac10.15", - Platform::Mac11 | Platform::Mac11Arm64 => "mac11", - Platform::Mac12 | Platform::Mac12Arm64 => "mac12", - Platform::Mac13 | Platform::Mac13Arm64 => "mac13", - Platform::Mac14 | Platform::Mac14Arm64 => "mac14", - Platform::Mac15 | Platform::Mac15Arm64 => "mac15", - Platform::Unknown => "", - } - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct BuildPaths { - #[serde(flatten)] - pub paths: HashMap>, -} diff --git a/playwright/private/cli/src/extract_zip.rs b/playwright/private/cli/src/extract_zip.rs deleted file mode 100644 index fd2d442..0000000 --- a/playwright/private/cli/src/extract_zip.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::fs; -use std::fs::File; -use std::io::BufReader; -use std::path::PathBuf; -use zip::ZipArchive; - -pub fn extract_zip(zip_path: PathBuf, output_dir: PathBuf) -> std::io::Result<()> { - fs::create_dir_all(&output_dir)?; - let zip_file = File::open(zip_path)?; - let mut archive = - ZipArchive::new(BufReader::new(zip_file)).expect("couldn't open test zip file"); - archive.extract(&output_dir).unwrap(); - Ok(()) -} diff --git a/playwright/private/cli/src/integrity_map.rs b/playwright/private/cli/src/integrity_map.rs deleted file mode 100644 index c4ea656..0000000 --- a/playwright/private/cli/src/integrity_map.rs +++ /dev/null @@ -1,59 +0,0 @@ -use sha2::{Digest, Sha256}; -use std::collections::HashMap; -use std::fs; -use std::fs::File; -use std::io::Read; -use std::path::PathBuf; - -pub fn integrity_map(output_path: PathBuf, browsers: Vec, silent: bool) { - let integrity_map: HashMap = browsers - .into_iter() - .map(|browser| { - ( - to_integrity_map_key(&browser), - to_integrity_map_value(&browser), - ) - }) - .collect::>(); - - let map_str = serde_json::to_string_pretty(&integrity_map) - .expect("Could not serialize integrity map to json"); - if !silent { - println!("integrity_map = {}", map_str); - } - - fs::write(output_path, map_str).expect("Could not write file"); -} - -fn to_integrity_map_key(browser: &str) -> String { - browser - .split(":") - .next() - .unwrap_or_else(|| panic!("Could not split browser mapping {browser}")) - .to_string() -} - -fn to_integrity_map_value(browser: &str) -> String { - let path = browser - .split(":") - .nth(1) - .unwrap_or_else(|| panic!("Could not split browser mapping {browser}")) - .to_string(); - let mut file = - File::open(&path).unwrap_or_else(|_| panic!("Could not read browser archive {path}")); - let mut hasher = Sha256::new(); - let mut buffer = [0; 1024]; - - loop { - let bytes_read = file - .read(&mut buffer) - .expect("Could not read file into buffer"); - if bytes_read == 0 { - break; - } - hasher.update(&buffer[..bytes_read]); - } - - let hash = hasher.finalize(); - format!("sha256-{:x}", hash) -} diff --git a/playwright/private/cli/src/main.rs b/playwright/private/cli/src/main.rs deleted file mode 100644 index 462274e..0000000 --- a/playwright/private/cli/src/main.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::path::PathBuf; -mod browser_targets; -mod browsers; -mod download_paths; -mod extract_zip; -mod integrity_map; -mod platform_groups; -mod templates; -use browser_targets::{get_browser_rules, HttpFile}; -use extract_zip::extract_zip; -use integrity_map::integrity_map; - -mod flags { - use std::path::PathBuf; - - xflags::xflags! { - cmd rules-playwright { - cmd workspace { - required --browser-json-path browser_json_path: PathBuf - required --browsers-workspace-name-prefix browsers_workspace_name_prefix: String - required --rules-playwright-cannonical-name rules_playwright_cannonical_name: String - } - cmd http-files { - required --browser-json-path browser_json_path: PathBuf - required --browsers-workspace-name-prefix browsers_workspace_name_prefix: String - } - cmd unzip { - required --input-path input_path: PathBuf - required --output-path output_path: PathBuf - } - cmd integrity-map { - required --output-path output_path: PathBuf - optional --silent silent: bool - repeated browsers: String - } - } - - } -} - -pub fn main() -> std::io::Result<()> { - let flags = flags::RulesPlaywright::from_env_or_exit(); - - let out_dir = PathBuf::from("./"); - - match flags.subcommand { - flags::RulesPlaywrightCmd::Workspace(cmd) => { - templates::write_workspace( - &out_dir, - get_browser_rules(&cmd.browsers_workspace_name_prefix, &cmd.browser_json_path)?, - &cmd.rules_playwright_cannonical_name, - )?; - } - flags::RulesPlaywrightCmd::HttpFiles(cmd) => { - let http_files: Vec = - get_browser_rules(&cmd.browsers_workspace_name_prefix, &cmd.browser_json_path)? - .into_iter() - .map(|b| b.into()) - .collect(); - serde_json::to_writer(std::io::stdout(), &http_files)?; - } - flags::RulesPlaywrightCmd::Unzip(cmd) => { - extract_zip(cmd.input_path, cmd.output_path)?; - } - flags::RulesPlaywrightCmd::IntegrityMap(cmd) => { - integrity_map(cmd.output_path, cmd.browsers, cmd.silent.unwrap_or(false)); - } - } - - Ok(()) -} diff --git a/playwright/private/cli/src/platform_groups.rs b/playwright/private/cli/src/platform_groups.rs deleted file mode 100644 index 8fb83b5..0000000 --- a/playwright/private/cli/src/platform_groups.rs +++ /dev/null @@ -1,63 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use crate::download_paths::Platform; - -#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)] -pub enum PlatformGroup { - LinuxX86_64, - LinuxArm64, - MacosX86_64, - MacosArm64, -} - -impl Into for PlatformGroup { - fn into(self) -> String { - match self { - PlatformGroup::LinuxX86_64 => "linux_x86_64".to_string(), - PlatformGroup::LinuxArm64 => "linux_arm64".to_string(), - PlatformGroup::MacosX86_64 => "macos_x86_64".to_string(), - PlatformGroup::MacosArm64 => "macos_arm64".to_string(), - } - } -} - -impl Into for Platform { - fn into(self) -> PlatformGroup { - match self { - // Linux x86_64 platforms - Platform::Ubuntu1804X64 - | Platform::Ubuntu2004X64 - | Platform::Ubuntu2204X64 - | Platform::Ubuntu2404X64 - | Platform::Debian11X64 - | Platform::Debian12X64 => PlatformGroup::LinuxX86_64, - - // Linux ARM64 platforms - Platform::Ubuntu1804Arm64 - | Platform::Ubuntu2004Arm64 - | Platform::Ubuntu2204Arm64 - | Platform::Ubuntu2404Arm64 - | Platform::Debian11Arm64 - | Platform::Debian12Arm64 => PlatformGroup::LinuxArm64, - - // macOS x86_64 platforms - Platform::Mac1013 - | Platform::Mac1014 - | Platform::Mac1015 - | Platform::Mac11 - | Platform::Mac12 - | Platform::Mac13 - | Platform::Mac14 - | Platform::Mac15 => PlatformGroup::MacosX86_64, - - // macOS ARM64 platforms - Platform::Mac11Arm64 - | Platform::Mac12Arm64 - | Platform::Mac13Arm64 - | Platform::Mac14Arm64 - | Platform::Mac15Arm64 => PlatformGroup::MacosArm64, - - Platform::Unknown => PlatformGroup::LinuxX86_64, // You might want to handle Unknown differently - } - } -} diff --git a/playwright/private/cli/src/templates/aliases.rs b/playwright/private/cli/src/templates/aliases.rs deleted file mode 100644 index 6f105ca..0000000 --- a/playwright/private/cli/src/templates/aliases.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::download_paths::PlatformBase; -use askama::Template; -use std::{collections::HashMap, fs, io, path::Path}; - -use super::RootTarget; - -#[derive(Template)] -#[template( - source = r#" -package(default_visibility = ["//visibility:public"]) -{% for target in alias_targets %} -alias( - name = "{{ target.label }}", - actual = select( - { - {%- for entry in target.src_select %} - "{{ entry.key }}": "{{ entry.value }}", - {%- endfor %} - }, - ), -) -{% endfor %} -"#, - ext = "txt" -)] -struct AliasBuildFileTemplate { - alias_targets: Vec, -} - -struct AliasTarget { - label: String, - src_select: Vec, -} - -struct AliasTargetSelect { - key: String, - value: String, -} - -pub fn write_build_file( - out_dir: &Path, - root_targets: &HashMap, - rules_playwright_cannonical_name: &str, -) -> io::Result<()> { - let template = AliasBuildFileTemplate { - alias_targets: root_targets - .iter() - .flat_map(|(_, root_target)| { - root_target - .src_select - .values() - .map(|platform_group_target| AliasTarget { - label: format!("{}_{}", root_target.name, platform_group_target.name), - src_select: platform_group_target - .src_select - .iter() - .map(|(platform, platform_target)| AliasTargetSelect { - key: format!( - "@{rules_playwright_cannonical_name}//tools/platforms:{}", - platform.base_name() - ), - value: format!("//browsers:{}", platform_target.name), - }) - .collect(), - }) - }) - .collect(), - }; - - let aliases_dir = out_dir.join("aliases"); - fs::create_dir(aliases_dir)?; - fs::write( - out_dir.join("aliases/BUILD.bazel"), - template.render().unwrap(), - ) -} diff --git a/playwright/private/cli/src/templates/browsers.rs b/playwright/private/cli/src/templates/browsers.rs deleted file mode 100644 index d442abe..0000000 --- a/playwright/private/cli/src/templates/browsers.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::{fs, io, path::Path}; - -use askama::Template; - -use crate::browser_targets::BrowserTarget; - -#[derive(Template)] -#[template( - source = r#" -load("@{{ rules_playwright_cannonical_name }}//playwright:defs.bzl", "unzip_browser") - -package(default_visibility = ["//visibility:public"]) - -{% for target in browser_targets %} -unzip_browser( - name = "{{ target.label }}", - browser = "@{{ target.http_file_workspace_name }}//file", - output_dir = "{{ target.output_dir }}", - http_file_path = "{{ target.http_file_path }}", -) -{% endfor %} -"#, - ext = "txt" -)] -struct BrowsersBuildFileTemplate<'a> { - browser_targets: &'a Vec, - rules_playwright_cannonical_name: &'a str, -} - -pub fn write_build_file( - out_dir: &Path, - browser_targets: &Vec, - rules_playwright_cannonical_name: &str, -) -> io::Result<()> { - let browsers_dir = out_dir.join("browsers"); - fs::create_dir(browsers_dir)?; - fs::write( - out_dir.join("browsers/BUILD.bazel"), - BrowsersBuildFileTemplate { - browser_targets, - rules_playwright_cannonical_name, - } - .render() - .unwrap(), - ) -} diff --git a/playwright/private/cli/src/templates/mod.rs b/playwright/private/cli/src/templates/mod.rs deleted file mode 100644 index dacd50e..0000000 --- a/playwright/private/cli/src/templates/mod.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::{collections::HashMap, io, path::Path}; - -use crate::{ - browser_targets::BrowserTarget, download_paths::Platform, platform_groups::PlatformGroup, -}; - -mod aliases; -mod browsers; -mod root; - -pub fn write_workspace( - out_dir: &Path, - browser_targets: Vec, - rules_playwright_cannonical_name: &str, -) -> io::Result<()> { - browsers::write_build_file(out_dir, &browser_targets, rules_playwright_cannonical_name)?; - - let mut root_targets: HashMap = HashMap::new(); - for target in browser_targets { - let root_target = root_targets - .entry(target.browser) - .or_insert_with(|| RootTarget { - name: target.browser_name, - src_select: HashMap::new(), - }); - - let platform_group: PlatformGroup = target.platform.clone().into(); - let platform_group_target = root_target - .src_select - .entry(platform_group.clone()) - .or_insert_with(|| PlatformGroupTarget { - name: platform_group.into(), - src_select: HashMap::new(), - }); - - platform_group_target.src_select.insert( - target.platform, - PlatformTarget { - name: target.label, - browser: target.http_file_workspace_name, - output_dir: target.output_dir, - }, - ); - } - - root::write_build_file(out_dir, &root_targets, rules_playwright_cannonical_name)?; - aliases::write_build_file(out_dir, &root_targets, rules_playwright_cannonical_name) -} - -struct RootTarget { - pub name: String, - pub src_select: HashMap, -} - -struct PlatformGroupTarget { - pub name: String, - pub src_select: HashMap, -} - -#[allow(dead_code)] -struct PlatformTarget { - pub name: String, - pub browser: String, - pub output_dir: String, -} diff --git a/playwright/private/cli/src/templates/root.rs b/playwright/private/cli/src/templates/root.rs deleted file mode 100644 index f2096b0..0000000 --- a/playwright/private/cli/src/templates/root.rs +++ /dev/null @@ -1,81 +0,0 @@ -use askama::Template; -use std::{collections::HashMap, fs, io, path::Path}; - -use super::RootTarget; - -#[derive(Template)] -#[template( - source = r#" -load("@{{ rules_playwright_cannonical_name }}//playwright:defs.bzl", "select_exec") - -package(default_visibility = ["//visibility:public"]) -{% for target in select_targets %} -select_exec( - name = "{{ target.label }}", - src = select( - { - {%- for group in target.platform_groups %} - "{{ group.name }}": "{{ group.alias_label }}", - {%- endfor %} - }, - ), -) -{% endfor %} -"#, - ext = "txt" -)] -struct RootBuildFileTemplate<'a> { - rules_playwright_cannonical_name: &'a str, - select_targets: &'a [SelectTarget], -} - -struct SelectTarget { - label: String, - platform_groups: Vec, -} - -struct SelectPlatformGroup { - name: String, - alias_label: String, -} - -pub fn write_build_file( - out_dir: &Path, - root_targets: &HashMap, - rules_playwright_cannonical_name: &str, -) -> io::Result<()> { - let select_targets: Vec = root_targets - .iter() - .map(|(_browser, root_target)| SelectTarget { - label: root_target.name.clone(), - platform_groups: root_target - .src_select - .iter() - .map(|(platform_group, platform_group_target)| { - let name_label: String = platform_group.clone().into(); - - SelectPlatformGroup { - name: format!( - "@{rules_playwright_cannonical_name}//tools/platforms:{}", - name_label - ), - alias_label: format!( - "//aliases:{}_{}", - root_target.name, platform_group_target.name - ), - } - }) - .collect(), - }) - .collect(); - - fs::write( - out_dir.join("BUILD.bazel"), - RootBuildFileTemplate { - select_targets: &select_targets, - rules_playwright_cannonical_name, - } - .render() - .unwrap(), - ) -} diff --git a/playwright/private/integrity_map.bzl b/playwright/private/integrity_map.bzl index e81d465..882f086 100644 --- a/playwright/private/integrity_map.bzl +++ b/playwright/private/integrity_map.bzl @@ -6,24 +6,59 @@ load(":unzip_browser.bzl", "UnzippedBrowserInfo") def _playwright_integrity_map_impl(ctx): output = ctx.actions.declare_file(ctx.attr.output if ctx.attr.output else ctx.attr.name + ".json") - browser_args = [] + browser_pairs = [] inputs = [] for browser in ctx.attr.browsers: http_file_path = browser[UnzippedBrowserInfo].http_file_path browser_archive = browser[UnzippedBrowserInfo].browser_archive inputs.append(browser_archive) - browser_args.append("{}:{}".format(http_file_path, browser_archive.path)) + browser_pairs.append("{}:{}".format(http_file_path, browser_archive.path)) - silent_args = [] - if ctx.attr.silent: - silent_args = ["--silent", "true"] + script = """ +set -euo pipefail +out="$1" +silent="$2" +shift 2 - ctx.actions.run( +tmp="${out}.tmp" +echo "{" > "$tmp" +first=1 + +for pair in "$@"; do + key="${pair%%:*}" + path="${pair#*:}" + if command -v sha256sum >/dev/null 2>&1; then + digest="$(sha256sum "$path" | awk '{print $1}')" + elif command -v shasum >/dev/null 2>&1; then + digest="$(shasum -a 256 "$path" | awk '{print $1}')" + else + digest="$(openssl dgst -sha256 "$path" | awk '{print $2}')" + fi + + if [ "$first" -eq 0 ]; then + echo "," >> "$tmp" + fi + first=0 + printf ' "%s": "sha256-%s"' "$key" "$digest" >> "$tmp" +done + +echo >> "$tmp" +echo "}" >> "$tmp" +mv "$tmp" "$out" + +if [ "$silent" != "true" ]; then + printf 'integrity_map = ' + cat "$out" + echo +fi +""" + + ctx.actions.run_shell( inputs = inputs, outputs = [output], - executable = ctx.executable._cli, - arguments = ["integrity-map", "--output-path", output.path] + silent_args + browser_args, + command = script, + arguments = [output.path, "true" if ctx.attr.silent else "false"] + browser_pairs, ) return [DefaultInfo(files = depset([output]))] @@ -58,12 +93,6 @@ playwright_integrity_map = rule( Set to True to prevent this debug output from being printed. """, ), - "_cli": attr.label( - default = "//tools/release:cli", - allow_single_file = True, - executable = True, - cfg = "exec", - ), }, ) diff --git a/playwright/private/unzip_browser.bzl b/playwright/private/unzip_browser.bzl index d5ab35c..e63e9fc 100644 --- a/playwright/private/unzip_browser.bzl +++ b/playwright/private/unzip_browser.bzl @@ -12,11 +12,15 @@ UnzippedBrowserInfo = provider( def _unzip_browser_impl(ctx): output_dir = ctx.actions.declare_directory(ctx.attr.output_dir) - ctx.actions.run( + ctx.actions.run_shell( inputs = [ctx.file.browser], outputs = [output_dir], - executable = ctx.executable._cli, - arguments = ["unzip", "--output-path", output_dir.path, "--input-path", ctx.file.browser.path], + command = """ +set -euo pipefail +mkdir -p "$1" +unzip -q "$2" -d "$1" +""", + arguments = [output_dir.path, ctx.file.browser.path], ) return [ DefaultInfo(files = depset([output_dir])), @@ -36,11 +40,5 @@ unzip_browser = rule( mandatory = True, ), "output_dir": attr.string(mandatory = True), - "_cli": attr.label( - default = "//tools/release:cli", - allow_single_file = True, - executable = True, - cfg = "exec", - ), }, ) diff --git a/playwright/private/util.bzl b/playwright/private/util.bzl index 42a87cd..7ca8a9d 100644 --- a/playwright/private/util.bzl +++ b/playwright/private/util.bzl @@ -22,45 +22,3 @@ def get_browsers_json_path(ctx, playwright_version, browsers_json): output = browsers_json_path, ) return ctx.path("playwright-core/package/browsers.json") - -def get_cli_path(ctx): - """Returns the platform-specific path to the browser workspace generator binary. - - Args: - ctx: The Starlark context object containing OS information - - Returns: - Path to the browser workspace generator binary for the current platform - """ - arch = "arm64" - if ctx.os.arch == "amd64" or ctx.os.arch == "x86_64": - arch = "x86_64" - - platform = "unknown-linux-musl" - if "mac" in ctx.os.name: - platform = "apple-darwin" - - return ctx.path(Label("//tools/release:artifacts/cli-{arch}-{platform}".format(platform = platform, arch = arch))) - -def get_all_cli_paths(ctx): - """Returns paths to all platform-specific CLI binaries. - - Watching all CLI binaries (instead of just the current platform's) ensures - MODULE.bazel.lock remains consistent across different host platforms. - - Args: - ctx: The Starlark context object - - Returns: - List of paths to all CLI binaries for all supported platforms - """ - platforms = [ - ("arm64", "apple-darwin"), - ("x86_64", "apple-darwin"), - ("arm64", "unknown-linux-musl"), - ("x86_64", "unknown-linux-musl"), - ] - return [ - ctx.path(Label("//tools/release:artifacts/cli-{arch}-{platform}".format(arch = arch, platform = platform))) - for arch, platform in platforms - ] diff --git a/playwright/repositories.bzl b/playwright/repositories.bzl index 09b2611..79c3ef9 100644 --- a/playwright/repositories.bzl +++ b/playwright/repositories.bzl @@ -4,8 +4,9 @@ These are needed for local dev, and users must install them as well. See https://docs.bazel.build/versions/main/skylark/deploying.html#dependencies """ +load("//playwright/private:browser_targets.bzl", "compute_browser_targets", "render_workspace_files") load("//playwright/private:known_browsers.bzl", "KNOWN_BROWSER_INTEGRITY") -load("//playwright/private:util.bzl", "get_all_cli_paths", "get_browsers_json_path", "get_cli_path") +load("//playwright/private:util.bzl", "get_browsers_json_path") _PLAYWRIGHT_PACKAGE = "playwright" _PLAYWRIGHT_TEST_PACKAGE = "@playwright/test" @@ -41,29 +42,16 @@ def _playwright_repo_impl(ctx): if not playwright_version: fail("playwright not found in dependencies or devDependencies") - # Watch all CLI binaries to ensure MODULE.bazel.lock remains consistent - # across platforms and detects changes when binaries are updated - for cli_path in get_all_cli_paths(ctx): - ctx.watch(cli_path) - if ctx.attr.browsers_json: ctx.watch(ctx.attr.browsers_json) - - result = ctx.execute( - [ - get_cli_path(ctx), - "workspace", - "--browser-json-path", - get_browsers_json_path(ctx, playwright_version, ctx.attr.browsers_json), - "--browsers-workspace-name-prefix", - ctx.attr.browsers_workspace_name_prefix, - "--rules-playwright-cannonical-name", - ctx.attr.rules_playwright_cannonical_name, - ], - ) - - if result.return_code != 0: - fail(ctx.attr.name, "workspace command failed", result.stdout, result.stderr) + browsers_json_path = get_browsers_json_path(ctx, playwright_version, ctx.attr.browsers_json) + browsers_json = ctx.read(browsers_json_path) + download_paths_json = ctx.read(Label("//playwright/private/cli:src/download_paths.json")) + browser_targets = compute_browser_targets(ctx.attr.browsers_workspace_name_prefix, browsers_json, download_paths_json) + files = render_workspace_files(browser_targets, ctx.attr.rules_playwright_cannonical_name) + ctx.file("BUILD.bazel", files["BUILD.bazel"]) + ctx.file("aliases/BUILD.bazel", files["aliases/BUILD.bazel"]) + ctx.file("browsers/BUILD.bazel", files["browsers/BUILD.bazel"]) if hasattr(ctx, "repo_metadata"): return ctx.repo_metadata(reproducible = True) @@ -97,24 +85,13 @@ playwright_repository = repository_rule( ) def _define_browsers_impl(rctx): - # Watch all CLI binaries to ensure MODULE.bazel.lock remains consistent - # across platforms and detects changes when binaries are updated - for cli_path in get_all_cli_paths(rctx): - rctx.watch(cli_path) - rctx.watch(rctx.attr.browsers_json) - result = rctx.execute( - [ - get_cli_path(rctx), - "http-files", - "--browser-json-path", - rctx.path(rctx.attr.browsers_json), - "--browsers-workspace-name-prefix", - rctx.attr.name, - ], + download_paths_json = rctx.read(Label("//playwright/private/cli:src/download_paths.json")) + browser_targets = compute_browser_targets( + rctx.attr.name, + rctx.read(rctx.path(rctx.attr.browsers_json)), + download_paths_json, ) - if result.return_code != 0: - fail("http-files command failed", result.stdout, result.stderr) result_build = [ """load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")""", @@ -127,8 +104,8 @@ def _define_browsers_impl(rctx): for key, value in rctx.attr.browser_integrity.items(): integrity_map[key] = value - for http_file_json in json.decode(result.stdout): - path = http_file_json["path"] + for target in browser_targets: + path = target["http_file_path"] integrity_attr = "" if path in integrity_map: integrity_attr = 'integrity = "{}",\n'.format(integrity_map[path]) @@ -145,7 +122,7 @@ def _define_browsers_impl(rctx): {urls} ) """.format( - name = http_file_json["name"], + name = target["http_file_workspace_name"], path = path, integrity = integrity_attr, urls = urls_attr, diff --git a/tools/release/BUILD.bazel b/tools/release/BUILD.bazel index 85a603a..6d9e15b 100644 --- a/tools/release/BUILD.bazel +++ b/tools/release/BUILD.bazel @@ -1,69 +1,6 @@ -load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory") -load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_file") -load("@bazel_skylib//rules:native_binary.bzl", "native_binary") - package(default_visibility = ["//visibility:public"]) -config_setting( - name = "release", - values = { - "compilation_mode": "opt", - }, -) - -[ - platform( - name = "{}_{}".format(os, cpu), - constraint_values = [ - "@platforms//os:" + os, - "@platforms//cpu:" + cpu, - "//tools/linkers:musl" if os == "linux" else "//tools/linkers:unknown", - ], - ) - for os in [ - "linux", - "macos", - ] - for cpu in [ - "arm64", - "x86_64", - ] -] - -LINUX_ARTIFACTS = [ - "//playwright/private/cli:cli_linux", -] - -MACOS_ARTIFACTS = [ - "//playwright/private/cli:cli_macos", -] - -copy_to_directory( - name = "release_artifacts", - srcs = LINUX_ARTIFACTS + MACOS_ARTIFACTS, - out = "artifacts", - root_paths = ["playwright/private/cli"], - tags = ["manual"], -) - -write_source_file( - name = "copy_release_artifacts", - diff_test = False, - executable = True, - in_file = ":release_artifacts", - out_file = "artifacts", - tags = ["manual"], -) - -native_binary( - name = "cli", - src = select( - { - "//tools/platforms:macos_x86_64": ":artifacts/cli-x86_64-apple-darwin", - "//tools/platforms:macos_arm64": ":artifacts/cli-arm64-apple-darwin", - "//tools/platforms:linux_x86_64": ":artifacts/cli-x86_64-unknown-linux-musl", - "//tools/platforms:linux_arm64": ":artifacts/cli-arm64-unknown-linux-musl", - }, - ), - out = "cli", +filegroup( + name = "artifacts", + srcs = glob(["artifacts/*"]), ) diff --git a/tools/release/defs.bzl b/tools/release/defs.bzl index 91d0324..8137024 100644 --- a/tools/release/defs.bzl +++ b/tools/release/defs.bzl @@ -1,75 +1,8 @@ "Make releases for platforms supported by rules_playwright" -load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file") -load("@rules_rust//rust:defs.bzl", _rust_binary = "rust_binary") - -DEFAULT_OS = ["linux", "macos"] -DEFAULT_ARCHS = ["arm64", "x86_64"] - -def _map_os_to_triple(os): - if os == "linux": - return "unknown-linux-musl" - if os == "macos": - return "apple-darwin" - fail("Unrecognized os", os) - -# buildozer: disable=function-docstring def rust_binary(name, visibility = [], **kwargs): - selection = {} - for os in DEFAULT_OS: - outs = [] - - target_suffix = "{}_{}".format(name, os) - target_compatible_with = ["@platforms//os:{}".format(os)] - - for arch in DEFAULT_ARCHS: - arch_target_suffix = "{}_{}".format(target_suffix, arch) - binary_name = "{}_build".format(arch_target_suffix) - platform = "//tools/platforms:{}_{}".format(os, arch) - release_platform = "//tools/release:{}_{}".format(os, arch) - - # Artifact naming follows typical Rust "triples" convention. - artifact = "{}-{}-{}".format(name, arch, _map_os_to_triple(os)) - outs.append(artifact) - - selection.update([[platform, binary_name]]) - - _rust_binary( - name = binary_name, - crate_name = name, - platform = release_platform, - target_compatible_with = target_compatible_with, - rustc_flags = select({ - "//tools/release": [ - "-Ccodegen-units=1", - "-Cpanic=abort", - "-Copt-level=z", - "-Cstrip=symbols", - ], - "//conditions:default": [ - "-Copt-level=0", - ], - }), - tags = ["manual"], - **kwargs - ) - - copy_file( - name = "copy_{}".format(arch_target_suffix), - src = binary_name, - out = artifact, - tags = ["manual"], - ) - - native.filegroup( - name = target_suffix, - srcs = outs, - tags = ["manual"], - visibility = ["//tools/release:__pkg__"], - ) - - native.alias( + native.filegroup( name = name, - actual = select(selection), + srcs = kwargs.get("srcs", []), visibility = visibility, ) From 940a1f68d5c0d0d34e9f24afa429ae93088d7541 Mon Sep 17 00:00:00 2001 From: Ben King <9087625+benfdking@users.noreply.github.com> Date: Sun, 17 May 2026 19:46:40 +0100 Subject: [PATCH 5/9] ci: build root workspace instead of testing empty test set --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f84ffe7..f4908f3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,8 +27,8 @@ jobs: - name: Install Playwright system dependencies run: npx playwright install-deps - - name: Build and Test rules_playwright - run: bazelisk test //... + - name: Build rules_playwright + run: bazelisk build //... - name: Build and Test WORKSPACE example working-directory: examples/workspace From a5bbbe75f68105478dbd41c3619d8b376b77e47d Mon Sep 17 00:00:00 2001 From: Ben King <9087625+benfdking@users.noreply.github.com> Date: Sun, 17 May 2026 20:42:04 +0100 Subject: [PATCH 6/9] fix: ignore unsupported download path platforms in starlark generator --- playwright/private/browser_targets.bzl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/playwright/private/browser_targets.bzl b/playwright/private/browser_targets.bzl index 9656f75..ebf5732 100644 --- a/playwright/private/browser_targets.bzl +++ b/playwright/private/browser_targets.bzl @@ -79,6 +79,10 @@ def compute_browser_targets(browsers_workspace_name_prefix, browsers_json, downl for platform, template in download_paths[browser_name].items(): if platform == "" or platform.startswith("win") or template == None: continue + # Keep parity with prior Rust implementation: only generate targets + # for platforms this ruleset currently models. + if _base_platform(platform) == None: + continue current_revision = revision_overrides.get(platform, revision) has_revision_override = platform in revision_overrides @@ -193,4 +197,3 @@ def render_workspace_files(browser_targets, rules_playwright_cannonical_name): "BUILD.bazel": "\n".join(root_lines).strip() + "\n", "aliases/BUILD.bazel": "\n".join(aliases_lines).strip() + "\n", } - From df9a40060bc8fee70a15222a0380ae0c50c390ce Mon Sep 17 00:00:00 2001 From: Ben King <9087625+benfdking@users.noreply.github.com> Date: Sun, 17 May 2026 20:47:24 +0100 Subject: [PATCH 7/9] feat: add debian13 platform support --- playwright/defs.bzl | 1 + playwright/private/browser_targets.bzl | 2 ++ 2 files changed, 3 insertions(+) diff --git a/playwright/defs.bzl b/playwright/defs.bzl index 05dab7e..f9311c3 100644 --- a/playwright/defs.bzl +++ b/playwright/defs.bzl @@ -7,6 +7,7 @@ load("//playwright/private:unzip_browser.bzl", _unzip_browser = "unzip_browser") LINUX_DISTROS = [ "debian11", "debian12", + "debian13", "ubuntu20.04", "ubuntu22.04", "ubuntu24.04", diff --git a/playwright/private/browser_targets.bzl b/playwright/private/browser_targets.bzl index ebf5732..1f410c8 100644 --- a/playwright/private/browser_targets.bzl +++ b/playwright/private/browser_targets.bzl @@ -24,6 +24,8 @@ def _base_platform(platform): return "debian12" if platform.startswith("debian13"): return "debian13" + if platform.startswith("debian13"): + return "debian13" if platform.startswith("mac10.13"): return "mac10.13" if platform.startswith("mac10.14"): From 2f3e430d4b644c3a1968b97fd5f50d1c5b400e30 Mon Sep 17 00:00:00 2001 From: Ben King <9087625+benfdking@users.noreply.github.com> Date: Sun, 17 May 2026 20:56:10 +0100 Subject: [PATCH 8/9] fix(workspace): align Playwright example with CfT browser paths --- examples/workspace/WORKSPACE.bazel | 9 +++--- examples/workspace/package.json | 5 ++- examples/workspace/pnpm-lock.yaml | 52 +++++++++++++++++------------- 3 files changed, 38 insertions(+), 28 deletions(-) diff --git a/examples/workspace/WORKSPACE.bazel b/examples/workspace/WORKSPACE.bazel index 69ff877..ee4ee97 100644 --- a/examples/workspace/WORKSPACE.bazel +++ b/examples/workspace/WORKSPACE.bazel @@ -34,22 +34,21 @@ local_repository( ) http_archive( - name = "playwright-core-1.51.0", + name = "playwright-core-1.58.1", build_file_content = """ exports_files([ "browsers.json" ]) """, - integrity = "sha256-AjT7444dRLpDAElJJjUS+1RNkElB95H2aseuQsFn0fQ=", strip_prefix = "package", - url = "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.0.tgz", + url = "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.1.tgz", ) load("@mrmeku_rules_playwright//playwright:repositories.bzl", "define_browsers", "playwright_repository") define_browsers( name = "browsers_index", - browsers_json = "@playwright-core-1.51.0//:browsers.json", + browsers_json = "@playwright-core-1.58.1//:browsers.json", ) load("@browsers_index//:browsers.bzl", "fetch_browsers") @@ -59,7 +58,7 @@ fetch_browsers() playwright_repository( name = "playwright", browsers_workspace_name_prefix = "browsers_index", - playwright_version = "1.51.0", + playwright_version = "1.58.1", rules_playwright_cannonical_name = "mrmeku_rules_playwright", ) diff --git a/examples/workspace/package.json b/examples/workspace/package.json index 1c237a6..312be6d 100644 --- a/examples/workspace/package.json +++ b/examples/workspace/package.json @@ -1,5 +1,8 @@ { "devDependencies": { - "@playwright/test": "1.51.0" + "@playwright/test": "1.58.1" + }, + "pnpm": { + "onlyBuiltDependencies": [] } } diff --git a/examples/workspace/pnpm-lock.yaml b/examples/workspace/pnpm-lock.yaml index ac12db4..46ab5af 100644 --- a/examples/workspace/pnpm-lock.yaml +++ b/examples/workspace/pnpm-lock.yaml @@ -1,44 +1,52 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -devDependencies: - '@playwright/test': - specifier: 1.51.0 - version: 1.51.0 +importers: + + .: + devDependencies: + '@playwright/test': + specifier: 1.58.1 + version: 1.58.1 packages: - /@playwright/test@1.51.0: - resolution: {integrity: sha1-jVyEALRloL/bz5k+OQzuy5A+ptI=} + '@playwright/test@1.58.1': + resolution: {integrity: sha512-6LdVIUERWxQMmUSSQi0I53GgCBYgM2RpGngCPY7hSeju+VrKjq3lvs7HpJoPbDiY5QM5EYRtRX5fvrinnMAz3w==} engines: {node: '>=18'} hasBin: true - dependencies: - playwright: 1.51.0 - dev: true - /fsevents@2.3.2: - resolution: {integrity: sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - requiresBuild: true - dev: true - optional: true - /playwright-core@1.51.0: - resolution: {integrity: sha1-uyPqa7YpgkLQiK5elm/8+NyYJ+g=} + playwright-core@1.58.1: + resolution: {integrity: sha512-bcWzOaTxcW+VOOGBCQgnaKToLJ65d6AqfLVKEWvexyS3AS6rbXl+xdpYRMGSRBClPvyj44njOWoxjNdL/H9UNg==} engines: {node: '>=18'} hasBin: true - dev: true - /playwright@1.51.0: - resolution: {integrity: sha1-m6FUSXumK8bcGZxY7hkpXrNaRwc=} + playwright@1.58.1: + resolution: {integrity: sha512-+2uTZHxSCcxjvGc5C891LrS1/NlxglGxzrC4seZiVjcYVQfUa87wBL6rTDqzGjuoWNjnBzRqKmF6zRYGMvQUaQ==} engines: {node: '>=18'} hasBin: true + +snapshots: + + '@playwright/test@1.58.1': + dependencies: + playwright: 1.58.1 + + fsevents@2.3.2: + optional: true + + playwright-core@1.58.1: {} + + playwright@1.58.1: dependencies: - playwright-core: 1.51.0 + playwright-core: 1.58.1 optionalDependencies: fsevents: 2.3.2 - dev: true From 6e6b849662b184b70b28eff6016ab4a5fd91f89d Mon Sep 17 00:00:00 2001 From: Ben King <9087625+benfdking@users.noreply.github.com> Date: Sun, 17 May 2026 21:00:23 +0100 Subject: [PATCH 9/9] chore(workspace): bump Bazel version to 9.1.0 --- examples/workspace/.bazelversion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/workspace/.bazelversion b/examples/workspace/.bazelversion index ba7f754..47da986 100644 --- a/examples/workspace/.bazelversion +++ b/examples/workspace/.bazelversion @@ -1 +1 @@ -7.4.0 +9.1.0