Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions bin/get-playwright-version.js
Original file line number Diff line number Diff line change
@@ -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 );
128 changes: 128 additions & 0 deletions bin/save-wp-env-docker-images.js
Original file line number Diff line number Diff line change
@@ -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=<path> 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' }
);
44 changes: 33 additions & 11 deletions e2e/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ inputs:
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
type: string
default: ''
playwright-cmd:
description: CLI command to run playwright tests. Default is 'npm run test:e2e'.
required: false
type: string
default: 'npm run test:e2e'

runs:
using: 'composite'
Expand All @@ -18,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
Expand All @@ -28,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' }}
Expand Down Expand Up @@ -56,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
Expand All @@ -66,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.
Expand All @@ -85,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'
Expand All @@ -95,21 +113,25 @@ runs:

- name: Install WordPress and start the server
run: |
npm run wp-env start
if [ -n "${{ inputs.wp-env-config-path }}" ]; then
npm run wp-env start -- --config="${{ inputs.wp-env-config-path }}"
else
npm run wp-env start
fi
shell: bash
env:
WP_ENV_PHP_VERSION: '8.0'

- 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
run: |
npm run test:e2e
- name: Run Playwright tests with WordPress ${{ env.WP_VERSION }}
run: ${{ inputs.playwright-cmd }}
shell: bash

- name: Upload Playwright report
Expand Down