@wordpress/env 11.0.1-next.v.0 → 11.0.1-next.v.20260206T143.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -9
- package/lib/cli.js +51 -5
- package/lib/commands/clean.js +10 -3
- package/lib/commands/cleanup.js +70 -0
- package/lib/commands/destroy.js +18 -13
- package/lib/commands/index.js +4 -0
- package/lib/commands/logs.js +3 -1
- package/lib/commands/reset.js +42 -0
- package/lib/commands/run.js +3 -1
- package/lib/commands/start.js +36 -1
- package/lib/commands/stop.js +3 -1
- package/lib/config/get-config-from-environment-vars.js +2 -5
- package/lib/config/parse-config.js +8 -0
- package/lib/config/test/__snapshots__/config-integration.js.snap +8 -0
- package/lib/config/test/parse-config.js +2 -0
- package/lib/runtime/docker/build-docker-compose-config.js +29 -2
- package/lib/runtime/docker/index.js +57 -25
- package/lib/runtime/docker/wordpress.js +0 -15
- package/lib/runtime/errors.js +11 -0
- package/lib/runtime/index.js +40 -18
- package/lib/runtime/playground/blueprint-builder.js +6 -14
- package/lib/runtime/playground/index.js +32 -4
- package/lib/test/build-docker-compose-config.js +83 -0
- package/lib/test/cli.js +47 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -186,7 +186,7 @@ To reset the database:
|
|
|
186
186
|
**⚠️ WARNING: This will permanently delete any posts, pages, media, etc. in the local WordPress installation.**
|
|
187
187
|
|
|
188
188
|
```sh
|
|
189
|
-
$ wp-env
|
|
189
|
+
$ wp-env reset all
|
|
190
190
|
$ wp-env start
|
|
191
191
|
```
|
|
192
192
|
|
|
@@ -340,15 +340,15 @@ Options:
|
|
|
340
340
|
--debug Enable debug output. [boolean] [default: false]
|
|
341
341
|
```
|
|
342
342
|
|
|
343
|
-
### `wp-env
|
|
343
|
+
### `wp-env reset [environment]`
|
|
344
344
|
|
|
345
345
|
```sh
|
|
346
|
-
wp-env
|
|
346
|
+
wp-env reset [environment]
|
|
347
347
|
|
|
348
|
-
|
|
348
|
+
Resets the WordPress databases.
|
|
349
349
|
|
|
350
350
|
Positionals:
|
|
351
|
-
environment Which environments' databases to
|
|
351
|
+
environment Which environments' databases to reset.
|
|
352
352
|
[string] [choices: "all", "development", "tests"] [default: "tests"]
|
|
353
353
|
|
|
354
354
|
Options:
|
|
@@ -469,17 +469,32 @@ To set the permalink to the year, month, and post name:
|
|
|
469
469
|
wp-env run cli "wp rewrite structure /%year%/%monthnum%/%postname%/"
|
|
470
470
|
```
|
|
471
471
|
|
|
472
|
+
### `wp-env cleanup`
|
|
473
|
+
|
|
474
|
+
```sh
|
|
475
|
+
wp-env cleanup
|
|
476
|
+
|
|
477
|
+
Cleanup the WordPress environment. Removes docker containers, volumes, networks,
|
|
478
|
+
and local files, but preserves docker images for faster re-starts.
|
|
479
|
+
|
|
480
|
+
Options:
|
|
481
|
+
--debug Enable debug output. [boolean] [default: false]
|
|
482
|
+
--scripts Execute any configured lifecycle scripts. [boolean] [default: true]
|
|
483
|
+
--force Skip the confirmation prompt. [boolean] [default: false]
|
|
484
|
+
```
|
|
485
|
+
|
|
472
486
|
### `wp-env destroy`
|
|
473
487
|
|
|
474
488
|
```sh
|
|
475
489
|
wp-env destroy
|
|
476
490
|
|
|
477
|
-
Destroy the WordPress environment. Deletes docker containers, volumes,
|
|
478
|
-
|
|
491
|
+
Destroy the WordPress environment. Deletes docker containers, volumes, networks,
|
|
492
|
+
and images associated with the WordPress environment and removes local files.
|
|
479
493
|
|
|
480
494
|
Options:
|
|
481
495
|
--debug Enable debug output. [boolean] [default: false]
|
|
482
496
|
--scripts Execute any configured lifecycle scripts. [boolean] [default: true]
|
|
497
|
+
--force Skip the confirmation prompt. [boolean] [default: false]
|
|
483
498
|
```
|
|
484
499
|
|
|
485
500
|
### `wp-env logs [environment]`
|
|
@@ -629,7 +644,8 @@ example, `WP_ENV_LIFECYCLE_SCRIPT_AFTER_START`. Keep in mind that these will be
|
|
|
629
644
|
build won't break on subsequent executions.
|
|
630
645
|
|
|
631
646
|
* `afterStart`: Runs after `wp-env start` has finished setting up the environment.
|
|
632
|
-
* `
|
|
647
|
+
* `afterReset`: Runs after `wp-env reset` has finished resetting the environment.
|
|
648
|
+
* `afterCleanup`: Runs after `wp-env cleanup` has cleaned up the environment.
|
|
633
649
|
* `afterDestroy`: Runs after `wp-env destroy` has destroyed the environment.
|
|
634
650
|
|
|
635
651
|
## Examples
|
|
@@ -769,7 +785,7 @@ You can tell `wp-env` to use a specific PHP version for compatibility and testin
|
|
|
769
785
|
|
|
770
786
|
### Multisite support
|
|
771
787
|
|
|
772
|
-
You can tell `wp-env`
|
|
788
|
+
You can tell `wp-env` if the site should be multisite enabled.
|
|
773
789
|
|
|
774
790
|
```json
|
|
775
791
|
{
|
package/lib/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
getAvailableRuntimes,
|
|
19
19
|
getRuntime,
|
|
20
20
|
UnsupportedCommandError,
|
|
21
|
+
EnvironmentNotInitializedError,
|
|
21
22
|
} = require( './runtime' );
|
|
22
23
|
|
|
23
24
|
// Colors.
|
|
@@ -45,8 +46,11 @@ const withSpinner =
|
|
|
45
46
|
process.exit( 0 );
|
|
46
47
|
},
|
|
47
48
|
( error ) => {
|
|
48
|
-
if (
|
|
49
|
-
|
|
49
|
+
if (
|
|
50
|
+
error instanceof UnsupportedCommandError ||
|
|
51
|
+
error instanceof EnvironmentNotInitializedError
|
|
52
|
+
) {
|
|
53
|
+
// Error is a known user-facing error.
|
|
50
54
|
spinner.fail( error.message );
|
|
51
55
|
process.exit( 1 );
|
|
52
56
|
} else if (
|
|
@@ -162,13 +166,31 @@ module.exports = function cli() {
|
|
|
162
166
|
() => {},
|
|
163
167
|
withSpinner( env.stop )
|
|
164
168
|
);
|
|
169
|
+
yargs.command(
|
|
170
|
+
'reset [environment]',
|
|
171
|
+
wpYellow( 'Resets the WordPress databases.' ),
|
|
172
|
+
( args ) => {
|
|
173
|
+
args.positional( 'environment', {
|
|
174
|
+
type: 'string',
|
|
175
|
+
describe: "Which environments' databases to reset.",
|
|
176
|
+
choices: [ 'all', 'development', 'tests' ],
|
|
177
|
+
default: 'tests',
|
|
178
|
+
} );
|
|
179
|
+
args.option( 'scripts', {
|
|
180
|
+
type: 'boolean',
|
|
181
|
+
describe: 'Execute any configured lifecycle scripts.',
|
|
182
|
+
default: true,
|
|
183
|
+
} );
|
|
184
|
+
},
|
|
185
|
+
withSpinner( env.reset )
|
|
186
|
+
);
|
|
165
187
|
yargs.command(
|
|
166
188
|
'clean [environment]',
|
|
167
|
-
|
|
189
|
+
chalk.gray( '[Deprecated: use reset] Resets the WordPress databases.' ),
|
|
168
190
|
( args ) => {
|
|
169
191
|
args.positional( 'environment', {
|
|
170
192
|
type: 'string',
|
|
171
|
-
describe: "Which environments' databases to
|
|
193
|
+
describe: "Which environments' databases to reset.",
|
|
172
194
|
choices: [ 'all', 'development', 'tests' ],
|
|
173
195
|
default: 'tests',
|
|
174
196
|
} );
|
|
@@ -246,7 +268,7 @@ module.exports = function cli() {
|
|
|
246
268
|
yargs.command(
|
|
247
269
|
'destroy',
|
|
248
270
|
wpRed(
|
|
249
|
-
'Destroy the WordPress environment. Deletes docker containers, volumes, and
|
|
271
|
+
'Destroy the WordPress environment. Deletes docker containers, volumes, networks, and images associated with the WordPress environment and removes local files.'
|
|
250
272
|
),
|
|
251
273
|
( args ) => {
|
|
252
274
|
args.option( 'scripts', {
|
|
@@ -254,9 +276,33 @@ module.exports = function cli() {
|
|
|
254
276
|
describe: 'Execute any configured lifecycle scripts.',
|
|
255
277
|
default: true,
|
|
256
278
|
} );
|
|
279
|
+
args.option( 'force', {
|
|
280
|
+
type: 'boolean',
|
|
281
|
+
describe: 'Skip the confirmation prompt.',
|
|
282
|
+
default: false,
|
|
283
|
+
} );
|
|
257
284
|
},
|
|
258
285
|
withSpinner( env.destroy )
|
|
259
286
|
);
|
|
287
|
+
yargs.command(
|
|
288
|
+
'cleanup',
|
|
289
|
+
wpYellow(
|
|
290
|
+
'Cleanup the WordPress environment. Removes docker containers, volumes, networks, and local files, but preserves docker images for faster re-starts.'
|
|
291
|
+
),
|
|
292
|
+
( args ) => {
|
|
293
|
+
args.option( 'scripts', {
|
|
294
|
+
type: 'boolean',
|
|
295
|
+
describe: 'Execute any configured lifecycle scripts.',
|
|
296
|
+
default: true,
|
|
297
|
+
} );
|
|
298
|
+
args.option( 'force', {
|
|
299
|
+
type: 'boolean',
|
|
300
|
+
describe: 'Skip the confirmation prompt.',
|
|
301
|
+
default: false,
|
|
302
|
+
} );
|
|
303
|
+
},
|
|
304
|
+
withSpinner( env.cleanup )
|
|
305
|
+
);
|
|
260
306
|
yargs.command(
|
|
261
307
|
'status',
|
|
262
308
|
'Get the status of the wp-env environment including URLs, ports, and configuration.',
|
package/lib/commands/clean.js
CHANGED
|
@@ -17,10 +17,12 @@ const { getRuntime, detectRuntime } = require( '../runtime' );
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* @deprecated Use `reset` instead.
|
|
21
|
+
*
|
|
22
|
+
* Resets the development server's database, the tests server's database, or both.
|
|
21
23
|
*
|
|
22
24
|
* @param {Object} options
|
|
23
|
-
* @param {WPEnvironmentSelection} options.environment The environment to
|
|
25
|
+
* @param {WPEnvironmentSelection} options.environment The environment to reset. Either 'development', 'tests', or 'all'.
|
|
24
26
|
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
25
27
|
* @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
|
|
26
28
|
* @param {boolean} options.debug True if debug mode is enabled.
|
|
@@ -31,11 +33,16 @@ module.exports = async function clean( {
|
|
|
31
33
|
scripts,
|
|
32
34
|
debug,
|
|
33
35
|
} ) {
|
|
36
|
+
spinner.warn( 'The `clean` command is deprecated. Use `reset` instead.' );
|
|
37
|
+
|
|
34
38
|
const config = await loadConfig( path.resolve( '.' ) );
|
|
35
|
-
const runtime = getRuntime(
|
|
39
|
+
const runtime = getRuntime(
|
|
40
|
+
await detectRuntime( config.workDirectoryPath )
|
|
41
|
+
);
|
|
36
42
|
|
|
37
43
|
await runtime.clean( config, { environment, spinner, debug } );
|
|
38
44
|
|
|
45
|
+
// Execute afterClean for backwards compatibility.
|
|
39
46
|
if ( scripts ) {
|
|
40
47
|
await executeLifecycleScript( 'afterClean', config, spinner );
|
|
41
48
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* External dependencies
|
|
4
|
+
*/
|
|
5
|
+
const fs = require( 'fs' ).promises;
|
|
6
|
+
const path = require( 'path' );
|
|
7
|
+
const { confirm } = require( '@inquirer/prompts' );
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Internal dependencies
|
|
11
|
+
*/
|
|
12
|
+
const { loadConfig } = require( '../config' );
|
|
13
|
+
const { executeLifecycleScript } = require( '../execute-lifecycle-script' );
|
|
14
|
+
const { getRuntime, detectRuntime } = require( '../runtime' );
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Cleanup the development server.
|
|
18
|
+
*
|
|
19
|
+
* Removes Docker containers, volumes, networks, and local files,
|
|
20
|
+
* but preserves Docker images for faster re-starts.
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} options
|
|
23
|
+
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
24
|
+
* @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
|
|
25
|
+
* @param {boolean} options.force If true, skips the confirmation prompt.
|
|
26
|
+
* @param {boolean} options.debug True if debug mode is enabled.
|
|
27
|
+
*/
|
|
28
|
+
module.exports = async function cleanup( { spinner, scripts, force, debug } ) {
|
|
29
|
+
const config = await loadConfig( path.resolve( '.' ) );
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await fs.readdir( config.workDirectoryPath );
|
|
33
|
+
} catch {
|
|
34
|
+
spinner.text = 'Could not find any files to remove.';
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const runtime = getRuntime( detectRuntime( config.workDirectoryPath ) );
|
|
39
|
+
|
|
40
|
+
spinner.info( runtime.getCleanupWarningMessage() );
|
|
41
|
+
|
|
42
|
+
let yesDelete = force;
|
|
43
|
+
if ( ! force ) {
|
|
44
|
+
try {
|
|
45
|
+
yesDelete = await confirm( {
|
|
46
|
+
message: 'Are you sure you want to continue?',
|
|
47
|
+
default: false,
|
|
48
|
+
} );
|
|
49
|
+
} catch ( error ) {
|
|
50
|
+
if ( error.name === 'ExitPromptError' ) {
|
|
51
|
+
console.log( 'Cancelled.' );
|
|
52
|
+
process.exit( 1 );
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
spinner.start();
|
|
59
|
+
|
|
60
|
+
if ( ! yesDelete ) {
|
|
61
|
+
spinner.text = 'Cancelled.';
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await runtime.cleanup( config, { spinner, debug } );
|
|
66
|
+
|
|
67
|
+
if ( scripts ) {
|
|
68
|
+
await executeLifecycleScript( 'afterCleanup', config, spinner );
|
|
69
|
+
}
|
|
70
|
+
};
|
package/lib/commands/destroy.js
CHANGED
|
@@ -19,9 +19,10 @@ const { getRuntime, detectRuntime } = require( '../runtime' );
|
|
|
19
19
|
* @param {Object} options
|
|
20
20
|
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
21
21
|
* @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
|
|
22
|
+
* @param {boolean} options.force If true, skips the confirmation prompt.
|
|
22
23
|
* @param {boolean} options.debug True if debug mode is enabled.
|
|
23
24
|
*/
|
|
24
|
-
module.exports = async function destroy( { spinner, scripts, debug } ) {
|
|
25
|
+
module.exports = async function destroy( { spinner, scripts, force, debug } ) {
|
|
25
26
|
const config = await loadConfig( path.resolve( '.' ) );
|
|
26
27
|
|
|
27
28
|
try {
|
|
@@ -31,22 +32,26 @@ module.exports = async function destroy( { spinner, scripts, debug } ) {
|
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
const runtime = getRuntime(
|
|
35
|
+
const runtime = getRuntime(
|
|
36
|
+
await detectRuntime( config.workDirectoryPath )
|
|
37
|
+
);
|
|
35
38
|
|
|
36
39
|
spinner.info( runtime.getDestroyWarningMessage() );
|
|
37
40
|
|
|
38
|
-
let yesDelete =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
let yesDelete = force;
|
|
42
|
+
if ( ! force ) {
|
|
43
|
+
try {
|
|
44
|
+
yesDelete = await confirm( {
|
|
45
|
+
message: 'Are you sure you want to continue?',
|
|
46
|
+
default: false,
|
|
47
|
+
} );
|
|
48
|
+
} catch ( error ) {
|
|
49
|
+
if ( error.name === 'ExitPromptError' ) {
|
|
50
|
+
console.log( 'Cancelled.' );
|
|
51
|
+
process.exit( 1 );
|
|
52
|
+
}
|
|
53
|
+
throw error;
|
|
48
54
|
}
|
|
49
|
-
throw error;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
spinner.start();
|
package/lib/commands/index.js
CHANGED
|
@@ -4,18 +4,22 @@
|
|
|
4
4
|
*/
|
|
5
5
|
const start = require( './start' );
|
|
6
6
|
const stop = require( './stop' );
|
|
7
|
+
const reset = require( './reset' );
|
|
7
8
|
const clean = require( './clean' );
|
|
8
9
|
const run = require( './run' );
|
|
9
10
|
const destroy = require( './destroy' );
|
|
11
|
+
const cleanup = require( './cleanup' );
|
|
10
12
|
const logs = require( './logs' );
|
|
11
13
|
const status = require( './status' );
|
|
12
14
|
|
|
13
15
|
module.exports = {
|
|
14
16
|
start,
|
|
15
17
|
stop,
|
|
18
|
+
reset,
|
|
16
19
|
clean,
|
|
17
20
|
run,
|
|
18
21
|
destroy,
|
|
22
|
+
cleanup,
|
|
19
23
|
logs,
|
|
20
24
|
status,
|
|
21
25
|
};
|
package/lib/commands/logs.js
CHANGED
|
@@ -21,6 +21,8 @@ const { getRuntime, detectRuntime } = require( '../runtime' );
|
|
|
21
21
|
*/
|
|
22
22
|
module.exports = async function logs( { environment, watch, spinner, debug } ) {
|
|
23
23
|
const config = await loadConfig( path.resolve( '.' ) );
|
|
24
|
-
const runtime = getRuntime(
|
|
24
|
+
const runtime = getRuntime(
|
|
25
|
+
await detectRuntime( config.workDirectoryPath )
|
|
26
|
+
);
|
|
25
27
|
await runtime.logs( config, { environment, watch, spinner, debug } );
|
|
26
28
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* External dependencies
|
|
4
|
+
*/
|
|
5
|
+
const path = require( 'path' );
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
const { executeLifecycleScript } = require( '../execute-lifecycle-script' );
|
|
11
|
+
const { loadConfig } = require( '../config' );
|
|
12
|
+
const { getRuntime, detectRuntime } = require( '../runtime' );
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {import('../wordpress').WPEnvironment} WPEnvironment
|
|
16
|
+
* @typedef {import('../wordpress').WPEnvironmentSelection} WPEnvironmentSelection
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resets the development server's database, the tests server's database, or both.
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} options
|
|
23
|
+
* @param {WPEnvironmentSelection} options.environment The environment to reset. Either 'development', 'tests', or 'all'.
|
|
24
|
+
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
25
|
+
* @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
|
|
26
|
+
* @param {boolean} options.debug True if debug mode is enabled.
|
|
27
|
+
*/
|
|
28
|
+
module.exports = async function reset( {
|
|
29
|
+
environment,
|
|
30
|
+
spinner,
|
|
31
|
+
scripts,
|
|
32
|
+
debug,
|
|
33
|
+
} ) {
|
|
34
|
+
const config = await loadConfig( path.resolve( '.' ) );
|
|
35
|
+
const runtime = getRuntime( detectRuntime( config.workDirectoryPath ) );
|
|
36
|
+
|
|
37
|
+
await runtime.clean( config, { environment, spinner, debug } );
|
|
38
|
+
|
|
39
|
+
if ( scripts ) {
|
|
40
|
+
await executeLifecycleScript( 'afterReset', config, spinner );
|
|
41
|
+
}
|
|
42
|
+
};
|
package/lib/commands/run.js
CHANGED
|
@@ -30,7 +30,9 @@ module.exports = async function run( {
|
|
|
30
30
|
debug,
|
|
31
31
|
} ) {
|
|
32
32
|
const config = await loadConfig( path.resolve( '.' ) );
|
|
33
|
-
const runtime = getRuntime(
|
|
33
|
+
const runtime = getRuntime(
|
|
34
|
+
await detectRuntime( config.workDirectoryPath )
|
|
35
|
+
);
|
|
34
36
|
|
|
35
37
|
// Include any double dashed arguments in the command so that we can pass them to Docker.
|
|
36
38
|
// This lets users pass options that the command defines without them being parsed.
|
package/lib/commands/start.js
CHANGED
|
@@ -12,7 +12,7 @@ const { rimraf } = require( 'rimraf' );
|
|
|
12
12
|
*/
|
|
13
13
|
const { loadConfig } = require( '../config' );
|
|
14
14
|
const { executeLifecycleScript } = require( '../execute-lifecycle-script' );
|
|
15
|
-
const { getRuntime } = require( '../runtime' );
|
|
15
|
+
const { getRuntime, getSavedRuntime, saveRuntime } = require( '../runtime' );
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @typedef {import('../config').WPConfig} WPConfig
|
|
@@ -51,6 +51,38 @@ module.exports = async function start( {
|
|
|
51
51
|
const config = await loadConfig( path.resolve( '.' ) );
|
|
52
52
|
config.debug = debug;
|
|
53
53
|
|
|
54
|
+
// Check if switching runtimes and prompt user to destroy old environment first.
|
|
55
|
+
const savedRuntime = await getSavedRuntime( config.workDirectoryPath );
|
|
56
|
+
if ( savedRuntime && savedRuntime !== runtimeName ) {
|
|
57
|
+
spinner.stop();
|
|
58
|
+
let shouldDestroy = false;
|
|
59
|
+
try {
|
|
60
|
+
shouldDestroy = await confirm( {
|
|
61
|
+
message: `Environment was previously started with '${ savedRuntime }' runtime. Destroy it and start with '${ runtimeName }'?`,
|
|
62
|
+
default: true,
|
|
63
|
+
} );
|
|
64
|
+
} catch ( error ) {
|
|
65
|
+
if ( error.name === 'ExitPromptError' ) {
|
|
66
|
+
console.log( 'Cancelled.' );
|
|
67
|
+
process.exit( 1 );
|
|
68
|
+
}
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if ( ! shouldDestroy ) {
|
|
73
|
+
spinner.fail(
|
|
74
|
+
`Aborted. Run 'wp-env destroy' manually or start with '--runtime=${ savedRuntime }'.`
|
|
75
|
+
);
|
|
76
|
+
process.exit( 1 );
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// User confirmed - destroy old runtime first.
|
|
80
|
+
spinner.start();
|
|
81
|
+
spinner.text = `Destroying previous ${ savedRuntime } environment.`;
|
|
82
|
+
const oldRuntime = getRuntime( savedRuntime );
|
|
83
|
+
await oldRuntime.destroy( config, { spinner } );
|
|
84
|
+
}
|
|
85
|
+
|
|
54
86
|
if ( ! config.detectedLocalConfig ) {
|
|
55
87
|
const { configDirectoryPath } = config;
|
|
56
88
|
spinner.warn(
|
|
@@ -76,6 +108,9 @@ module.exports = async function start( {
|
|
|
76
108
|
spx,
|
|
77
109
|
debug,
|
|
78
110
|
} );
|
|
111
|
+
|
|
112
|
+
// Save the runtime type after successful start.
|
|
113
|
+
await saveRuntime( runtimeName, config.workDirectoryPath );
|
|
79
114
|
} catch ( error ) {
|
|
80
115
|
// Attempt to stop any partially-started environment so that
|
|
81
116
|
// processes do not linger after a failed start.
|
package/lib/commands/stop.js
CHANGED
|
@@ -19,6 +19,8 @@ const { getRuntime, detectRuntime } = require( '../runtime' );
|
|
|
19
19
|
*/
|
|
20
20
|
module.exports = async function stop( { spinner, debug } ) {
|
|
21
21
|
const config = await loadConfig( path.resolve( '.' ) );
|
|
22
|
-
const runtime = getRuntime(
|
|
22
|
+
const runtime = getRuntime(
|
|
23
|
+
await detectRuntime( config.workDirectoryPath )
|
|
24
|
+
);
|
|
23
25
|
await runtime.stop( config, { spinner, debug } );
|
|
24
26
|
};
|
|
@@ -23,7 +23,6 @@ const { checkPort, checkVersion, checkString } = require( './validate-config' );
|
|
|
23
23
|
* @property {?number} phpmyadminPort An override for the development environment's phpMyAdmin port.
|
|
24
24
|
* @property {?WPSource} coreSource An override for all environment's coreSource.
|
|
25
25
|
* @property {?string} phpVersion An override for all environment's PHP version.
|
|
26
|
-
* @property {?boolean} multisite An override for if environmen should be multisite.
|
|
27
26
|
* @property {?Object.<string, string>} lifecycleScripts An override for various lifecycle scripts.
|
|
28
27
|
*/
|
|
29
28
|
|
|
@@ -69,10 +68,6 @@ module.exports = function getConfigFromEnvironmentVars( cacheDirectoryPath ) {
|
|
|
69
68
|
environmentConfig.phpVersion = process.env.WP_ENV_PHP_VERSION;
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
if ( process.env.WP_ENV_MULTISITE ) {
|
|
73
|
-
environmentConfig.multisite = !! process.env.WP_ENV_MULTISITE;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
71
|
return environmentConfig;
|
|
77
72
|
};
|
|
78
73
|
|
|
@@ -108,6 +103,8 @@ function getLifecycleScriptOverrides() {
|
|
|
108
103
|
const lifecycleEnvironmentVars = {
|
|
109
104
|
WP_ENV_LIFECYCLE_SCRIPT_AFTER_START: 'afterStart',
|
|
110
105
|
WP_ENV_LIFECYCLE_SCRIPT_AFTER_CLEAN: 'afterClean',
|
|
106
|
+
WP_ENV_LIFECYCLE_SCRIPT_AFTER_RESET: 'afterReset',
|
|
107
|
+
WP_ENV_LIFECYCLE_SCRIPT_AFTER_CLEANUP: 'afterCleanup',
|
|
111
108
|
WP_ENV_LIFECYCLE_SCRIPT_AFTER_DESTROY: 'afterDestroy',
|
|
112
109
|
};
|
|
113
110
|
for ( const envVar in lifecycleEnvironmentVars ) {
|
|
@@ -235,6 +235,8 @@ async function getDefaultConfig(
|
|
|
235
235
|
lifecycleScripts: {
|
|
236
236
|
afterStart: null,
|
|
237
237
|
afterClean: null,
|
|
238
|
+
afterReset: null,
|
|
239
|
+
afterCleanup: null,
|
|
238
240
|
afterDestroy: null,
|
|
239
241
|
},
|
|
240
242
|
env: {
|
|
@@ -312,6 +314,12 @@ function getEnvironmentVarOverrides( cacheDirectoryPath ) {
|
|
|
312
314
|
overrideConfig.env.tests.phpVersion = overrides.phpVersion;
|
|
313
315
|
}
|
|
314
316
|
|
|
317
|
+
if ( overrides.multisite ) {
|
|
318
|
+
overrideConfig.multisite = overrides.multisite;
|
|
319
|
+
overrideConfig.env.development.multisite = overrides.multisite;
|
|
320
|
+
overrideConfig.env.tests.multisite = overrides.multisite;
|
|
321
|
+
}
|
|
322
|
+
|
|
315
323
|
return overrideConfig;
|
|
316
324
|
}
|
|
317
325
|
|
|
@@ -71,7 +71,9 @@ exports[`Config Integration should load local and override configuration files 1
|
|
|
71
71
|
},
|
|
72
72
|
"lifecycleScripts": {
|
|
73
73
|
"afterClean": null,
|
|
74
|
+
"afterCleanup": null,
|
|
74
75
|
"afterDestroy": "test",
|
|
76
|
+
"afterReset": null,
|
|
75
77
|
"afterStart": null,
|
|
76
78
|
},
|
|
77
79
|
"name": "gutenberg",
|
|
@@ -150,7 +152,9 @@ exports[`Config Integration should load local configuration file 1`] = `
|
|
|
150
152
|
},
|
|
151
153
|
"lifecycleScripts": {
|
|
152
154
|
"afterClean": null,
|
|
155
|
+
"afterCleanup": null,
|
|
153
156
|
"afterDestroy": null,
|
|
157
|
+
"afterReset": null,
|
|
154
158
|
"afterStart": "test",
|
|
155
159
|
},
|
|
156
160
|
"name": "gutenberg",
|
|
@@ -229,7 +233,9 @@ exports[`Config Integration should use default configuration 1`] = `
|
|
|
229
233
|
},
|
|
230
234
|
"lifecycleScripts": {
|
|
231
235
|
"afterClean": null,
|
|
236
|
+
"afterCleanup": null,
|
|
232
237
|
"afterDestroy": null,
|
|
238
|
+
"afterReset": null,
|
|
233
239
|
"afterStart": null,
|
|
234
240
|
},
|
|
235
241
|
"name": "gutenberg",
|
|
@@ -310,7 +316,9 @@ exports[`Config Integration should use environment variables over local and over
|
|
|
310
316
|
},
|
|
311
317
|
"lifecycleScripts": {
|
|
312
318
|
"afterClean": null,
|
|
319
|
+
"afterCleanup": null,
|
|
313
320
|
"afterDestroy": null,
|
|
321
|
+
"afterReset": null,
|
|
314
322
|
"afterStart": "test",
|
|
315
323
|
},
|
|
316
324
|
"name": "gutenberg",
|
|
@@ -181,6 +181,19 @@ module.exports = function buildDockerComposeConfig( config ) {
|
|
|
181
181
|
config.env.tests.phpmyadminPort ?? ''
|
|
182
182
|
}}:80`;
|
|
183
183
|
|
|
184
|
+
// MySQL healthcheck using MariaDB's official healthcheck.sh script.
|
|
185
|
+
// --connect: verifies TCP connection and that entrypoint has finished
|
|
186
|
+
// --innodb_initialized: ensures InnoDB storage engine is fully initialized
|
|
187
|
+
// MARIADB_AUTO_UPGRADE env var ensures healthcheck user exists for existing installations.
|
|
188
|
+
// Timing is generous to support slow CI environments.
|
|
189
|
+
const mysqlHealthcheck = {
|
|
190
|
+
test: [ 'CMD', 'healthcheck.sh', '--connect', '--innodb_initialized' ],
|
|
191
|
+
interval: '5s',
|
|
192
|
+
timeout: '10s',
|
|
193
|
+
retries: 12,
|
|
194
|
+
start_period: '60s',
|
|
195
|
+
};
|
|
196
|
+
|
|
184
197
|
return {
|
|
185
198
|
services: {
|
|
186
199
|
mysql: {
|
|
@@ -191,8 +204,11 @@ module.exports = function buildDockerComposeConfig( config ) {
|
|
|
191
204
|
MYSQL_ROOT_PASSWORD:
|
|
192
205
|
dbEnv.credentials.WORDPRESS_DB_PASSWORD,
|
|
193
206
|
MYSQL_DATABASE: dbEnv.development.WORDPRESS_DB_NAME,
|
|
207
|
+
// Ensures healthcheck user is created for existing installations.
|
|
208
|
+
MARIADB_AUTO_UPGRADE: '1',
|
|
194
209
|
},
|
|
195
210
|
volumes: [ 'mysql:/var/lib/mysql' ],
|
|
211
|
+
healthcheck: mysqlHealthcheck,
|
|
196
212
|
},
|
|
197
213
|
'tests-mysql': {
|
|
198
214
|
image: 'mariadb:lts',
|
|
@@ -202,11 +218,18 @@ module.exports = function buildDockerComposeConfig( config ) {
|
|
|
202
218
|
MYSQL_ROOT_PASSWORD:
|
|
203
219
|
dbEnv.credentials.WORDPRESS_DB_PASSWORD,
|
|
204
220
|
MYSQL_DATABASE: dbEnv.tests.WORDPRESS_DB_NAME,
|
|
221
|
+
// Ensures healthcheck user is created for existing installations.
|
|
222
|
+
MARIADB_AUTO_UPGRADE: '1',
|
|
205
223
|
},
|
|
206
224
|
volumes: [ 'mysql-test:/var/lib/mysql' ],
|
|
225
|
+
healthcheck: mysqlHealthcheck,
|
|
207
226
|
},
|
|
208
227
|
wordpress: {
|
|
209
|
-
depends_on:
|
|
228
|
+
depends_on: {
|
|
229
|
+
mysql: {
|
|
230
|
+
condition: 'service_healthy',
|
|
231
|
+
},
|
|
232
|
+
},
|
|
210
233
|
build: {
|
|
211
234
|
context: '.',
|
|
212
235
|
dockerfile: 'WordPress.Dockerfile',
|
|
@@ -224,7 +247,11 @@ module.exports = function buildDockerComposeConfig( config ) {
|
|
|
224
247
|
extra_hosts: [ 'host.docker.internal:host-gateway' ],
|
|
225
248
|
},
|
|
226
249
|
'tests-wordpress': {
|
|
227
|
-
depends_on:
|
|
250
|
+
depends_on: {
|
|
251
|
+
'tests-mysql': {
|
|
252
|
+
condition: 'service_healthy',
|
|
253
|
+
},
|
|
254
|
+
},
|
|
228
255
|
build: {
|
|
229
256
|
context: '.',
|
|
230
257
|
dockerfile: 'Tests-WordPress.Dockerfile',
|
|
@@ -11,7 +11,6 @@ const { rimraf } = require( 'rimraf' );
|
|
|
11
11
|
/**
|
|
12
12
|
* Promisified dependencies
|
|
13
13
|
*/
|
|
14
|
-
const sleep = util.promisify( setTimeout );
|
|
15
14
|
const exec = util.promisify( require( 'child_process' ).exec );
|
|
16
15
|
|
|
17
16
|
/**
|
|
@@ -26,7 +25,6 @@ const {
|
|
|
26
25
|
validateRunContainer,
|
|
27
26
|
} = require( './validate-run-container' );
|
|
28
27
|
const {
|
|
29
|
-
checkDatabaseConnection,
|
|
30
28
|
configureWordPress,
|
|
31
29
|
resetDatabase,
|
|
32
30
|
setupWordPressDirectories,
|
|
@@ -174,7 +172,7 @@ class DockerRuntime {
|
|
|
174
172
|
}
|
|
175
173
|
|
|
176
174
|
await Promise.all( [
|
|
177
|
-
dockerCompose.
|
|
175
|
+
dockerCompose.upMany( [ 'mysql', 'tests-mysql' ], {
|
|
178
176
|
...dockerComposeConfig,
|
|
179
177
|
commandOptions: shouldConfigureWp
|
|
180
178
|
? [ '--build', '--force-recreate' ]
|
|
@@ -250,19 +248,6 @@ class DockerRuntime {
|
|
|
250
248
|
if ( shouldConfigureWp ) {
|
|
251
249
|
spinner.text = 'Configuring WordPress.';
|
|
252
250
|
|
|
253
|
-
try {
|
|
254
|
-
await checkDatabaseConnection( fullConfig );
|
|
255
|
-
} catch ( error ) {
|
|
256
|
-
// Wait 30 seconds for MySQL to accept connections.
|
|
257
|
-
await retry( () => checkDatabaseConnection( fullConfig ), {
|
|
258
|
-
times: 30,
|
|
259
|
-
delay: 1000,
|
|
260
|
-
} );
|
|
261
|
-
|
|
262
|
-
// It takes 3-4 seconds for MySQL to be ready after it starts accepting connections.
|
|
263
|
-
await sleep( 4000 );
|
|
264
|
-
}
|
|
265
|
-
|
|
266
251
|
// Retry WordPress installation in case MySQL *still* wasn't ready.
|
|
267
252
|
await Promise.all( [
|
|
268
253
|
retry(
|
|
@@ -369,6 +354,15 @@ class DockerRuntime {
|
|
|
369
354
|
return 'WARNING! This will remove Docker containers, volumes, networks, and images associated with the WordPress instance.';
|
|
370
355
|
}
|
|
371
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Get the warning message for cleanup confirmation.
|
|
359
|
+
*
|
|
360
|
+
* @return {string} Warning message.
|
|
361
|
+
*/
|
|
362
|
+
getCleanupWarningMessage() {
|
|
363
|
+
return 'WARNING! This will remove Docker containers, volumes, networks, and local files associated with the WordPress instance. Docker images will be preserved.';
|
|
364
|
+
}
|
|
365
|
+
|
|
372
366
|
/**
|
|
373
367
|
* Stop the Docker containers.
|
|
374
368
|
*
|
|
@@ -413,7 +407,8 @@ class DockerRuntime {
|
|
|
413
407
|
spinner.text = 'Removing local files.';
|
|
414
408
|
// Note: there is a race condition where docker compose actually hasn't finished
|
|
415
409
|
// by this point, which causes rimraf to fail. We need to wait at least 2.5-5s,
|
|
416
|
-
// but using 10s in case it's
|
|
410
|
+
// but using 10s in case it's dependent on the machine. Removing images takes
|
|
411
|
+
// longer so we use a longer wait time here.
|
|
417
412
|
await new Promise( ( resolve ) => setTimeout( resolve, 10000 ) );
|
|
418
413
|
await rimraf( config.workDirectoryPath );
|
|
419
414
|
|
|
@@ -421,11 +416,38 @@ class DockerRuntime {
|
|
|
421
416
|
}
|
|
422
417
|
|
|
423
418
|
/**
|
|
424
|
-
*
|
|
419
|
+
* Cleanup the Docker containers and remove local files, but preserve images.
|
|
420
|
+
*
|
|
421
|
+
* @param {WPConfig} config The wp-env config object.
|
|
422
|
+
* @param {Object} options Cleanup options.
|
|
423
|
+
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
424
|
+
* @param {boolean} options.debug True if debug mode is enabled.
|
|
425
|
+
*/
|
|
426
|
+
async cleanup( config, { spinner, debug } ) {
|
|
427
|
+
spinner.text = 'Removing docker containers, volumes, and networks.';
|
|
428
|
+
|
|
429
|
+
await dockerCompose.down( {
|
|
430
|
+
config: config.dockerComposeConfigPath,
|
|
431
|
+
commandOptions: [ '--volumes', '--remove-orphans' ],
|
|
432
|
+
log: debug,
|
|
433
|
+
} );
|
|
434
|
+
|
|
435
|
+
spinner.text = 'Removing local files.';
|
|
436
|
+
// Note: there is a race condition where docker compose actually hasn't finished
|
|
437
|
+
// by this point, which causes rimraf to fail. We need to wait at least 2.5-5s,
|
|
438
|
+
// but since we're not removing images, the wait can be shorter.
|
|
439
|
+
await new Promise( ( resolve ) => setTimeout( resolve, 3000 ) );
|
|
440
|
+
await rimraf( config.workDirectoryPath );
|
|
441
|
+
|
|
442
|
+
spinner.text = 'Cleaned up WordPress environment.';
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Reset the WordPress database.
|
|
425
447
|
*
|
|
426
448
|
* @param {WPConfig} config The wp-env config object.
|
|
427
|
-
* @param {Object} options
|
|
428
|
-
* @param {string} options.environment The environment to
|
|
449
|
+
* @param {Object} options Reset options.
|
|
450
|
+
* @param {string} options.environment The environment to reset.
|
|
429
451
|
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
430
452
|
* @param {boolean} options.debug True if debug mode is enabled.
|
|
431
453
|
*/
|
|
@@ -435,13 +457,23 @@ class DockerRuntime {
|
|
|
435
457
|
const description = `${ environment } environment${
|
|
436
458
|
environment === 'all' ? 's' : ''
|
|
437
459
|
}`;
|
|
438
|
-
spinner.text = `
|
|
460
|
+
spinner.text = `Resetting ${ description }.`;
|
|
439
461
|
|
|
440
462
|
const tasks = [];
|
|
441
463
|
|
|
442
|
-
// Start the
|
|
443
|
-
//
|
|
444
|
-
|
|
464
|
+
// Start the appropriate MySQL service(s) first to avoid race conditions
|
|
465
|
+
// where parallel tasks try to create docker networks with the same name.
|
|
466
|
+
// The dependency chain (cli -> wordpress -> mysql with service_healthy)
|
|
467
|
+
// ensures MySQL is ready before database operations run.
|
|
468
|
+
const mysqlServices = [];
|
|
469
|
+
if ( environment === 'all' || environment === 'development' ) {
|
|
470
|
+
mysqlServices.push( 'mysql' );
|
|
471
|
+
}
|
|
472
|
+
if ( environment === 'all' || environment === 'tests' ) {
|
|
473
|
+
mysqlServices.push( 'tests-mysql' );
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
await dockerCompose.upMany( mysqlServices, {
|
|
445
477
|
config: fullConfig.dockerComposeConfigPath,
|
|
446
478
|
log: fullConfig.debug,
|
|
447
479
|
} );
|
|
@@ -466,7 +498,7 @@ class DockerRuntime {
|
|
|
466
498
|
|
|
467
499
|
await Promise.all( tasks );
|
|
468
500
|
|
|
469
|
-
spinner.text = `
|
|
501
|
+
spinner.text = `Reset ${ description }.`;
|
|
470
502
|
}
|
|
471
503
|
|
|
472
504
|
/**
|
|
@@ -47,20 +47,6 @@ function isWPMajorMinorVersionLower( version, compareVersion ) {
|
|
|
47
47
|
return versionNumber < compareVersionNumber;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
/**
|
|
51
|
-
* Checks a WordPress database connection. An error is thrown if the test is
|
|
52
|
-
* unsuccessful.
|
|
53
|
-
*
|
|
54
|
-
* @param {WPConfig} config The wp-env config object.
|
|
55
|
-
*/
|
|
56
|
-
async function checkDatabaseConnection( { dockerComposeConfigPath, debug } ) {
|
|
57
|
-
await dockerCompose.run( 'cli', 'wp db check', {
|
|
58
|
-
config: dockerComposeConfigPath,
|
|
59
|
-
commandOptions: [ '--rm' ],
|
|
60
|
-
log: debug,
|
|
61
|
-
} );
|
|
62
|
-
}
|
|
63
|
-
|
|
64
50
|
/**
|
|
65
51
|
* Configures WordPress for the given environment by installing WordPress,
|
|
66
52
|
* activating all plugins, and activating the first theme. These steps are
|
|
@@ -316,7 +302,6 @@ async function copyCoreFiles( fromPath, toPath ) {
|
|
|
316
302
|
}
|
|
317
303
|
|
|
318
304
|
module.exports = {
|
|
319
|
-
checkDatabaseConnection,
|
|
320
305
|
configureWordPress,
|
|
321
306
|
resetDatabase,
|
|
322
307
|
setupWordPressDirectories,
|
package/lib/runtime/errors.js
CHANGED
|
@@ -12,6 +12,17 @@ class UnsupportedCommandError extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown when the environment has not been initialized.
|
|
17
|
+
*/
|
|
18
|
+
class EnvironmentNotInitializedError extends Error {
|
|
19
|
+
constructor() {
|
|
20
|
+
super( 'Environment not initialized. Run `wp-env start` first.' );
|
|
21
|
+
this.name = 'EnvironmentNotInitializedError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
module.exports = {
|
|
16
26
|
UnsupportedCommandError,
|
|
27
|
+
EnvironmentNotInitializedError,
|
|
17
28
|
};
|
package/lib/runtime/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* External dependencies
|
|
5
|
-
*/
|
|
6
|
-
const { existsSync } = require( 'fs' );
|
|
7
|
-
const path = require( 'path' );
|
|
8
|
-
|
|
9
3
|
/**
|
|
10
4
|
* Internal dependencies
|
|
11
5
|
*/
|
|
12
6
|
const DockerRuntime = require( './docker' );
|
|
13
7
|
const PlaygroundRuntime = require( './playground' );
|
|
14
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
UnsupportedCommandError,
|
|
10
|
+
EnvironmentNotInitializedError,
|
|
11
|
+
} = require( './errors' );
|
|
12
|
+
const { setCache, getCache } = require( '../cache' );
|
|
13
|
+
|
|
14
|
+
const RUNTIME_CACHE_KEY = 'runtime';
|
|
15
15
|
|
|
16
16
|
const runtimes = {
|
|
17
17
|
docker: DockerRuntime,
|
|
@@ -42,28 +42,50 @@ function getAvailableRuntimes() {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
45
|
+
* Save the runtime type to the cache file.
|
|
46
|
+
* Called when start command initializes the environment.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} runtimeName The runtime name ('docker' or 'playground').
|
|
49
|
+
* @param {string} workDirectoryPath Path to the wp-env work directory.
|
|
50
|
+
*/
|
|
51
|
+
async function saveRuntime( runtimeName, workDirectoryPath ) {
|
|
52
|
+
await setCache( RUNTIME_CACHE_KEY, runtimeName, { workDirectoryPath } );
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get the saved runtime type from cache.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} workDirectoryPath Path to the wp-env work directory.
|
|
59
|
+
* @return {Promise<string|undefined>} The saved runtime name, or undefined if not set.
|
|
60
|
+
*/
|
|
61
|
+
async function getSavedRuntime( workDirectoryPath ) {
|
|
62
|
+
return await getCache( RUNTIME_CACHE_KEY, { workDirectoryPath } );
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Detect which runtime was used by reading from the cache.
|
|
67
|
+
* Throws EnvironmentNotInitializedError if no runtime has been saved.
|
|
47
68
|
*
|
|
48
69
|
* @param {string} workDirectoryPath Path to the wp-env work directory.
|
|
49
|
-
* @return {string} Runtime name ('docker' or 'playground').
|
|
70
|
+
* @return {Promise<string>} Runtime name ('docker' or 'playground').
|
|
71
|
+
* @throws {EnvironmentNotInitializedError} If environment not initialized.
|
|
50
72
|
*/
|
|
51
|
-
function detectRuntime( workDirectoryPath ) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
);
|
|
56
|
-
if ( existsSync( playgroundBlueprintFile ) ) {
|
|
57
|
-
return 'playground';
|
|
73
|
+
async function detectRuntime( workDirectoryPath ) {
|
|
74
|
+
const savedRuntime = await getSavedRuntime( workDirectoryPath );
|
|
75
|
+
if ( ! savedRuntime ) {
|
|
76
|
+
throw new EnvironmentNotInitializedError();
|
|
58
77
|
}
|
|
59
|
-
return
|
|
78
|
+
return savedRuntime;
|
|
60
79
|
}
|
|
61
80
|
|
|
62
81
|
module.exports = {
|
|
63
82
|
getRuntime,
|
|
64
83
|
getAvailableRuntimes,
|
|
65
84
|
detectRuntime,
|
|
85
|
+
saveRuntime,
|
|
86
|
+
getSavedRuntime,
|
|
66
87
|
DockerRuntime,
|
|
67
88
|
PlaygroundRuntime,
|
|
68
89
|
UnsupportedCommandError,
|
|
90
|
+
EnvironmentNotInitializedError,
|
|
69
91
|
};
|
|
@@ -107,21 +107,13 @@ function getMountArgs( config ) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// Mount themes
|
|
110
|
+
// All theme types (local, git, zip) can be mounted after downloading/extraction
|
|
110
111
|
for ( const theme of envConfig.themeSources || [] ) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
);
|
|
117
|
-
} else {
|
|
118
|
-
throw new Error(
|
|
119
|
-
`Theme source "${ theme.basename || theme.path }" of type "${
|
|
120
|
-
theme.type
|
|
121
|
-
}" ` +
|
|
122
|
-
`is not supported with Playground runtime. Only local and git themes are supported.`
|
|
123
|
-
);
|
|
124
|
-
}
|
|
112
|
+
args.push(
|
|
113
|
+
'--mount-dir',
|
|
114
|
+
theme.path,
|
|
115
|
+
`/wordpress/wp-content/themes/${ theme.basename }`
|
|
116
|
+
);
|
|
125
117
|
}
|
|
126
118
|
|
|
127
119
|
// Mount custom mappings
|
|
@@ -75,6 +75,15 @@ class PlaygroundRuntime {
|
|
|
75
75
|
return 'WARNING! This will remove the WordPress Playground environment and all local files.';
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Get the warning message for cleanup confirmation.
|
|
80
|
+
*
|
|
81
|
+
* @return {string} Warning message.
|
|
82
|
+
*/
|
|
83
|
+
getCleanupWarningMessage() {
|
|
84
|
+
return 'WARNING! This will remove the WordPress Playground environment and all local files.';
|
|
85
|
+
}
|
|
86
|
+
|
|
78
87
|
/**
|
|
79
88
|
* Start the WordPress Playground environment.
|
|
80
89
|
*
|
|
@@ -350,6 +359,25 @@ class PlaygroundRuntime {
|
|
|
350
359
|
spinner.text = 'Removed WordPress Playground environment.';
|
|
351
360
|
}
|
|
352
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Cleanup the WordPress Playground environment.
|
|
364
|
+
*
|
|
365
|
+
* For Playground, cleanup is the same as destroy since there are no
|
|
366
|
+
* shared resources like Docker images to preserve.
|
|
367
|
+
*
|
|
368
|
+
* @param {Object} config The wp-env config object.
|
|
369
|
+
* @param {Object} options Cleanup options.
|
|
370
|
+
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
371
|
+
*/
|
|
372
|
+
async cleanup( config, { spinner } ) {
|
|
373
|
+
await this.stop( config, { spinner } );
|
|
374
|
+
|
|
375
|
+
spinner.text = 'Removing local files.';
|
|
376
|
+
await rimraf( config.workDirectoryPath );
|
|
377
|
+
|
|
378
|
+
spinner.text = 'Cleaned up WordPress Playground environment.';
|
|
379
|
+
}
|
|
380
|
+
|
|
353
381
|
/**
|
|
354
382
|
* Run a command in the Playground environment.
|
|
355
383
|
*
|
|
@@ -367,21 +395,21 @@ class PlaygroundRuntime {
|
|
|
367
395
|
}
|
|
368
396
|
|
|
369
397
|
/**
|
|
370
|
-
*
|
|
398
|
+
* Reset the WordPress database.
|
|
371
399
|
*
|
|
372
400
|
* @param {Object} config The wp-env config object.
|
|
373
|
-
* @param {Object} options
|
|
401
|
+
* @param {Object} options Reset options.
|
|
374
402
|
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
375
403
|
* @param {boolean} options.debug True if debug mode is enabled.
|
|
376
404
|
*/
|
|
377
405
|
async clean( config, { spinner, debug } ) {
|
|
378
|
-
spinner.text = '
|
|
406
|
+
spinner.text = 'Resetting WordPress Playground environment.';
|
|
379
407
|
|
|
380
408
|
// For Playground, we restart the server to reset the database
|
|
381
409
|
await this.stop( config, { spinner } );
|
|
382
410
|
await this.start( config, { spinner, debug } );
|
|
383
411
|
|
|
384
|
-
spinner.text = '
|
|
412
|
+
spinner.text = 'Reset WordPress Playground environment.';
|
|
385
413
|
}
|
|
386
414
|
|
|
387
415
|
/**
|
|
@@ -131,4 +131,87 @@ describe( 'buildDockerComposeConfig', () => {
|
|
|
131
131
|
expect( dockerConfig.volumes.wordpress ).toBe( undefined );
|
|
132
132
|
expect( dockerConfig.volumes[ 'tests-wordpress' ] ).toBe( undefined );
|
|
133
133
|
} );
|
|
134
|
+
|
|
135
|
+
it( 'should add healthcheck to mysql services', () => {
|
|
136
|
+
const config = buildDockerComposeConfig( {
|
|
137
|
+
workDirectoryPath: '/some/path',
|
|
138
|
+
env: {
|
|
139
|
+
development: {
|
|
140
|
+
port: 8888,
|
|
141
|
+
mysqlPort: 3306,
|
|
142
|
+
coreSource: null,
|
|
143
|
+
pluginSources: [],
|
|
144
|
+
themeSources: [],
|
|
145
|
+
mappings: {},
|
|
146
|
+
},
|
|
147
|
+
tests: {
|
|
148
|
+
port: 8889,
|
|
149
|
+
mysqlPort: 3307,
|
|
150
|
+
coreSource: null,
|
|
151
|
+
pluginSources: [],
|
|
152
|
+
themeSources: [],
|
|
153
|
+
mappings: {},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
} );
|
|
157
|
+
|
|
158
|
+
expect( config.services.mysql.healthcheck ).toBeDefined();
|
|
159
|
+
expect( config.services.mysql.healthcheck.test ).toEqual( [
|
|
160
|
+
'CMD',
|
|
161
|
+
'healthcheck.sh',
|
|
162
|
+
'--connect',
|
|
163
|
+
'--innodb_initialized',
|
|
164
|
+
] );
|
|
165
|
+
expect( config.services.mysql.healthcheck.interval ).toBe( '5s' );
|
|
166
|
+
expect( config.services.mysql.healthcheck.timeout ).toBe( '10s' );
|
|
167
|
+
expect( config.services.mysql.healthcheck.retries ).toBe( 12 );
|
|
168
|
+
expect( config.services.mysql.healthcheck.start_period ).toBe( '60s' );
|
|
169
|
+
|
|
170
|
+
// Verify MARIADB_AUTO_UPGRADE is set for existing installations
|
|
171
|
+
expect( config.services.mysql.environment.MARIADB_AUTO_UPGRADE ).toBe(
|
|
172
|
+
'1'
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
expect( config.services[ 'tests-mysql' ].healthcheck ).toBeDefined();
|
|
176
|
+
expect( config.services[ 'tests-mysql' ].healthcheck.test ).toEqual( [
|
|
177
|
+
'CMD',
|
|
178
|
+
'healthcheck.sh',
|
|
179
|
+
'--connect',
|
|
180
|
+
'--innodb_initialized',
|
|
181
|
+
] );
|
|
182
|
+
expect(
|
|
183
|
+
config.services[ 'tests-mysql' ].environment.MARIADB_AUTO_UPGRADE
|
|
184
|
+
).toBe( '1' );
|
|
185
|
+
} );
|
|
186
|
+
|
|
187
|
+
it( 'should use service_healthy condition for WordPress depends_on', () => {
|
|
188
|
+
const config = buildDockerComposeConfig( {
|
|
189
|
+
workDirectoryPath: '/some/path',
|
|
190
|
+
env: {
|
|
191
|
+
development: {
|
|
192
|
+
port: 8888,
|
|
193
|
+
mysqlPort: 3306,
|
|
194
|
+
coreSource: null,
|
|
195
|
+
pluginSources: [],
|
|
196
|
+
themeSources: [],
|
|
197
|
+
mappings: {},
|
|
198
|
+
},
|
|
199
|
+
tests: {
|
|
200
|
+
port: 8889,
|
|
201
|
+
mysqlPort: 3307,
|
|
202
|
+
coreSource: null,
|
|
203
|
+
pluginSources: [],
|
|
204
|
+
themeSources: [],
|
|
205
|
+
mappings: {},
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
} );
|
|
209
|
+
|
|
210
|
+
expect( config.services.wordpress.depends_on ).toEqual( {
|
|
211
|
+
mysql: { condition: 'service_healthy' },
|
|
212
|
+
} );
|
|
213
|
+
expect( config.services[ 'tests-wordpress' ].depends_on ).toEqual( {
|
|
214
|
+
'tests-mysql': { condition: 'service_healthy' },
|
|
215
|
+
} );
|
|
216
|
+
} );
|
|
134
217
|
} );
|
package/lib/test/cli.js
CHANGED
|
@@ -19,8 +19,11 @@ jest.mock( '../env', () => {
|
|
|
19
19
|
return {
|
|
20
20
|
start: jest.fn( Promise.resolve.bind( Promise ) ),
|
|
21
21
|
stop: jest.fn( Promise.resolve.bind( Promise ) ),
|
|
22
|
+
reset: jest.fn( Promise.resolve.bind( Promise ) ),
|
|
22
23
|
clean: jest.fn( Promise.resolve.bind( Promise ) ),
|
|
23
24
|
run: jest.fn( Promise.resolve.bind( Promise ) ),
|
|
25
|
+
destroy: jest.fn( Promise.resolve.bind( Promise ) ),
|
|
26
|
+
cleanup: jest.fn( Promise.resolve.bind( Promise ) ),
|
|
24
27
|
ValidationError: actual.ValidationError,
|
|
25
28
|
LifecycleScriptError: actual.LifecycleScriptError,
|
|
26
29
|
};
|
|
@@ -41,26 +44,33 @@ describe( 'env cli', () => {
|
|
|
41
44
|
expect( spinner.text ).toBe( '' );
|
|
42
45
|
} );
|
|
43
46
|
|
|
44
|
-
it( 'parses
|
|
45
|
-
cli().parse( [ '
|
|
46
|
-
const { environment, spinner } = env.
|
|
47
|
+
it( 'parses reset commands for the default environment.', () => {
|
|
48
|
+
cli().parse( [ 'reset' ] );
|
|
49
|
+
const { environment, spinner } = env.reset.mock.calls[ 0 ][ 0 ];
|
|
47
50
|
expect( environment ).toBe( 'tests' );
|
|
48
51
|
expect( spinner.text ).toBe( '' );
|
|
49
52
|
} );
|
|
50
|
-
it( 'parses
|
|
51
|
-
cli().parse( [ '
|
|
52
|
-
const { environment, spinner } = env.
|
|
53
|
+
it( 'parses reset commands for all environments.', () => {
|
|
54
|
+
cli().parse( [ 'reset', 'all' ] );
|
|
55
|
+
const { environment, spinner } = env.reset.mock.calls[ 0 ][ 0 ];
|
|
53
56
|
expect( environment ).toBe( 'all' );
|
|
54
57
|
expect( spinner.text ).toBe( '' );
|
|
55
58
|
} );
|
|
56
|
-
it( 'parses
|
|
57
|
-
cli().parse( [ '
|
|
58
|
-
const { environment, spinner } = env.
|
|
59
|
+
it( 'parses reset commands for the development environment.', () => {
|
|
60
|
+
cli().parse( [ 'reset', 'development' ] );
|
|
61
|
+
const { environment, spinner } = env.reset.mock.calls[ 0 ][ 0 ];
|
|
59
62
|
expect( environment ).toBe( 'development' );
|
|
60
63
|
expect( spinner.text ).toBe( '' );
|
|
61
64
|
} );
|
|
62
|
-
it( 'parses
|
|
63
|
-
cli().parse( [ '
|
|
65
|
+
it( 'parses reset commands for the tests environment.', () => {
|
|
66
|
+
cli().parse( [ 'reset', 'tests' ] );
|
|
67
|
+
const { environment, spinner } = env.reset.mock.calls[ 0 ][ 0 ];
|
|
68
|
+
expect( environment ).toBe( 'tests' );
|
|
69
|
+
expect( spinner.text ).toBe( '' );
|
|
70
|
+
} );
|
|
71
|
+
|
|
72
|
+
it( 'parses clean (deprecated) commands for the default environment.', () => {
|
|
73
|
+
cli().parse( [ 'clean' ] );
|
|
64
74
|
const { environment, spinner } = env.clean.mock.calls[ 0 ][ 0 ];
|
|
65
75
|
expect( environment ).toBe( 'tests' );
|
|
66
76
|
expect( spinner.text ).toBe( '' );
|
|
@@ -81,6 +91,32 @@ describe( 'env cli', () => {
|
|
|
81
91
|
expect( spinner.text ).toBe( '' );
|
|
82
92
|
} );
|
|
83
93
|
|
|
94
|
+
it( 'parses destroy commands.', () => {
|
|
95
|
+
cli().parse( [ 'destroy' ] );
|
|
96
|
+
const { spinner, scripts, force } = env.destroy.mock.calls[ 0 ][ 0 ];
|
|
97
|
+
expect( spinner.text ).toBe( '' );
|
|
98
|
+
expect( scripts ).toBe( true );
|
|
99
|
+
expect( force ).toBe( false );
|
|
100
|
+
} );
|
|
101
|
+
it( 'parses destroy commands with --force flag.', () => {
|
|
102
|
+
cli().parse( [ 'destroy', '--force' ] );
|
|
103
|
+
const { force } = env.destroy.mock.calls[ 0 ][ 0 ];
|
|
104
|
+
expect( force ).toBe( true );
|
|
105
|
+
} );
|
|
106
|
+
|
|
107
|
+
it( 'parses cleanup commands.', () => {
|
|
108
|
+
cli().parse( [ 'cleanup' ] );
|
|
109
|
+
const { spinner, scripts, force } = env.cleanup.mock.calls[ 0 ][ 0 ];
|
|
110
|
+
expect( spinner.text ).toBe( '' );
|
|
111
|
+
expect( scripts ).toBe( true );
|
|
112
|
+
expect( force ).toBe( false );
|
|
113
|
+
} );
|
|
114
|
+
it( 'parses cleanup commands with --force flag.', () => {
|
|
115
|
+
cli().parse( [ 'cleanup', '--force' ] );
|
|
116
|
+
const { force } = env.cleanup.mock.calls[ 0 ][ 0 ];
|
|
117
|
+
expect( force ).toBe( true );
|
|
118
|
+
} );
|
|
119
|
+
|
|
84
120
|
it( 'handles successful commands with messages.', async () => {
|
|
85
121
|
env.start.mockResolvedValueOnce( 'success message' );
|
|
86
122
|
cli().parse( [ 'start' ] );
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/env",
|
|
3
|
-
"version": "11.0.1-next.v.0+
|
|
3
|
+
"version": "11.0.1-next.v.20260206T143.0+81f8de885",
|
|
4
4
|
"description": "A zero-config, self contained local WordPress environment for development and testing.",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"scripts": {
|
|
65
65
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "6ae3fbb907e6c309ff6a0685e5e5ff0c2ee23b15"
|
|
68
68
|
}
|