From 4eb6c31bce6b86430d568b7623fa668012a465a3 Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Tue, 29 Sep 2026 01:33:56 -0400 Subject: [PATCH] Add an option to choose the MariaDB version used by wp-env. --- packages/env/CHANGELOG.md | 4 ++ packages/env/README.md | 18 +++++ .../env/lib/build-docker-compose-config.js | 8 ++- packages/env/lib/config/config.js | 20 +++++- packages/env/lib/config/parse-config.js | 1 + .../config/test/__snapshots__/config.js.snap | 2 + packages/env/lib/config/test/config.js | 71 +++++++++++++++++++ packages/env/lib/config/validate-config.js | 38 ++++++++++ .../env/test/build-docker-compose-config.js | 39 ++++++++++ 9 files changed, 198 insertions(+), 3 deletions(-) diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index 53a9d9ce91b77d..da8d4826addddb 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### New Features + +- Add a `mariadbVersion` option and `WP_ENV_MARIADB_VERSION` environment variable to choose the MariaDB version used by the Docker runtime ([#83751](https://github.com/WordPress/gutenberg/pull/83751)). + ### Enhancements - The `composer` service uses the `composer:2` image instead of the floating `composer` image, so a new major version of Composer can't break `wp-env run composer` ([#84777](https://github.com/WordPress/gutenberg/pull/84777)). diff --git a/packages/env/README.md b/packages/env/README.md index d5667f6753a147..ca3fda293d9387 100644 --- a/packages/env/README.md +++ b/packages/env/README.md @@ -385,6 +385,7 @@ You can customize the WordPress installation, plugins and themes that the develo | -------------- | -------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | `"core"` | `string\|null` | `null` | The WordPress installation to use. If `null` is specified, `wp-env` will use the latest production release of WordPress. | | `"phpVersion"` | `string\|null` | `null` | The PHP version to use. If `null` is specified, `wp-env` will use the default version used with production release of WordPress. | +| `"mariadbVersion"` | `string\|null` | `null` | The MariaDB version to use: `"lts"`, `"latest"`, or a version such as `"10.11"`. If `null` is specified, `latest` is used. | | `"plugins"` | `string[]` | `[]` | A list of plugins to install and activate in the environment. | | `"themes"` | `string[]` | `[]` | A list of themes to install in the environment. | | `"port"` | `integer` | `8888` (`8889` for the tests instance) | The primary port number to use for the installation. You'll access the instance through the port: 'http://localhost:8888'. | @@ -573,4 +574,21 @@ You can tell `wp-env` to use a specific PHP version for compatibility and testin } ``` +#### Specific MariaDB Version + +You can tell `wp-env` which MariaDB version the database should run, for compatibility and testing. Use `"lts"`, `"latest"`, or a version such as `"10.11"` or `"11.4.2"`. This can also be set via the environment variable `WP_ENV_MARIADB_VERSION`, which applies to every environment. + +```json +{ + "mariadbVersion": "10.11", + "plugins": [ "." ] +} +``` + +MariaDB cannot start on a database written by a newer version. When switching to a lower version, run `wp-env destroy` first. It removes the environment's Docker containers, volumes, networks, and local files, not only the databases, so everything is recreated on the next start. + +**Note:** With a `mariadbVersion` older than 11.4, `wp db` commands in the `cli` containers fail, and `wp-env clean` leaves the database as it is: the MariaDB client in the CLI image requires TLS when `phpVersion` is unset or 8.1 or newer, and MariaDB servers before 11.4 do not offer it. WordPress itself is not affected. To start over with an empty database, run `wp-env destroy`. + +**Note:** MariaDB 5.5 images are not published for ARM processors. Apple silicon devices can still run version 5.5 under emulation by setting `DOCKER_DEFAULT_PLATFORM=linux/amd64` before running `wp-env start`. Be aware that every container in the environment runs under emulation, not only the database, so the whole environment runs more slowly. +

Code is Poetry.

diff --git a/packages/env/lib/build-docker-compose-config.js b/packages/env/lib/build-docker-compose-config.js index 471b24a8df06bb..08ab19c14c2c63 100644 --- a/packages/env/lib/build-docker-compose-config.js +++ b/packages/env/lib/build-docker-compose-config.js @@ -174,7 +174,9 @@ module.exports = function buildDockerComposeConfig( config ) { version: '3.7', services: { mysql: { - image: 'mariadb', + image: config.env.development.mariadbVersion + ? `mariadb:${ config.env.development.mariadbVersion }` + : 'mariadb', ports: [ '3306' ], environment: { MYSQL_ROOT_PASSWORD: @@ -184,7 +186,9 @@ module.exports = function buildDockerComposeConfig( config ) { volumes: [ 'mysql:/var/lib/mysql' ], }, 'tests-mysql': { - image: 'mariadb', + image: config.env.tests.mariadbVersion + ? `mariadb:${ config.env.tests.mariadbVersion }` + : 'mariadb', ports: [ '3306' ], environment: { MYSQL_ROOT_PASSWORD: diff --git a/packages/env/lib/config/config.js b/packages/env/lib/config/config.js index 784ad2e9af2728..17208915ac3b7a 100644 --- a/packages/env/lib/config/config.js +++ b/packages/env/lib/config/config.js @@ -10,7 +10,11 @@ const os = require( 'os' ); * Internal dependencies */ const detectDirectoryType = require( './detect-directory-type' ); -const { validateConfig, ValidationError } = require( './validate-config' ); +const { + validateConfig, + checkMariaDBVersion, + ValidationError, +} = require( './validate-config' ); const readRawConfigFile = require( './read-raw-config-file' ); const parseConfig = require( './parse-config' ); const md5 = require( '../md5' ); @@ -27,6 +31,7 @@ const md5 = require( '../md5' ); * @property {Object.} env Specific config for different environments. * @property {boolean} debug True if debug mode is enabled. * @property {string} phpVersion Version of PHP to use in the environments, of the format 0.0. + * @property {string} mariadbVersion Version of MariaDB to use in the environments: "lts", "latest", or of the format 0.0. Null uses the default. */ /** @@ -71,6 +76,7 @@ module.exports = async function readConfig( configPath ) { const defaultConfiguration = { core: null, phpVersion: null, + mariadbVersion: null, plugins: [], themes: [], port: 8888, @@ -261,6 +267,18 @@ function withOverrides( config ) { config.env.tests.phpVersion = process.env.WP_ENV_PHP_VERSION || config.env.tests.phpVersion; + // Override MariaDB version with environment variable. + if ( process.env.WP_ENV_MARIADB_VERSION ) { + checkMariaDBVersion( + 'environment variable', + 'WP_ENV_MARIADB_VERSION', + process.env.WP_ENV_MARIADB_VERSION + ); + config.env.development.mariadbVersion = + process.env.WP_ENV_MARIADB_VERSION; + config.env.tests.mariadbVersion = process.env.WP_ENV_MARIADB_VERSION; + } + const updateEnvUrl = ( configKey ) => { [ 'development', 'tests' ].forEach( ( envKey ) => { try { diff --git a/packages/env/lib/config/parse-config.js b/packages/env/lib/config/parse-config.js index a0877fcb059451..d932f04da4d888 100644 --- a/packages/env/lib/config/parse-config.js +++ b/packages/env/lib/config/parse-config.js @@ -36,6 +36,7 @@ module.exports = function parseConfig( config, options ) { return { port: config.port, phpVersion: config.phpVersion, + mariadbVersion: config.mariadbVersion, coreSource: includeTestsPath( parseSourceString( config.core, options ), options diff --git a/packages/env/lib/config/test/__snapshots__/config.js.snap b/packages/env/lib/config/test/__snapshots__/config.js.snap index db2748f02c3329..5fc280ad998ec7 100644 --- a/packages/env/lib/config/test/__snapshots__/config.js.snap +++ b/packages/env/lib/config/test/__snapshots__/config.js.snap @@ -22,6 +22,7 @@ Object { }, "coreSource": null, "mappings": Object {}, + "mariadbVersion": null, "phpVersion": null, "pluginSources": Array [], "port": 2000, @@ -44,6 +45,7 @@ Object { }, "coreSource": null, "mappings": Object {}, + "mariadbVersion": null, "phpVersion": null, "pluginSources": Array [], "port": 1000, diff --git a/packages/env/lib/config/test/config.js b/packages/env/lib/config/test/config.js index c9d620b12d2185..bc5235e440e0a5 100644 --- a/packages/env/lib/config/test/config.js +++ b/packages/env/lib/config/test/config.js @@ -705,6 +705,77 @@ describe( 'readConfig', () => { } ); } ); + describe( 'mariadbVersion parsing', () => { + afterEach( () => { + delete process.env.WP_ENV_MARIADB_VERSION; + } ); + + it( 'should default to null', async () => { + readFile.mockImplementation( () => + Promise.resolve( JSON.stringify( {} ) ) + ); + const config = await readConfig( '.wp-env.json' ); + expect( config.env.development.mariadbVersion ).toBeNull(); + expect( config.env.tests.mariadbVersion ).toBeNull(); + } ); + + it( 'should parse mariadbVersion at the root and per environment', async () => { + readFile.mockImplementation( () => + Promise.resolve( + JSON.stringify( { + mariadbVersion: '10.11', + env: { tests: { mariadbVersion: 'latest' } }, + } ) + ) + ); + const config = await readConfig( '.wp-env.json' ); + expect( config.env.development.mariadbVersion ).toBe( '10.11' ); + expect( config.env.tests.mariadbVersion ).toBe( 'latest' ); + } ); + + it( 'should throw a validation error for an invalid mariadbVersion', async () => { + readFile.mockImplementation( () => + Promise.resolve( + JSON.stringify( { + env: { tests: { mariadbVersion: 'LTS' } }, + } ) + ) + ); + await expect( readConfig( '.wp-env.json' ) ).rejects.toEqual( + new ValidationError( + 'Invalid .wp-env.json: "env.tests.mariadbVersion" must be "lts", "latest", or a version such as "10.11" or "11.4.2".' + ) + ); + } ); + + it( 'should override mariadbVersion in every environment with WP_ENV_MARIADB_VERSION', async () => { + readFile.mockImplementation( () => + Promise.resolve( + JSON.stringify( { + mariadbVersion: '10.11', + env: { tests: { mariadbVersion: 'latest' } }, + } ) + ) + ); + process.env.WP_ENV_MARIADB_VERSION = '10.3'; + const config = await readConfig( '.wp-env.json' ); + expect( config.env.development.mariadbVersion ).toBe( '10.3' ); + expect( config.env.tests.mariadbVersion ).toBe( '10.3' ); + } ); + + it( 'should throw a validation error for an invalid WP_ENV_MARIADB_VERSION', async () => { + readFile.mockImplementation( () => + Promise.resolve( JSON.stringify( {} ) ) + ); + process.env.WP_ENV_MARIADB_VERSION = 'LTS'; + await expect( readConfig( '.wp-env.json' ) ).rejects.toEqual( + new ValidationError( + 'Invalid environment variable: "WP_ENV_MARIADB_VERSION" must be "lts", "latest", or a version such as "10.11" or "11.4.2".' + ) + ); + } ); + } ); + describe( 'port number parsing', () => { it( 'should throw a validaton error if the ports are not numbers', async () => { expect.assertions( 10 ); diff --git a/packages/env/lib/config/validate-config.js b/packages/env/lib/config/validate-config.js index c3aa849e6322d1..fc17aeddfe550c 100644 --- a/packages/env/lib/config/validate-config.js +++ b/packages/env/lib/config/validate-config.js @@ -83,10 +83,48 @@ function validateConfig( config, envLocation ) { ); } + if ( + config.mariadbVersion !== undefined && + config.mariadbVersion !== null + ) { + checkMariaDBVersion( + '.wp-env.json', + `${ envPrefix }mariadbVersion`, + config.mariadbVersion + ); + } + return config; } +/** + * Validates a MariaDB version and throws if it isn't valid. Accepts the + * "lts" and "latest" image tags or a numeric version such as "10.11". + * + * @param {string} source Where the value came from, for the error message. + * @param {string} configKey The configuration key we're validating. + * @param {string} version The version that we're checking. + */ +function checkMariaDBVersion( source, configKey, version ) { + if ( typeof version !== 'string' ) { + throw new ValidationError( + `Invalid ${ source }: "${ configKey }" must be a string.` + ); + } + + if ( version === 'lts' || version === 'latest' ) { + return; + } + + if ( ! /^[0-9]+(?:\.[0-9]+)*$/.test( version ) ) { + throw new ValidationError( + `Invalid ${ source }: "${ configKey }" must be "lts", "latest", or a version such as "10.11" or "11.4.2".` + ); + } +} + module.exports = { validateConfig, + checkMariaDBVersion, ValidationError, }; diff --git a/packages/env/test/build-docker-compose-config.js b/packages/env/test/build-docker-compose-config.js index b839d71ebcd14f..9993599b6dd0e4 100644 --- a/packages/env/test/build-docker-compose-config.js +++ b/packages/env/test/build-docker-compose-config.js @@ -115,4 +115,43 @@ describe( 'buildDockerComposeConfig', () => { expectedVolumes ); } ); + + describe( 'mariadbVersion', () => { + it( 'uses the floating mariadb image by default', () => { + const config = buildDockerComposeConfig( { + workDirectoryPath: '/path', + env: { development: CONFIG, tests: CONFIG }, + } ); + + expect( config.services.mysql.image ).toBe( 'mariadb' ); + expect( config.services[ 'tests-mysql' ].image ).toBe( 'mariadb' ); + } ); + + it( 'treats null the same as unset', () => { + const config = buildDockerComposeConfig( { + workDirectoryPath: '/path', + env: { + development: { ...CONFIG, mariadbVersion: null }, + tests: { ...CONFIG, mariadbVersion: null }, + }, + } ); + + expect( config.services.mysql.image ).toBe( 'mariadb' ); + } ); + + it( 'uses each environment’s own version', () => { + const config = buildDockerComposeConfig( { + workDirectoryPath: '/path', + env: { + development: { ...CONFIG, mariadbVersion: '10.6.4' }, + tests: { ...CONFIG, mariadbVersion: 'latest' }, + }, + } ); + + expect( config.services.mysql.image ).toBe( 'mariadb:10.6.4' ); + expect( config.services[ 'tests-mysql' ].image ).toBe( + 'mariadb:latest' + ); + } ); + } ); } );