From 7ef498be6d2945d680a79f3bc0b50eeeee0d950e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zieli=C5=84ski?= Date: Mon, 4 May 2026 23:35:38 +0200 Subject: [PATCH 1/4] Parallelize WASM extension builds --- .../publish-wasm-extension-artifact.yml | 7 ++++- .github/workflows/wasm-spike.yml | 7 ++++- .../wasm-spike/build-in-docker-rust.sh | 31 ++++++++++++++++--- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish-wasm-extension-artifact.yml b/.github/workflows/publish-wasm-extension-artifact.yml index 57560100..3900ff40 100644 --- a/.github/workflows/publish-wasm-extension-artifact.yml +++ b/.github/workflows/publish-wasm-extension-artifact.yml @@ -32,7 +32,7 @@ jobs: timeout-minutes: 45 strategy: fail-fast: false - max-parallel: 1 + max-parallel: 6 matrix: # The Rust WASM path uses ext-php-rs 0.15, which depends on PHP 8 # Zend APIs. PHP 7.4 cannot be added here by extending the matrix. @@ -83,6 +83,9 @@ jobs: cache: 'npm' cache-dependency-path: wordpress-playground/package-lock.json + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Install Playground deps working-directory: wordpress-playground run: npm ci --ignore-scripts @@ -93,6 +96,8 @@ jobs: PHP_VERSION: ${{ matrix.php }} ASYNC_MODE: jspi COMPILE_EXTENSION_PACKAGE: '@php-wasm/compile-extension@3.1.27' + DOCKER_BUILD_CACHE: gha + DOCKER_BUILD_CACHE_SCOPE_PREFIX: wp-mysql-parser-wasm PLAYGROUND_REPO: ${{ github.workspace }}/wordpress-playground run: bash build-in-docker-rust.sh diff --git a/.github/workflows/wasm-spike.yml b/.github/workflows/wasm-spike.yml index 39acf065..25c108c2 100644 --- a/.github/workflows/wasm-spike.yml +++ b/.github/workflows/wasm-spike.yml @@ -25,7 +25,7 @@ jobs: timeout-minutes: 45 strategy: fail-fast: false - max-parallel: 1 + max-parallel: 6 matrix: # The Rust WASM path uses ext-php-rs 0.15, which depends on PHP 8 # Zend APIs. PHP 7.4 cannot be added here by extending the matrix. @@ -77,6 +77,9 @@ jobs: cache: 'npm' cache-dependency-path: wordpress-playground/package-lock.json + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Install Playground deps working-directory: wordpress-playground run: npm ci --ignore-scripts @@ -87,6 +90,8 @@ jobs: PHP_VERSION: ${{ matrix.php }} ASYNC_MODE: ${{ matrix.async-mode }} COMPILE_EXTENSION_PACKAGE: '@php-wasm/compile-extension@3.1.27' + DOCKER_BUILD_CACHE: gha + DOCKER_BUILD_CACHE_SCOPE_PREFIX: wp-mysql-parser-wasm PLAYGROUND_REPO: ${{ github.workspace }}/wordpress-playground run: bash build-in-docker-rust.sh diff --git a/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh b/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh index e7b13afa..8bccf466 100755 --- a/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh +++ b/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh @@ -56,19 +56,40 @@ fi RUST_IMAGE="playground-php-wasm-ext-rust:${PHP_VERSION}-${ASYNC_MODE}" BASE_IMAGE="playground-php-wasm:compile-extension-php${PHP_VERSION//./-}-${ASYNC_MODE}" +DOCKER_BUILD_CACHE="${DOCKER_BUILD_CACHE:-}" +DOCKER_BUILD_CACHE_SCOPE_PREFIX="${DOCKER_BUILD_CACHE_SCOPE_PREFIX:-wp-mysql-parser-wasm}" + +docker_build() { + local cache_scope="$1" + shift + + if [ "$DOCKER_BUILD_CACHE" = "gha" ]; then + docker buildx build --load \ + --cache-from "type=gha,scope=${DOCKER_BUILD_CACHE_SCOPE_PREFIX}-${cache_scope}" \ + --cache-to "type=gha,mode=max,scope=${DOCKER_BUILD_CACHE_SCOPE_PREFIX}-${cache_scope}" \ + "$@" + return + fi + + docker build "$@" +} echo "==> Stage 0: preparing $BASE_IMAGE via Playground compile-extension tooling" -make -C "$PLAYGROUND_REPO/packages/php-wasm/compile" base-image -docker build \ +BASE_IMAGE_DIR="$PLAYGROUND_REPO/packages/php-wasm/compile/base-image" +docker_build "base-image" \ + -f "$BASE_IMAGE_DIR/Dockerfile" \ + --tag="playground-php-wasm:base" \ + "$BASE_IMAGE_DIR" +docker_build "compile-extension-php${PHP_VERSION//./-}-${ASYNC_MODE}" \ -f "$PLAYGROUND_REPO/packages/php-wasm/compile-extension/docker/Dockerfile.ext" \ - "$PLAYGROUND_REPO/packages/php-wasm" \ --tag="$BASE_IMAGE" \ --progress=plain \ --build-arg "PHP_VERSION=$PHP_RELEASE" \ - --build-arg "JSPI=yes" + --build-arg "JSPI=yes" \ + "$PLAYGROUND_REPO/packages/php-wasm" echo "==> Stage 0: building $RUST_IMAGE" -docker build \ +docker_build "rust-php${PHP_VERSION//./-}-${ASYNC_MODE}" \ --build-arg "BASE_IMAGE=$BASE_IMAGE" \ --build-arg "HOST_PHP_VERSION=$PHP_VERSION" \ --build-arg "HOST_PHP_API_VERSION=$PHP_API_VERSION" \ From 27269d7994c32806ec91207bb19798e5a271de4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zieli=C5=84ski?= Date: Mon, 4 May 2026 23:54:01 +0200 Subject: [PATCH 2/4] Keep WASM parallel builds on plain Docker --- .../publish-wasm-extension-artifact.yml | 5 ---- .github/workflows/wasm-spike.yml | 5 ---- .../wasm-spike/build-in-docker-rust.sh | 23 +++---------------- 3 files changed, 3 insertions(+), 30 deletions(-) diff --git a/.github/workflows/publish-wasm-extension-artifact.yml b/.github/workflows/publish-wasm-extension-artifact.yml index 3900ff40..b381e970 100644 --- a/.github/workflows/publish-wasm-extension-artifact.yml +++ b/.github/workflows/publish-wasm-extension-artifact.yml @@ -83,9 +83,6 @@ jobs: cache: 'npm' cache-dependency-path: wordpress-playground/package-lock.json - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Install Playground deps working-directory: wordpress-playground run: npm ci --ignore-scripts @@ -96,8 +93,6 @@ jobs: PHP_VERSION: ${{ matrix.php }} ASYNC_MODE: jspi COMPILE_EXTENSION_PACKAGE: '@php-wasm/compile-extension@3.1.27' - DOCKER_BUILD_CACHE: gha - DOCKER_BUILD_CACHE_SCOPE_PREFIX: wp-mysql-parser-wasm PLAYGROUND_REPO: ${{ github.workspace }}/wordpress-playground run: bash build-in-docker-rust.sh diff --git a/.github/workflows/wasm-spike.yml b/.github/workflows/wasm-spike.yml index 25c108c2..f95f48fa 100644 --- a/.github/workflows/wasm-spike.yml +++ b/.github/workflows/wasm-spike.yml @@ -77,9 +77,6 @@ jobs: cache: 'npm' cache-dependency-path: wordpress-playground/package-lock.json - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Install Playground deps working-directory: wordpress-playground run: npm ci --ignore-scripts @@ -90,8 +87,6 @@ jobs: PHP_VERSION: ${{ matrix.php }} ASYNC_MODE: ${{ matrix.async-mode }} COMPILE_EXTENSION_PACKAGE: '@php-wasm/compile-extension@3.1.27' - DOCKER_BUILD_CACHE: gha - DOCKER_BUILD_CACHE_SCOPE_PREFIX: wp-mysql-parser-wasm PLAYGROUND_REPO: ${{ github.workspace }}/wordpress-playground run: bash build-in-docker-rust.sh diff --git a/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh b/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh index 8bccf466..4e984722 100755 --- a/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh +++ b/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh @@ -56,31 +56,14 @@ fi RUST_IMAGE="playground-php-wasm-ext-rust:${PHP_VERSION}-${ASYNC_MODE}" BASE_IMAGE="playground-php-wasm:compile-extension-php${PHP_VERSION//./-}-${ASYNC_MODE}" -DOCKER_BUILD_CACHE="${DOCKER_BUILD_CACHE:-}" -DOCKER_BUILD_CACHE_SCOPE_PREFIX="${DOCKER_BUILD_CACHE_SCOPE_PREFIX:-wp-mysql-parser-wasm}" - -docker_build() { - local cache_scope="$1" - shift - - if [ "$DOCKER_BUILD_CACHE" = "gha" ]; then - docker buildx build --load \ - --cache-from "type=gha,scope=${DOCKER_BUILD_CACHE_SCOPE_PREFIX}-${cache_scope}" \ - --cache-to "type=gha,mode=max,scope=${DOCKER_BUILD_CACHE_SCOPE_PREFIX}-${cache_scope}" \ - "$@" - return - fi - - docker build "$@" -} echo "==> Stage 0: preparing $BASE_IMAGE via Playground compile-extension tooling" BASE_IMAGE_DIR="$PLAYGROUND_REPO/packages/php-wasm/compile/base-image" -docker_build "base-image" \ +docker build \ -f "$BASE_IMAGE_DIR/Dockerfile" \ --tag="playground-php-wasm:base" \ "$BASE_IMAGE_DIR" -docker_build "compile-extension-php${PHP_VERSION//./-}-${ASYNC_MODE}" \ +docker build \ -f "$PLAYGROUND_REPO/packages/php-wasm/compile-extension/docker/Dockerfile.ext" \ --tag="$BASE_IMAGE" \ --progress=plain \ @@ -89,7 +72,7 @@ docker_build "compile-extension-php${PHP_VERSION//./-}-${ASYNC_MODE}" \ "$PLAYGROUND_REPO/packages/php-wasm" echo "==> Stage 0: building $RUST_IMAGE" -docker_build "rust-php${PHP_VERSION//./-}-${ASYNC_MODE}" \ +docker build \ --build-arg "BASE_IMAGE=$BASE_IMAGE" \ --build-arg "HOST_PHP_VERSION=$PHP_VERSION" \ --build-arg "HOST_PHP_API_VERSION=$PHP_API_VERSION" \ From fdd151faba7c1e849b0b5300d2c09f8c9944fd32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zieli=C5=84ski?= Date: Tue, 5 May 2026 00:07:30 +0200 Subject: [PATCH 3/4] Share WASM base image across PHP builds --- .../publish-wasm-extension-artifact.yml | 44 +++++++++++++++++++ .github/workflows/wasm-spike.yml | 44 +++++++++++++++++++ .../wasm-spike/build-in-docker-rust.sh | 18 +++++--- 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-wasm-extension-artifact.yml b/.github/workflows/publish-wasm-extension-artifact.yml index b381e970..69fff8b2 100644 --- a/.github/workflows/publish-wasm-extension-artifact.yml +++ b/.github/workflows/publish-wasm-extension-artifact.yml @@ -26,8 +26,42 @@ concurrency: cancel-in-progress: true jobs: + base-image: + name: Build shared Playground base image + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Check out wordpress-playground + uses: actions/checkout@v4 + with: + repository: WordPress/wordpress-playground + ref: ${{ github.event.inputs.playground-ref || 'trunk' }} + path: wordpress-playground + sparse-checkout: | + packages/php-wasm/compile/base-image + + - name: Build Playground base image + run: | + docker build \ + -f wordpress-playground/packages/php-wasm/compile/base-image/Dockerfile \ + --tag playground-php-wasm:base \ + wordpress-playground/packages/php-wasm/compile/base-image + + - name: Save Playground base image + run: docker save --output "${RUNNER_TEMP}/playground-php-wasm-base.tar" playground-php-wasm:base + + - name: Upload Playground base image + uses: actions/upload-artifact@v4 + with: + name: playground-php-wasm-base-image + path: ${{ runner.temp }}/playground-php-wasm-base.tar + if-no-files-found: error + retention-days: 1 + build: name: Build wp_mysql_parser.so (PHP ${{ matrix.php }}) + needs: base-image runs-on: ubuntu-latest timeout-minutes: 45 strategy: @@ -83,6 +117,15 @@ jobs: cache: 'npm' cache-dependency-path: wordpress-playground/package-lock.json + - name: Download Playground base image + uses: actions/download-artifact@v4 + with: + name: playground-php-wasm-base-image + path: ${{ runner.temp }}/playground-base-image + + - name: Load Playground base image + run: docker load --input "${RUNNER_TEMP}/playground-base-image/playground-php-wasm-base.tar" + - name: Install Playground deps working-directory: wordpress-playground run: npm ci --ignore-scripts @@ -94,6 +137,7 @@ jobs: ASYNC_MODE: jspi COMPILE_EXTENSION_PACKAGE: '@php-wasm/compile-extension@3.1.27' PLAYGROUND_REPO: ${{ github.workspace }}/wordpress-playground + SKIP_BASE_IMAGE_BUILD: '1' run: bash build-in-docker-rust.sh - name: Verify side module exists diff --git a/.github/workflows/wasm-spike.yml b/.github/workflows/wasm-spike.yml index f95f48fa..ffaf6a8f 100644 --- a/.github/workflows/wasm-spike.yml +++ b/.github/workflows/wasm-spike.yml @@ -19,8 +19,42 @@ on: default: 'trunk' jobs: + base-image: + name: Build shared Playground base image + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Check out wordpress-playground + uses: actions/checkout@v4 + with: + repository: WordPress/wordpress-playground + ref: ${{ github.event.inputs.playground-ref || 'trunk' }} + path: wordpress-playground + sparse-checkout: | + packages/php-wasm/compile/base-image + + - name: Build Playground base image + run: | + docker build \ + -f wordpress-playground/packages/php-wasm/compile/base-image/Dockerfile \ + --tag playground-php-wasm:base \ + wordpress-playground/packages/php-wasm/compile/base-image + + - name: Save Playground base image + run: docker save --output "${RUNNER_TEMP}/playground-php-wasm-base.tar" playground-php-wasm:base + + - name: Upload Playground base image + uses: actions/upload-artifact@v4 + with: + name: playground-php-wasm-base-image + path: ${{ runner.temp }}/playground-php-wasm-base.tar + if-no-files-found: error + retention-days: 1 + build-and-load: name: Build wp_mysql_parser.so and load it in Playground (PHP ${{ matrix.php }}) + needs: base-image runs-on: ubuntu-latest timeout-minutes: 45 strategy: @@ -77,6 +111,15 @@ jobs: cache: 'npm' cache-dependency-path: wordpress-playground/package-lock.json + - name: Download Playground base image + uses: actions/download-artifact@v4 + with: + name: playground-php-wasm-base-image + path: ${{ runner.temp }}/playground-base-image + + - name: Load Playground base image + run: docker load --input "${RUNNER_TEMP}/playground-base-image/playground-php-wasm-base.tar" + - name: Install Playground deps working-directory: wordpress-playground run: npm ci --ignore-scripts @@ -88,6 +131,7 @@ jobs: ASYNC_MODE: ${{ matrix.async-mode }} COMPILE_EXTENSION_PACKAGE: '@php-wasm/compile-extension@3.1.27' PLAYGROUND_REPO: ${{ github.workspace }}/wordpress-playground + SKIP_BASE_IMAGE_BUILD: '1' run: bash build-in-docker-rust.sh - name: Verify build artifacts exist diff --git a/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh b/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh index 4e984722..11bb527f 100755 --- a/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh +++ b/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh @@ -58,11 +58,19 @@ RUST_IMAGE="playground-php-wasm-ext-rust:${PHP_VERSION}-${ASYNC_MODE}" BASE_IMAGE="playground-php-wasm:compile-extension-php${PHP_VERSION//./-}-${ASYNC_MODE}" echo "==> Stage 0: preparing $BASE_IMAGE via Playground compile-extension tooling" -BASE_IMAGE_DIR="$PLAYGROUND_REPO/packages/php-wasm/compile/base-image" -docker build \ - -f "$BASE_IMAGE_DIR/Dockerfile" \ - --tag="playground-php-wasm:base" \ - "$BASE_IMAGE_DIR" +if [ "${SKIP_BASE_IMAGE_BUILD:-}" = "1" ]; then + if ! docker image inspect playground-php-wasm:base >/dev/null 2>&1; then + echo "SKIP_BASE_IMAGE_BUILD=1 requires a preloaded playground-php-wasm:base image." >&2 + exit 1 + fi + echo "==> Stage 0: using preloaded playground-php-wasm:base" +else + BASE_IMAGE_DIR="$PLAYGROUND_REPO/packages/php-wasm/compile/base-image" + docker build \ + -f "$BASE_IMAGE_DIR/Dockerfile" \ + --tag="playground-php-wasm:base" \ + "$BASE_IMAGE_DIR" +fi docker build \ -f "$PLAYGROUND_REPO/packages/php-wasm/compile-extension/docker/Dockerfile.ext" \ --tag="$BASE_IMAGE" \ From 5f168dee979fd9197a5fdaaa72724ed9044e8435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zieli=C5=84ski?= Date: Tue, 5 May 2026 01:09:35 +0200 Subject: [PATCH 4/4] Reuse prepared compile-extension images --- .../wasm-spike/build-in-docker-rust.sh | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh b/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh index 11bb527f..f18d0e39 100755 --- a/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh +++ b/packages/php-ext-wp-mysql-parser/wasm-spike/build-in-docker-rust.sh @@ -54,6 +54,42 @@ if [ -z "$PLAYGROUND_REPO" ] || [ ! -f "$PLAYGROUND_REPO/packages/php-wasm/compi exit 1 fi +patch_compile_extension_cli() { + local cli_path="$1" + + # The npm CLI currently rebuilds the Docker images itself. This script has + # already prepared those exact images in Stage 0, so patch the pinned CLI to + # reuse them and fail loudly if the installed package changes shape. + node --input-type=module - "$cli_path" <<'NODE' +import { readFileSync, writeFileSync } from 'node:fs'; + +const cliPath = process.argv[2]; +let source = readFileSync(cliPath, 'utf8'); + +const replacements = [ + [ + 'async function k(e){await p("make",["base-image"],{cwd:e.compileRoot})}', + 'async function k(e){if(process.env.COMPILE_EXTENSION_SKIP_IMAGE_BUILD==="1"){await p("docker",["image","inspect","playground-php-wasm:base"],{stdio:"ignore"});console.log("Skipping compile-extension base image build; using existing playground-php-wasm:base");return}await p("make",["base-image"],{cwd:e.compileRoot})}', + ], + [ + 'async function j(e){const t=$(e);return await p("docker",["build","-f","compile-extension/docker/Dockerfile.ext",".",`--tag=${t}`,"--progress=plain","--build-arg",`PHP_VERSION=${e.phpRelease}`,"--build-arg","JSPI=yes"],{cwd:e.phpWasmRoot}),t}', + 'async function j(e){const t=$(e);if(process.env.COMPILE_EXTENSION_SKIP_IMAGE_BUILD==="1"){await p("docker",["image","inspect",t],{stdio:"ignore"});console.log(`Skipping compile-extension image build; using existing ${t}`);return t}return await p("docker",["build","-f","compile-extension/docker/Dockerfile.ext",".",`--tag=${t}`,"--progress=plain","--build-arg",`PHP_VERSION=${e.phpRelease}`,"--build-arg","JSPI=yes"],{cwd:e.phpWasmRoot}),t}', + ], +]; + +for (const [from, to] of replacements) { + if (!source.includes(from)) { + throw new Error(`Unable to patch @php-wasm/compile-extension CLI. Missing pattern: ${from}`); + } + source = source.replace(from, to); +} + +writeFileSync(cliPath, source); +NODE + + grep -q 'COMPILE_EXTENSION_SKIP_IMAGE_BUILD' "$cli_path" +} + RUST_IMAGE="playground-php-wasm-ext-rust:${PHP_VERSION}-${ASYNC_MODE}" BASE_IMAGE="playground-php-wasm:compile-extension-php${PHP_VERSION//./-}-${ASYNC_MODE}" @@ -275,10 +311,11 @@ ARTIFACT="wp_mysql_parser-php${PHP_VERSION}-${ASYNC_MODE}.so" COMPILE_EXTENSION_CLI="$CLI_STAGE/node_modules/@php-wasm/compile-extension/cli.js" npm install --prefix "$CLI_STAGE" --no-audit --no-fund --ignore-scripts "$COMPILE_EXTENSION_PACKAGE" +patch_compile_extension_cli "$COMPILE_EXTENSION_CLI" ( cd "$PLAYGROUND_REPO" - node "$COMPILE_EXTENSION_CLI" \ + COMPILE_EXTENSION_SKIP_IMAGE_BUILD=1 node "$COMPILE_EXTENSION_CLI" \ --source "$SRC_STAGE" \ --name wp_mysql_parser \ --php-versions "$PHP_VERSION" \