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.
+

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'
+ );
+ } );
+ } );
} );