From 386b3ce4b347552942bf65c888471f5520b4cb9d Mon Sep 17 00:00:00 2001 From: Hug0-Drelon Date: Mon, 15 Jun 2026 18:17:11 +0200 Subject: [PATCH 01/10] Use Node version for node_modules cache key. --- e2e/action.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/e2e/action.yml b/e2e/action.yml index f3f12c9..68c4060 100644 --- a/e2e/action.yml +++ b/e2e/action.yml @@ -28,6 +28,11 @@ runs: with: node-version: 22 + - name: Get Node.js version + id: node-version + run: echo "NODE_VERSION=$(node -v)" >> "$GITHUB_OUTPUT" + shell: bash + - name: Set up package-lock.json run: | npm install --package-lock-only --no-audit @@ -38,7 +43,7 @@ runs: uses: actions/cache@v4 with: path: '**/node_modules' - key: node_modules-${{ runner.os }}-${{ runner.arch }}-${{ steps.node-version.outputs.NODE_VERSION }}-${{ hashFiles('package-lock.json') }} + key: node_modules-${{ runner.os }}-${{ runner.arch }}-${{ steps.node-version.outputs.NODE_VERSION }}-${{ hashFiles('package.json', 'package-lock.json') }} - name: Install dependencies if: ${{ steps.cache-node_modules.outputs.cache-hit != 'true' }} From c50c30fff7173b0240514dddb0f959afff6ef5d0 Mon Sep 17 00:00:00 2001 From: Hug0-Drelon Date: Mon, 15 Jun 2026 18:24:48 +0200 Subject: [PATCH 02/10] Better cache for Playwright binaries, fallback cache key for better perf in CI. --- bin/get-playwright-version.js | 46 +++++++++++++++++++++++++++++++++++ e2e/action.yml | 5 +++- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 bin/get-playwright-version.js diff --git a/bin/get-playwright-version.js b/bin/get-playwright-version.js new file mode 100644 index 0000000..df2967f --- /dev/null +++ b/bin/get-playwright-version.js @@ -0,0 +1,46 @@ +'use strict'; + +/** + * Resolves the installed @playwright/test version for CI caching. + * + * Used by polylang/actions/e2e to build the Playwright browser cache key. Must be + * run from the consumer repository root (where npm ci has installed dependencies). + * + * Resolution order: + * 1. node_modules/@playwright/test/package.json (preferred — matches installed binaries) + * 2. package-lock.json (npm lockfile v2/v3 packages path, or legacy dependencies) + * + * @example + * PLAYWRIGHT_VERSION=$(node "${{ github.action_path }}/../bin/get-playwright-version.js") + * + * Output: + * - Prints the semver string to stdout on success. + * - Exits with code 1 and writes an error message to stderr when the version cannot be determined. + */ + +const tryRequire = ( id ) => { + try { + return require( id ); + } catch { + return null; + } +}; + +const fromPackage = tryRequire( '@playwright/test/package.json' ); + +if ( fromPackage?.version ) { + process.stdout.write( fromPackage.version ); + process.exit( 0 ); +} + +const lock = tryRequire( './package-lock.json' ); +const fromLock = lock?.packages?.['node_modules/@playwright/test']?.version + || lock?.dependencies?.['@playwright/test']?.version + || ''; + +if ( ! fromLock ) { + process.stderr.write( 'Could not determine @playwright/test version.' ); + process.exit( 1 ); +} + +process.stdout.write( fromLock ); diff --git a/e2e/action.yml b/e2e/action.yml index 68c4060..6fca736 100644 --- a/e2e/action.yml +++ b/e2e/action.yml @@ -71,7 +71,8 @@ runs: - name: Get installed Playwright version id: playwright-version run: | - echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./package-lock.json').packages['node_modules/@playwright/test'].version)")" >> $GITHUB_ENV + PLAYWRIGHT_VERSION=$(node "${{ github.action_path }}/../bin/get-playwright-version.js") + echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> "$GITHUB_ENV" shell: bash - name: Cache Playwright binaries @@ -81,6 +82,8 @@ runs: path: | ~/.cache/ms-playwright key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }} + restore-keys: | + ${{ runner.os }}-playwright- - name: Install Playwright binaries # Only Chromium for the moment. From e33ff84a2ef7decefbbc64e4e43910f07c79a4ab Mon Sep 17 00:00:00 2001 From: Hug0-Drelon Date: Mon, 15 Jun 2026 18:35:50 +0200 Subject: [PATCH 03/10] Improved wp-env docker images caching. --- bin/save-wp-env-docker-images.js | 128 +++++++++++++++++++++++++++++++ e2e/action.yml | 9 ++- 2 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 bin/save-wp-env-docker-images.js diff --git a/bin/save-wp-env-docker-images.js b/bin/save-wp-env-docker-images.js new file mode 100644 index 0000000..64e195f --- /dev/null +++ b/bin/save-wp-env-docker-images.js @@ -0,0 +1,128 @@ +'use strict'; + +/** + * Save wp-env Docker images to a tarball for GitHub Actions caching. + * + * Used by polylang/actions/e2e after `wp-env start`. Resolves the wp-env work + * directory via `wp-env status --json`, then saves images from its docker-compose.yml. + * + * @example + * node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" + * node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" --config=.wp-env.json + * node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" wp-env-image.tar + * + * Options: + * - --config= Custom wp-env config file (same as wp-env --config). + * - First non-flag argument: output tarball path (default: wp-env-image.tar). + * + * Output: + * - Writes the tarball to the output path. + * - Exits with code 1 when the install path or images cannot be resolved. + */ + +const { execFileSync } = require( 'child_process' ); +const fs = require( 'fs' ); +const path = require( 'path' ); + +const DEFAULT_OUTPUT = 'wp-env-image.tar'; +const COMPOSE_FILENAME = 'docker-compose.yml'; + +/** + * @param {string} configPath + * @return {string} + */ +const getInstallPath = ( configPath ) => { + let output; + + try { + output = execFileSync( + 'npm', + [ 'run', 'wp-env', '--', 'status', '--json', ...( configPath ? [ `--config=${ configPath }` ] : [] ) ], + { encoding: 'utf8' } + ); + } catch { + process.stderr.write( 'Could not resolve wp-env install path.\n' ); + process.exit( 1 ); + } + + let status; + + try { + status = JSON.parse( output.trim() ); + } catch { + process.stderr.write( 'Could not parse wp-env status output.\n' ); + process.exit( 1 ); + } + + if ( ! status?.installPath ) { + process.stderr.write( 'wp-env status did not return an install path.\n' ); + process.exit( 1 ); + } + + return status.installPath; +}; + +/** + * @param {string} composeFile + * @return {string[]} + */ +const getComposeImageIds = ( composeFile ) => { + try { + const output = execFileSync( + 'docker', + [ 'compose', '-f', composeFile, 'images', '-q' ], + { encoding: 'utf8' } + ); + + return output + .split( '\n' ) + .map( ( line ) => line.trim() ) + .filter( Boolean ); + } catch { + return []; + } +}; + +/** + * @param {string[]} argv + * @return {{ configPath: string, outputTar: string }} + */ +const parseArgs = ( argv ) => { + let configPath = process.env.WP_ENV_CONFIG_PATH || ''; + let outputTar = DEFAULT_OUTPUT; + + for ( const arg of argv ) { + if ( arg.startsWith( '--config=' ) ) { + configPath = arg.slice( '--config='.length ); + continue; + } + + if ( ! arg.startsWith( '--' ) ) { + outputTar = arg; + } + } + + return { configPath, outputTar }; +}; + +const { configPath, outputTar } = parseArgs( process.argv.slice( 2 ) ); +const installPath = getInstallPath( configPath ); +const composeFile = path.join( installPath, COMPOSE_FILENAME ); + +if ( ! fs.existsSync( composeFile ) ) { + process.stderr.write( `No docker-compose.yml found at ${ composeFile }.\n` ); + process.exit( 1 ); +} + +const imageIds = [ ...new Set( getComposeImageIds( composeFile ) ) ]; + +if ( imageIds.length === 0 ) { + process.stderr.write( 'No wp-env Docker images found to cache.\n' ); + process.exit( 1 ); +} + +execFileSync( + 'docker', + [ 'save', ...imageIds, '-o', outputTar ], + { stdio: 'inherit' } + ); diff --git a/e2e/action.yml b/e2e/action.yml index 6fca736..e143a1d 100644 --- a/e2e/action.yml +++ b/e2e/action.yml @@ -103,7 +103,7 @@ runs: uses: actions/cache@v4 with: path: wp-env-image.tar - key: ${{ runner.os }}-wp-env-${{ env.WP_VERSION }}-${{ inputs.container-cache-key }} + key: ${{ runner.os }}-wp-env-${{ env.WP_VERSION }}-${{ hashFiles('.wp-env.json', '.wp-env.override.json', inputs.wp-env-config-path) }}-${{ inputs.container-cache-key }} - name: Load cached Docker image (if any) if: steps.docker-cache.outputs.cache-hit == 'true' @@ -123,8 +123,11 @@ runs: - name: Save Docker image to cache if: steps.docker-cache.outputs.cache-hit != 'true' run: | - docker image ls - docker save $(docker images --format '{{.Repository}}:{{.Tag}}') -o wp-env-image.tar + if [ -n "${{ inputs.wp-env-config-path }}" ]; then + node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" --config="${{ inputs.wp-env-config-path }}" + else + node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" + fi shell: bash - name: Run Playwright tests with WordPress ${{ env.WP_VERSION }} From 06553d47d808ef4f51c97c0026918f9a52b298a1 Mon Sep 17 00:00:00 2001 From: Hug0-Drelon Date: Thu, 16 Jul 2026 18:29:15 +0200 Subject: [PATCH 04/10] fix(e2e): fall back to wp-env install-path for older env `status --json` is only available in @wordpress/env 11+; older versions still expose install-path. --- bin/save-wp-env-docker-images.js | 51 +++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/bin/save-wp-env-docker-images.js b/bin/save-wp-env-docker-images.js index 64e195f..80c4818 100644 --- a/bin/save-wp-env-docker-images.js +++ b/bin/save-wp-env-docker-images.js @@ -4,7 +4,9 @@ * Save wp-env Docker images to a tarball for GitHub Actions caching. * * Used by polylang/actions/e2e after `wp-env start`. Resolves the wp-env work - * directory via `wp-env status --json`, then saves images from its docker-compose.yml. + * directory via `wp-env status --json` (@wordpress/env 11+), falling back to + * `wp-env install-path` on older versions, then saves images from its + * docker-compose.yml. * * @example * node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" @@ -32,34 +34,49 @@ const COMPOSE_FILENAME = 'docker-compose.yml'; * @return {string} */ const getInstallPath = ( configPath ) => { - let output; + const configArgs = configPath ? [ `--config=${ configPath }` ] : []; try { - output = execFileSync( + const output = execFileSync( 'npm', - [ 'run', 'wp-env', '--', 'status', '--json', ...( configPath ? [ `--config=${ configPath }` ] : [] ) ], + [ 'run', 'wp-env', '--', 'status', '--json', ...configArgs ], { encoding: 'utf8' } ); - } catch { - process.stderr.write( 'Could not resolve wp-env install path.\n' ); - process.exit( 1 ); - } + const jsonStart = output.indexOf( '{' ); + const jsonEnd = output.lastIndexOf( '}' ); - let status; + if ( jsonStart !== -1 && jsonEnd > jsonStart ) { + const status = JSON.parse( output.slice( jsonStart, jsonEnd + 1 ) ); - try { - status = JSON.parse( output.trim() ); + if ( status?.installPath ) { + return status.installPath; + } + } } catch { - process.stderr.write( 'Could not parse wp-env status output.\n' ); - process.exit( 1 ); + // Fall through: `status --json` is only available in @wordpress/env 11+. } - if ( ! status?.installPath ) { - process.stderr.write( 'wp-env status did not return an install path.\n' ); - process.exit( 1 ); + try { + const output = execFileSync( + 'npm', + [ 'run', 'wp-env', '--', 'install-path', ...configArgs ], + { encoding: 'utf8' } + ); + // May include npm run banners; keep the first absolute path line. + const installPath = output + .split( '\n' ) + .map( ( entry ) => entry.trim() ) + .find( ( entry ) => entry.startsWith( '/' ) || /^[A-Za-z]:[\\/]/.test( entry ) ); + + if ( installPath ) { + return installPath; + } + } catch { + // Handled below. } - return status.installPath; + process.stderr.write( 'Could not resolve wp-env install path.\n' ); + process.exit( 1 ); }; /** From 76aded48eed42fe287d53f9fca004c2a69597d4b Mon Sep 17 00:00:00 2001 From: Hug0-Drelon Date: Thu, 16 Jul 2026 18:43:16 +0200 Subject: [PATCH 05/10] fix(e2e): resolve Playwright version from consumer cwd require() looked up packages from the action script path, not the repo root. --- bin/get-playwright-version.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/bin/get-playwright-version.js b/bin/get-playwright-version.js index df2967f..719239e 100644 --- a/bin/get-playwright-version.js +++ b/bin/get-playwright-version.js @@ -18,22 +18,32 @@ * - Exits with code 1 and writes an error message to stderr when the version cannot be determined. */ -const tryRequire = ( id ) => { +const fs = require( 'fs' ); +const path = require( 'path' ); + +/** + * @param {string} filePath + * @return {Object|null} + */ +const tryReadJson = ( filePath ) => { try { - return require( id ); + return JSON.parse( fs.readFileSync( filePath, 'utf8' ) ); } catch { return null; } }; -const fromPackage = tryRequire( '@playwright/test/package.json' ); +const root = process.cwd(); +const fromPackage = tryReadJson( + path.join( root, 'node_modules', '@playwright', 'test', 'package.json' ) +); if ( fromPackage?.version ) { process.stdout.write( fromPackage.version ); process.exit( 0 ); } -const lock = tryRequire( './package-lock.json' ); +const lock = tryReadJson( path.join( root, 'package-lock.json' ) ); const fromLock = lock?.packages?.['node_modules/@playwright/test']?.version || lock?.dependencies?.['@playwright/test']?.version || ''; From 3e2dc617bd8e7bbebd30880ebfa30a84fe5236ca Mon Sep 17 00:00:00 2001 From: Hug0-Drelon Date: Thu, 16 Jul 2026 19:12:18 +0200 Subject: [PATCH 06/10] perf(e2e): skip package-lock generation when lockfile exists Use npm ci when package-lock.json is present; otherwise npm install. --- e2e/action.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/e2e/action.yml b/e2e/action.yml index e143a1d..d080999 100644 --- a/e2e/action.yml +++ b/e2e/action.yml @@ -33,7 +33,9 @@ runs: run: echo "NODE_VERSION=$(node -v)" >> "$GITHUB_OUTPUT" shell: bash + # Repos without a committed lockfile (e.g. Polylang) need one for npm ci / cache key. - name: Set up package-lock.json + if: hashFiles('package-lock.json') == '' run: | npm install --package-lock-only --no-audit shell: bash @@ -48,7 +50,11 @@ runs: - name: Install dependencies if: ${{ steps.cache-node_modules.outputs.cache-hit != 'true' }} run: | + if [ -f package-lock.json ]; then npm ci + else + npm install --no-audit + fi shell: bash # Must install PHP deps before wp-env is running to prevent errors. From 852366d69b75f2bdb7af8ab6b299ba500b8de6b8 Mon Sep 17 00:00:00 2001 From: Hug0-Drelon Date: Thu, 16 Jul 2026 19:15:25 +0200 Subject: [PATCH 07/10] perf(e2e): use file existence check for package-lock setup Avoid hashing the lockfile just to decide whether to generate it. --- e2e/action.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e/action.yml b/e2e/action.yml index d080999..8fde8f7 100644 --- a/e2e/action.yml +++ b/e2e/action.yml @@ -35,9 +35,10 @@ runs: # Repos without a committed lockfile (e.g. Polylang) need one for npm ci / cache key. - name: Set up package-lock.json - if: hashFiles('package-lock.json') == '' run: | - npm install --package-lock-only --no-audit + if [ ! -f package-lock.json ]; then + npm install --package-lock-only --no-audit + fi shell: bash - name: Cache node_modules From b18b2ac207a7aff2f69040aabadbf5c355159bcb Mon Sep 17 00:00:00 2001 From: Hug0-Drelon Date: Thu, 16 Jul 2026 19:27:58 +0200 Subject: [PATCH 08/10] perf(e2e): drop wp-env Docker image tar cache Restore and docker load cost more than they save; wp-env start stays slow either way. --- bin/save-wp-env-docker-images.js | 145 ------------------------------- e2e/action.yml | 36 +------- 2 files changed, 1 insertion(+), 180 deletions(-) delete mode 100644 bin/save-wp-env-docker-images.js diff --git a/bin/save-wp-env-docker-images.js b/bin/save-wp-env-docker-images.js deleted file mode 100644 index 80c4818..0000000 --- a/bin/save-wp-env-docker-images.js +++ /dev/null @@ -1,145 +0,0 @@ -'use strict'; - -/** - * Save wp-env Docker images to a tarball for GitHub Actions caching. - * - * Used by polylang/actions/e2e after `wp-env start`. Resolves the wp-env work - * directory via `wp-env status --json` (@wordpress/env 11+), falling back to - * `wp-env install-path` on older versions, then saves images from its - * docker-compose.yml. - * - * @example - * node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" - * node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" --config=.wp-env.json - * node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" wp-env-image.tar - * - * Options: - * - --config= Custom wp-env config file (same as wp-env --config). - * - First non-flag argument: output tarball path (default: wp-env-image.tar). - * - * Output: - * - Writes the tarball to the output path. - * - Exits with code 1 when the install path or images cannot be resolved. - */ - -const { execFileSync } = require( 'child_process' ); -const fs = require( 'fs' ); -const path = require( 'path' ); - -const DEFAULT_OUTPUT = 'wp-env-image.tar'; -const COMPOSE_FILENAME = 'docker-compose.yml'; - -/** - * @param {string} configPath - * @return {string} - */ -const getInstallPath = ( configPath ) => { - const configArgs = configPath ? [ `--config=${ configPath }` ] : []; - - try { - const output = execFileSync( - 'npm', - [ 'run', 'wp-env', '--', 'status', '--json', ...configArgs ], - { encoding: 'utf8' } - ); - const jsonStart = output.indexOf( '{' ); - const jsonEnd = output.lastIndexOf( '}' ); - - if ( jsonStart !== -1 && jsonEnd > jsonStart ) { - const status = JSON.parse( output.slice( jsonStart, jsonEnd + 1 ) ); - - if ( status?.installPath ) { - return status.installPath; - } - } - } catch { - // Fall through: `status --json` is only available in @wordpress/env 11+. - } - - try { - const output = execFileSync( - 'npm', - [ 'run', 'wp-env', '--', 'install-path', ...configArgs ], - { encoding: 'utf8' } - ); - // May include npm run banners; keep the first absolute path line. - const installPath = output - .split( '\n' ) - .map( ( entry ) => entry.trim() ) - .find( ( entry ) => entry.startsWith( '/' ) || /^[A-Za-z]:[\\/]/.test( entry ) ); - - if ( installPath ) { - return installPath; - } - } catch { - // Handled below. - } - - process.stderr.write( 'Could not resolve wp-env install path.\n' ); - process.exit( 1 ); -}; - -/** - * @param {string} composeFile - * @return {string[]} - */ -const getComposeImageIds = ( composeFile ) => { - try { - const output = execFileSync( - 'docker', - [ 'compose', '-f', composeFile, 'images', '-q' ], - { encoding: 'utf8' } - ); - - return output - .split( '\n' ) - .map( ( line ) => line.trim() ) - .filter( Boolean ); - } catch { - return []; - } -}; - -/** - * @param {string[]} argv - * @return {{ configPath: string, outputTar: string }} - */ -const parseArgs = ( argv ) => { - let configPath = process.env.WP_ENV_CONFIG_PATH || ''; - let outputTar = DEFAULT_OUTPUT; - - for ( const arg of argv ) { - if ( arg.startsWith( '--config=' ) ) { - configPath = arg.slice( '--config='.length ); - continue; - } - - if ( ! arg.startsWith( '--' ) ) { - outputTar = arg; - } - } - - return { configPath, outputTar }; -}; - -const { configPath, outputTar } = parseArgs( process.argv.slice( 2 ) ); -const installPath = getInstallPath( configPath ); -const composeFile = path.join( installPath, COMPOSE_FILENAME ); - -if ( ! fs.existsSync( composeFile ) ) { - process.stderr.write( `No docker-compose.yml found at ${ composeFile }.\n` ); - process.exit( 1 ); -} - -const imageIds = [ ...new Set( getComposeImageIds( composeFile ) ) ]; - -if ( imageIds.length === 0 ) { - process.stderr.write( 'No wp-env Docker images found to cache.\n' ); - process.exit( 1 ); -} - -execFileSync( - 'docker', - [ 'save', ...imageIds, '-o', outputTar ], - { stdio: 'inherit' } - ); diff --git a/e2e/action.yml b/e2e/action.yml index 8fde8f7..819fdf5 100644 --- a/e2e/action.yml +++ b/e2e/action.yml @@ -3,11 +3,6 @@ name: Run Playwright e2e Tests description: Installs WordPress, starts the server, builds dependencies and run Playwright tests. Currently runs on PHP 8.0 with WordPress latest release. inputs: - container-cache-key: - description: Container cache key. Used to cache wp-env container. - required: false - type: string - default: 'no-external-cache-key' wp-env-config-path: description: Path to a custom wp-env config file passed to wp-env --config. Default is '' (wp-env default discovery). required: false @@ -99,25 +94,6 @@ runs: shell: bash if: steps.playwright-cache.outputs.cache-hit != 'true' - - name: Get WordPress latest version - id: wp-version - run: | - echo "WP_VERSION=$(node -e "fetch('https://api.wordpress.org/core/version-check/1.7/').then(r => r.json()).then(data => console.log(data.offers[0].current))")" >> $GITHUB_ENV - shell: bash - - - name: Cache wp-env Docker image - id: docker-cache - uses: actions/cache@v4 - with: - path: wp-env-image.tar - key: ${{ runner.os }}-wp-env-${{ env.WP_VERSION }}-${{ hashFiles('.wp-env.json', '.wp-env.override.json', inputs.wp-env-config-path) }}-${{ inputs.container-cache-key }} - - - name: Load cached Docker image (if any) - if: steps.docker-cache.outputs.cache-hit == 'true' - run: | - docker load -i wp-env-image.tar - shell: bash - - name: Install WordPress and start the server run: | if [ -n "${{ inputs.wp-env-config-path }}" ]; then @@ -127,17 +103,7 @@ runs: fi shell: bash - - name: Save Docker image to cache - if: steps.docker-cache.outputs.cache-hit != 'true' - run: | - if [ -n "${{ inputs.wp-env-config-path }}" ]; then - node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" --config="${{ inputs.wp-env-config-path }}" - else - node "${{ github.action_path }}/../bin/save-wp-env-docker-images.js" - fi - shell: bash - - - name: Run Playwright tests with WordPress ${{ env.WP_VERSION }} + - name: Run Playwright tests run: ${{ inputs.playwright-cmd }} shell: bash From 94a73d34a6df2cce0c7f90a6635f2cc0d493f98b Mon Sep 17 00:00:00 2001 From: Hug0-Drelon Date: Fri, 17 Jul 2026 09:37:38 +0200 Subject: [PATCH 09/10] perf(e2e): cache wp-env home directory Reuse downloaded WordPress sources across CI runs; pin WP_ENV_HOME for snap-safe path. --- e2e/action.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/e2e/action.yml b/e2e/action.yml index 819fdf5..04637ad 100644 --- a/e2e/action.yml +++ b/e2e/action.yml @@ -3,6 +3,11 @@ name: Run Playwright e2e Tests description: Installs WordPress, starts the server, builds dependencies and run Playwright tests. Currently runs on PHP 8.0 with WordPress latest release. inputs: + wp-env-cache-key: + description: Extra cache key fragment for the wp-env home directory. Use for inputs not reflected in .wp-env.json (e.g. WooCommerce version, Polylang Pro commit). + required: false + type: string + default: 'default' wp-env-config-path: description: Path to a custom wp-env config file passed to wp-env --config. Default is '' (wp-env default discovery). required: false @@ -94,6 +99,32 @@ runs: shell: bash if: steps.playwright-cache.outputs.cache-hit != 'true' + # Pin home so CI does not depend on /snap detection (~/wp-env vs ~/.wp-env). + # Same WP_ENV_HOME behavior in @wordpress/env 10 and 11. + - name: Set wp-env home directory + run: echo "WP_ENV_HOME=${HOME}/.wp-env" >> "$GITHUB_ENV" + shell: bash + + - name: Get @wordpress/env version + id: wp-env-version + run: | + echo "WP_ENV_VERSION=$(node -e "console.log(require('@wordpress/env/package.json').version)")" >> "$GITHUB_OUTPUT" + shell: bash + + # When core is "latest" (null), bust the workdir cache when WordPress.org ships a new release. + - name: Get WordPress latest version + id: wp-version + run: | + echo "WP_VERSION=$(node -e "fetch('https://api.wordpress.org/core/version-check/1.7/').then(r => r.json()).then(data => console.log(data.offers[0].current))")" >> "$GITHUB_ENV" + shell: bash + + # Key: {os}-wp-env-home-{@wordpress/env}-{WP latest}-{hash(.wp-env.json)}-{hash(custom config)|no-custom-config}-{wp-env-cache-key} + - name: Cache wp-env home + uses: actions/cache@v4 + with: + path: ${{ env.WP_ENV_HOME }} + key: ${{ runner.os }}-wp-env-home-${{ steps.wp-env-version.outputs.WP_ENV_VERSION }}-${{ env.WP_VERSION }}-${{ hashFiles('.wp-env.json') }}-${{ inputs.wp-env-config-path != '' && hashFiles(inputs.wp-env-config-path) || 'no-custom-config' }}-${{ inputs.wp-env-cache-key }} + - name: Install WordPress and start the server run: | if [ -n "${{ inputs.wp-env-config-path }}" ]; then From 626ea50b1fe9291abff388389a5945669484683a Mon Sep 17 00:00:00 2001 From: Hug0-Drelon Date: Fri, 17 Jul 2026 13:08:25 +0200 Subject: [PATCH 10/10] fix(e2e): reconfigure wp-env after home cache restore Restored workdirs keep config_checksum while MySQL volumes are empty, so skip install breaks REST. --- e2e/action.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/e2e/action.yml b/e2e/action.yml index 04637ad..0baf64b 100644 --- a/e2e/action.yml +++ b/e2e/action.yml @@ -120,11 +120,21 @@ runs: # Key: {os}-wp-env-home-{@wordpress/env}-{WP latest}-{hash(.wp-env.json)}-{hash(custom config)|no-custom-config}-{wp-env-cache-key} - name: Cache wp-env home + id: wp-env-home-cache uses: actions/cache@v4 with: path: ${{ env.WP_ENV_HOME }} key: ${{ runner.os }}-wp-env-home-${{ steps.wp-env-version.outputs.WP_ENV_VERSION }}-${{ env.WP_VERSION }}-${{ hashFiles('.wp-env.json') }}-${{ inputs.wp-env-config-path != '' && hashFiles(inputs.wp-env-config-path) || 'no-custom-config' }}-${{ inputs.wp-env-cache-key }} + # WP_ENV_HOME holds WordPress sources + wp-env-cache.json, not MySQL volumes. + # On a cache hit the config checksum makes wp-env skip `wp core install` against + # empty DB volumes → REST API / rewrite failures. Drop the checksum so start + # reconfigures while still reusing downloaded sources (wp-env 10 and 11). + - name: Force wp-env reconfigure after home cache restore + if: steps.wp-env-home-cache.outputs.cache-hit == 'true' + run: find "${WP_ENV_HOME}" -name 'wp-env-cache.json' -delete + shell: bash + - name: Install WordPress and start the server run: | if [ -n "${{ inputs.wp-env-config-path }}" ]; then