@wordpress/env 11.0.1-next.v.0 → 11.0.1-next.v.202602091733.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 +61 -9
- package/lib/cli.js +56 -5
- package/lib/commands/clean.js +13 -4
- package/lib/commands/cleanup.js +79 -0
- package/lib/commands/destroy.js +30 -18
- package/lib/commands/index.js +4 -0
- package/lib/commands/logs.js +17 -8
- package/lib/commands/reset.js +46 -0
- package/lib/commands/run.js +13 -9
- package/lib/commands/start.js +47 -10
- package/lib/commands/status.js +13 -7
- package/lib/commands/stop.js +13 -6
- package/lib/config/get-config-from-environment-vars.js +2 -5
- package/lib/config/load-config.js +34 -5
- package/lib/config/parse-config.js +44 -21
- package/lib/config/test/__snapshots__/config-integration.js.snap +12 -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 +79 -29
- package/lib/runtime/docker/init-config.js +13 -11
- 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/lib/commands/status.js
CHANGED
|
@@ -39,15 +39,21 @@ function isEnvironmentInitialized( config ) {
|
|
|
39
39
|
/**
|
|
40
40
|
* Outputs the status of the wp-env environment.
|
|
41
41
|
*
|
|
42
|
-
* @param {Object}
|
|
43
|
-
* @param {Object}
|
|
44
|
-
* @param {boolean}
|
|
45
|
-
* @param {boolean}
|
|
42
|
+
* @param {Object} options
|
|
43
|
+
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
44
|
+
* @param {boolean} options.debug True if debug mode is enabled.
|
|
45
|
+
* @param {boolean} options.json True to output as JSON.
|
|
46
|
+
* @param {string|null} options.config Path to a custom .wp-env.json configuration file.
|
|
46
47
|
*/
|
|
47
|
-
module.exports = async function status( {
|
|
48
|
+
module.exports = async function status( {
|
|
49
|
+
spinner,
|
|
50
|
+
debug,
|
|
51
|
+
json,
|
|
52
|
+
config: customConfigPath,
|
|
53
|
+
} ) {
|
|
48
54
|
spinner.text = 'Getting environment status.';
|
|
49
55
|
|
|
50
|
-
const config = await loadConfig( path.resolve( '.' ) );
|
|
56
|
+
const config = await loadConfig( path.resolve( '.' ), customConfigPath );
|
|
51
57
|
|
|
52
58
|
// Check if environment is initialized by looking for runtime-specific files.
|
|
53
59
|
// We check for these files specifically because the work directory may exist
|
|
@@ -70,7 +76,7 @@ module.exports = async function status( { spinner, debug, json } ) {
|
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
// Detect and get runtime.
|
|
73
|
-
const runtimeName = detectRuntime( config.workDirectoryPath );
|
|
79
|
+
const runtimeName = await detectRuntime( config.workDirectoryPath );
|
|
74
80
|
const runtime = getRuntime( runtimeName );
|
|
75
81
|
|
|
76
82
|
// Get status from runtime.
|
package/lib/commands/stop.js
CHANGED
|
@@ -13,12 +13,19 @@ const { getRuntime, detectRuntime } = require( '../runtime' );
|
|
|
13
13
|
/**
|
|
14
14
|
* Stops the development server.
|
|
15
15
|
*
|
|
16
|
-
* @param {Object}
|
|
17
|
-
* @param {Object}
|
|
18
|
-
* @param {boolean}
|
|
16
|
+
* @param {Object} options
|
|
17
|
+
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
18
|
+
* @param {boolean} options.debug True if debug mode is enabled.
|
|
19
|
+
* @param {string|null} options.config Path to a custom .wp-env.json configuration file.
|
|
19
20
|
*/
|
|
20
|
-
module.exports = async function stop( {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
module.exports = async function stop( {
|
|
22
|
+
spinner,
|
|
23
|
+
debug,
|
|
24
|
+
config: customConfigPath,
|
|
25
|
+
} ) {
|
|
26
|
+
const config = await loadConfig( path.resolve( '.' ), customConfigPath );
|
|
27
|
+
const runtime = getRuntime(
|
|
28
|
+
await detectRuntime( config.workDirectoryPath )
|
|
29
|
+
);
|
|
23
30
|
await runtime.stop( config, { spinner, debug } );
|
|
24
31
|
};
|
|
@@ -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 ) {
|
|
@@ -11,6 +11,7 @@ const fs = require( 'fs' ).promises;
|
|
|
11
11
|
const getCacheDirectory = require( './get-cache-directory' );
|
|
12
12
|
const md5 = require( '../md5' );
|
|
13
13
|
const { parseConfig, getConfigFilePath } = require( './parse-config' );
|
|
14
|
+
const { ValidationError } = require( './validate-config' );
|
|
14
15
|
const postProcessConfig = require( './post-process-config' );
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -35,12 +36,31 @@ const postProcessConfig = require( './post-process-config' );
|
|
|
35
36
|
/**
|
|
36
37
|
* Loads any configuration from a given directory.
|
|
37
38
|
*
|
|
38
|
-
* @param {string}
|
|
39
|
+
* @param {string} configDirectoryPath The directory we want to load the config from.
|
|
40
|
+
* @param {string|null} customConfigPath Optional custom config file path.
|
|
39
41
|
*
|
|
40
42
|
* @return {Promise<WPConfig>} The config object we've loaded.
|
|
41
43
|
*/
|
|
42
|
-
module.exports = async function loadConfig(
|
|
43
|
-
|
|
44
|
+
module.exports = async function loadConfig(
|
|
45
|
+
configDirectoryPath,
|
|
46
|
+
customConfigPath = null
|
|
47
|
+
) {
|
|
48
|
+
const configFilePath = getConfigFilePath(
|
|
49
|
+
configDirectoryPath,
|
|
50
|
+
'local',
|
|
51
|
+
customConfigPath
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// If a custom config path was provided, verify the file exists.
|
|
55
|
+
if ( customConfigPath ) {
|
|
56
|
+
try {
|
|
57
|
+
await fs.stat( configFilePath );
|
|
58
|
+
} catch ( error ) {
|
|
59
|
+
throw new ValidationError(
|
|
60
|
+
`Config file not found: ${ configFilePath }`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
44
64
|
|
|
45
65
|
const cacheDirectoryPath = path.resolve(
|
|
46
66
|
await getCacheDirectory(),
|
|
@@ -49,7 +69,11 @@ module.exports = async function loadConfig( configDirectoryPath ) {
|
|
|
49
69
|
|
|
50
70
|
// Parse any configuration we found in the given directory.
|
|
51
71
|
// This comes merged and prepared for internal consumption.
|
|
52
|
-
let config = await parseConfig(
|
|
72
|
+
let config = await parseConfig(
|
|
73
|
+
configDirectoryPath,
|
|
74
|
+
cacheDirectoryPath,
|
|
75
|
+
customConfigPath
|
|
76
|
+
);
|
|
53
77
|
|
|
54
78
|
// Make sure to perform any additional post-processing that
|
|
55
79
|
// may be needed before the config object is ready for
|
|
@@ -64,9 +88,14 @@ module.exports = async function loadConfig( configDirectoryPath ) {
|
|
|
64
88
|
),
|
|
65
89
|
configDirectoryPath,
|
|
66
90
|
workDirectoryPath: cacheDirectoryPath,
|
|
91
|
+
customConfigPath,
|
|
67
92
|
detectedLocalConfig: await hasLocalConfig( [
|
|
68
93
|
configFilePath,
|
|
69
|
-
getConfigFilePath(
|
|
94
|
+
getConfigFilePath(
|
|
95
|
+
configDirectoryPath,
|
|
96
|
+
'override',
|
|
97
|
+
customConfigPath
|
|
98
|
+
),
|
|
70
99
|
] ),
|
|
71
100
|
lifecycleScripts: config.lifecycleScripts,
|
|
72
101
|
env: config.env,
|
|
@@ -111,22 +111,27 @@ const DEFAULT_ENVIRONMENT_CONFIG = {
|
|
|
111
111
|
* constructs an object in the format used internally.
|
|
112
112
|
*
|
|
113
113
|
*
|
|
114
|
-
* @param {string}
|
|
115
|
-
* @param {string}
|
|
114
|
+
* @param {string} configDirectoryPath A path to the directory we are parsing the config for.
|
|
115
|
+
* @param {string} cacheDirectoryPath Path to the work directory located in ~/.wp-env.
|
|
116
|
+
* @param {string|null} customConfigPath Optional custom config file path.
|
|
116
117
|
*
|
|
117
118
|
* @return {Promise<WPRootConfig>} Parsed config.
|
|
118
119
|
*/
|
|
119
|
-
async function parseConfig(
|
|
120
|
+
async function parseConfig(
|
|
121
|
+
configDirectoryPath,
|
|
122
|
+
cacheDirectoryPath,
|
|
123
|
+
customConfigPath = null
|
|
124
|
+
) {
|
|
120
125
|
// The local config will be used to override any defaults.
|
|
121
126
|
const localConfig = await parseConfigFile(
|
|
122
|
-
getConfigFilePath( configDirectoryPath ),
|
|
127
|
+
getConfigFilePath( configDirectoryPath, 'local', customConfigPath ),
|
|
123
128
|
{ cacheDirectoryPath }
|
|
124
129
|
);
|
|
125
130
|
|
|
126
131
|
// Any overrides that can be used in place
|
|
127
132
|
// of properties set by the local config.
|
|
128
133
|
const overrideConfig = await parseConfigFile(
|
|
129
|
-
getConfigFilePath( configDirectoryPath, 'override' ),
|
|
134
|
+
getConfigFilePath( configDirectoryPath, 'override', customConfigPath ),
|
|
130
135
|
{ cacheDirectoryPath }
|
|
131
136
|
);
|
|
132
137
|
|
|
@@ -161,27 +166,37 @@ async function parseConfig( configDirectoryPath, cacheDirectoryPath ) {
|
|
|
161
166
|
/**
|
|
162
167
|
* Gets the path to the config file.
|
|
163
168
|
*
|
|
164
|
-
* @param {string}
|
|
165
|
-
* @param {string}
|
|
169
|
+
* @param {string} configDirectoryPath The path to the directory containing config files.
|
|
170
|
+
* @param {string} type The type of config file we're interested in: 'local' or 'override'.
|
|
171
|
+
* @param {string|null} customConfigPath Optional custom config file path (only used for 'local' type).
|
|
166
172
|
*
|
|
167
173
|
* @return {string} The path to the config file.
|
|
168
174
|
*/
|
|
169
|
-
function getConfigFilePath(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
175
|
+
function getConfigFilePath(
|
|
176
|
+
configDirectoryPath,
|
|
177
|
+
type = 'local',
|
|
178
|
+
customConfigPath = null
|
|
179
|
+
) {
|
|
180
|
+
// If a custom config path is provided for the local config, use it.
|
|
181
|
+
if ( type === 'local' && customConfigPath ) {
|
|
182
|
+
return path.resolve( customConfigPath );
|
|
183
|
+
}
|
|
176
184
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
185
|
+
// For override, derive from custom config: staging.json -> staging.override.json
|
|
186
|
+
if ( type === 'override' && customConfigPath ) {
|
|
187
|
+
const resolved = path.resolve( customConfigPath );
|
|
188
|
+
const ext = path.extname( resolved );
|
|
189
|
+
const base = path.basename( resolved, ext );
|
|
190
|
+
const dir = path.dirname( resolved );
|
|
191
|
+
return path.join( dir, `${ base }.override${ ext }` );
|
|
192
|
+
}
|
|
181
193
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
194
|
+
// Default behavior.
|
|
195
|
+
const fileName =
|
|
196
|
+
type === 'local' ? '.wp-env.json' : '.wp-env.override.json';
|
|
197
|
+
|
|
198
|
+
if ( type !== 'local' && type !== 'override' ) {
|
|
199
|
+
throw new Error( `Invalid config file type "${ type }.` );
|
|
185
200
|
}
|
|
186
201
|
|
|
187
202
|
return path.resolve( configDirectoryPath, fileName );
|
|
@@ -235,6 +250,8 @@ async function getDefaultConfig(
|
|
|
235
250
|
lifecycleScripts: {
|
|
236
251
|
afterStart: null,
|
|
237
252
|
afterClean: null,
|
|
253
|
+
afterReset: null,
|
|
254
|
+
afterCleanup: null,
|
|
238
255
|
afterDestroy: null,
|
|
239
256
|
},
|
|
240
257
|
env: {
|
|
@@ -312,6 +329,12 @@ function getEnvironmentVarOverrides( cacheDirectoryPath ) {
|
|
|
312
329
|
overrideConfig.env.tests.phpVersion = overrides.phpVersion;
|
|
313
330
|
}
|
|
314
331
|
|
|
332
|
+
if ( overrides.multisite ) {
|
|
333
|
+
overrideConfig.multisite = overrides.multisite;
|
|
334
|
+
overrideConfig.env.development.multisite = overrides.multisite;
|
|
335
|
+
overrideConfig.env.tests.multisite = overrides.multisite;
|
|
336
|
+
}
|
|
337
|
+
|
|
315
338
|
return overrideConfig;
|
|
316
339
|
}
|
|
317
340
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
exports[`Config Integration should load local and override configuration files 1`] = `
|
|
4
4
|
{
|
|
5
5
|
"configDirectoryPath": "/test/gutenberg",
|
|
6
|
+
"customConfigPath": null,
|
|
6
7
|
"detectedLocalConfig": true,
|
|
7
8
|
"dockerComposeConfigPath": "/cache/5fea4c5689ef6cc4a4e6eaaa39323338/docker-compose.yml",
|
|
8
9
|
"env": {
|
|
@@ -71,7 +72,9 @@ exports[`Config Integration should load local and override configuration files 1
|
|
|
71
72
|
},
|
|
72
73
|
"lifecycleScripts": {
|
|
73
74
|
"afterClean": null,
|
|
75
|
+
"afterCleanup": null,
|
|
74
76
|
"afterDestroy": "test",
|
|
77
|
+
"afterReset": null,
|
|
75
78
|
"afterStart": null,
|
|
76
79
|
},
|
|
77
80
|
"name": "gutenberg",
|
|
@@ -82,6 +85,7 @@ exports[`Config Integration should load local and override configuration files 1
|
|
|
82
85
|
exports[`Config Integration should load local configuration file 1`] = `
|
|
83
86
|
{
|
|
84
87
|
"configDirectoryPath": "/test/gutenberg",
|
|
88
|
+
"customConfigPath": null,
|
|
85
89
|
"detectedLocalConfig": true,
|
|
86
90
|
"dockerComposeConfigPath": "/cache/5fea4c5689ef6cc4a4e6eaaa39323338/docker-compose.yml",
|
|
87
91
|
"env": {
|
|
@@ -150,7 +154,9 @@ exports[`Config Integration should load local configuration file 1`] = `
|
|
|
150
154
|
},
|
|
151
155
|
"lifecycleScripts": {
|
|
152
156
|
"afterClean": null,
|
|
157
|
+
"afterCleanup": null,
|
|
153
158
|
"afterDestroy": null,
|
|
159
|
+
"afterReset": null,
|
|
154
160
|
"afterStart": "test",
|
|
155
161
|
},
|
|
156
162
|
"name": "gutenberg",
|
|
@@ -161,6 +167,7 @@ exports[`Config Integration should load local configuration file 1`] = `
|
|
|
161
167
|
exports[`Config Integration should use default configuration 1`] = `
|
|
162
168
|
{
|
|
163
169
|
"configDirectoryPath": "/test/gutenberg",
|
|
170
|
+
"customConfigPath": null,
|
|
164
171
|
"detectedLocalConfig": true,
|
|
165
172
|
"dockerComposeConfigPath": "/cache/5fea4c5689ef6cc4a4e6eaaa39323338/docker-compose.yml",
|
|
166
173
|
"env": {
|
|
@@ -229,7 +236,9 @@ exports[`Config Integration should use default configuration 1`] = `
|
|
|
229
236
|
},
|
|
230
237
|
"lifecycleScripts": {
|
|
231
238
|
"afterClean": null,
|
|
239
|
+
"afterCleanup": null,
|
|
232
240
|
"afterDestroy": null,
|
|
241
|
+
"afterReset": null,
|
|
233
242
|
"afterStart": null,
|
|
234
243
|
},
|
|
235
244
|
"name": "gutenberg",
|
|
@@ -240,6 +249,7 @@ exports[`Config Integration should use default configuration 1`] = `
|
|
|
240
249
|
exports[`Config Integration should use environment variables over local and override configuration files 1`] = `
|
|
241
250
|
{
|
|
242
251
|
"configDirectoryPath": "/test/gutenberg",
|
|
252
|
+
"customConfigPath": null,
|
|
243
253
|
"detectedLocalConfig": true,
|
|
244
254
|
"dockerComposeConfigPath": "/cache/5fea4c5689ef6cc4a4e6eaaa39323338/docker-compose.yml",
|
|
245
255
|
"env": {
|
|
@@ -310,7 +320,9 @@ exports[`Config Integration should use environment variables over local and over
|
|
|
310
320
|
},
|
|
311
321
|
"lifecycleScripts": {
|
|
312
322
|
"afterClean": null,
|
|
323
|
+
"afterCleanup": null,
|
|
313
324
|
"afterDestroy": null,
|
|
325
|
+
"afterReset": null,
|
|
314
326
|
"afterStart": "test",
|
|
315
327
|
},
|
|
316
328
|
"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,
|
|
@@ -109,6 +107,7 @@ class DockerRuntime {
|
|
|
109
107
|
xdebug,
|
|
110
108
|
spx,
|
|
111
109
|
writeChanges: true,
|
|
110
|
+
customConfigPath: config.customConfigPath,
|
|
112
111
|
} );
|
|
113
112
|
|
|
114
113
|
// Check if the hash of the config has changed. If so, run configuration.
|
|
@@ -174,7 +173,7 @@ class DockerRuntime {
|
|
|
174
173
|
}
|
|
175
174
|
|
|
176
175
|
await Promise.all( [
|
|
177
|
-
dockerCompose.
|
|
176
|
+
dockerCompose.upMany( [ 'mysql', 'tests-mysql' ], {
|
|
178
177
|
...dockerComposeConfig,
|
|
179
178
|
commandOptions: shouldConfigureWp
|
|
180
179
|
? [ '--build', '--force-recreate' ]
|
|
@@ -250,19 +249,6 @@ class DockerRuntime {
|
|
|
250
249
|
if ( shouldConfigureWp ) {
|
|
251
250
|
spinner.text = 'Configuring WordPress.';
|
|
252
251
|
|
|
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
252
|
// Retry WordPress installation in case MySQL *still* wasn't ready.
|
|
267
253
|
await Promise.all( [
|
|
268
254
|
retry(
|
|
@@ -369,6 +355,15 @@ class DockerRuntime {
|
|
|
369
355
|
return 'WARNING! This will remove Docker containers, volumes, networks, and images associated with the WordPress instance.';
|
|
370
356
|
}
|
|
371
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Get the warning message for cleanup confirmation.
|
|
360
|
+
*
|
|
361
|
+
* @return {string} Warning message.
|
|
362
|
+
*/
|
|
363
|
+
getCleanupWarningMessage() {
|
|
364
|
+
return 'WARNING! This will remove Docker containers, volumes, networks, and local files associated with the WordPress instance. Docker images will be preserved.';
|
|
365
|
+
}
|
|
366
|
+
|
|
372
367
|
/**
|
|
373
368
|
* Stop the Docker containers.
|
|
374
369
|
*
|
|
@@ -381,6 +376,7 @@ class DockerRuntime {
|
|
|
381
376
|
const { dockerComposeConfigPath } = await initConfig( {
|
|
382
377
|
spinner,
|
|
383
378
|
debug,
|
|
379
|
+
customConfigPath: config.customConfigPath,
|
|
384
380
|
} );
|
|
385
381
|
|
|
386
382
|
spinner.text = 'Stopping WordPress.';
|
|
@@ -413,7 +409,8 @@ class DockerRuntime {
|
|
|
413
409
|
spinner.text = 'Removing local files.';
|
|
414
410
|
// Note: there is a race condition where docker compose actually hasn't finished
|
|
415
411
|
// 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
|
|
412
|
+
// but using 10s in case it's dependent on the machine. Removing images takes
|
|
413
|
+
// longer so we use a longer wait time here.
|
|
417
414
|
await new Promise( ( resolve ) => setTimeout( resolve, 10000 ) );
|
|
418
415
|
await rimraf( config.workDirectoryPath );
|
|
419
416
|
|
|
@@ -421,27 +418,68 @@ class DockerRuntime {
|
|
|
421
418
|
}
|
|
422
419
|
|
|
423
420
|
/**
|
|
424
|
-
*
|
|
421
|
+
* Cleanup the Docker containers and remove local files, but preserve images.
|
|
422
|
+
*
|
|
423
|
+
* @param {WPConfig} config The wp-env config object.
|
|
424
|
+
* @param {Object} options Cleanup options.
|
|
425
|
+
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
426
|
+
* @param {boolean} options.debug True if debug mode is enabled.
|
|
427
|
+
*/
|
|
428
|
+
async cleanup( config, { spinner, debug } ) {
|
|
429
|
+
spinner.text = 'Removing docker containers, volumes, and networks.';
|
|
430
|
+
|
|
431
|
+
await dockerCompose.down( {
|
|
432
|
+
config: config.dockerComposeConfigPath,
|
|
433
|
+
commandOptions: [ '--volumes', '--remove-orphans' ],
|
|
434
|
+
log: debug,
|
|
435
|
+
} );
|
|
436
|
+
|
|
437
|
+
spinner.text = 'Removing local files.';
|
|
438
|
+
// Note: there is a race condition where docker compose actually hasn't finished
|
|
439
|
+
// by this point, which causes rimraf to fail. We need to wait at least 2.5-5s,
|
|
440
|
+
// but since we're not removing images, the wait can be shorter.
|
|
441
|
+
await new Promise( ( resolve ) => setTimeout( resolve, 3000 ) );
|
|
442
|
+
await rimraf( config.workDirectoryPath );
|
|
443
|
+
|
|
444
|
+
spinner.text = 'Cleaned up WordPress environment.';
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Reset the WordPress database.
|
|
425
449
|
*
|
|
426
450
|
* @param {WPConfig} config The wp-env config object.
|
|
427
|
-
* @param {Object} options
|
|
428
|
-
* @param {string} options.environment The environment to
|
|
451
|
+
* @param {Object} options Reset options.
|
|
452
|
+
* @param {string} options.environment The environment to reset.
|
|
429
453
|
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
430
454
|
* @param {boolean} options.debug True if debug mode is enabled.
|
|
431
455
|
*/
|
|
432
456
|
async clean( config, { environment, spinner, debug } ) {
|
|
433
|
-
const fullConfig = await initConfig( {
|
|
457
|
+
const fullConfig = await initConfig( {
|
|
458
|
+
spinner,
|
|
459
|
+
debug,
|
|
460
|
+
customConfigPath: config.customConfigPath,
|
|
461
|
+
} );
|
|
434
462
|
|
|
435
463
|
const description = `${ environment } environment${
|
|
436
464
|
environment === 'all' ? 's' : ''
|
|
437
465
|
}`;
|
|
438
|
-
spinner.text = `
|
|
466
|
+
spinner.text = `Resetting ${ description }.`;
|
|
439
467
|
|
|
440
468
|
const tasks = [];
|
|
441
469
|
|
|
442
|
-
// Start the
|
|
443
|
-
//
|
|
444
|
-
|
|
470
|
+
// Start the appropriate MySQL service(s) first to avoid race conditions
|
|
471
|
+
// where parallel tasks try to create docker networks with the same name.
|
|
472
|
+
// The dependency chain (cli -> wordpress -> mysql with service_healthy)
|
|
473
|
+
// ensures MySQL is ready before database operations run.
|
|
474
|
+
const mysqlServices = [];
|
|
475
|
+
if ( environment === 'all' || environment === 'development' ) {
|
|
476
|
+
mysqlServices.push( 'mysql' );
|
|
477
|
+
}
|
|
478
|
+
if ( environment === 'all' || environment === 'tests' ) {
|
|
479
|
+
mysqlServices.push( 'tests-mysql' );
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
await dockerCompose.upMany( mysqlServices, {
|
|
445
483
|
config: fullConfig.dockerComposeConfigPath,
|
|
446
484
|
log: fullConfig.debug,
|
|
447
485
|
} );
|
|
@@ -466,7 +504,7 @@ class DockerRuntime {
|
|
|
466
504
|
|
|
467
505
|
await Promise.all( tasks );
|
|
468
506
|
|
|
469
|
-
spinner.text = `
|
|
507
|
+
spinner.text = `Reset ${ description }.`;
|
|
470
508
|
}
|
|
471
509
|
|
|
472
510
|
/**
|
|
@@ -493,7 +531,11 @@ class DockerRuntime {
|
|
|
493
531
|
// Validate the container name (throws for deprecated containers)
|
|
494
532
|
validateRunContainer( container );
|
|
495
533
|
|
|
496
|
-
const fullConfig = await initConfig( {
|
|
534
|
+
const fullConfig = await initConfig( {
|
|
535
|
+
spinner,
|
|
536
|
+
debug,
|
|
537
|
+
customConfigPath: config.customConfigPath,
|
|
538
|
+
} );
|
|
497
539
|
|
|
498
540
|
// Shows a contextual tip for the given command.
|
|
499
541
|
const joinedCommand = command.join( ' ' );
|
|
@@ -520,7 +562,11 @@ class DockerRuntime {
|
|
|
520
562
|
* @param {boolean} options.debug True if debug mode is enabled.
|
|
521
563
|
*/
|
|
522
564
|
async logs( config, { environment, watch, spinner, debug } ) {
|
|
523
|
-
const fullConfig = await initConfig( {
|
|
565
|
+
const fullConfig = await initConfig( {
|
|
566
|
+
spinner,
|
|
567
|
+
debug,
|
|
568
|
+
customConfigPath: config.customConfigPath,
|
|
569
|
+
} );
|
|
524
570
|
|
|
525
571
|
// If we show text while watching the logs, it will continue showing up every
|
|
526
572
|
// few lines in the logs as they happen, which isn't a good look. So only
|
|
@@ -589,7 +635,11 @@ class DockerRuntime {
|
|
|
589
635
|
async getStatus( config, { spinner, debug } ) {
|
|
590
636
|
spinner.text = 'Getting environment status.';
|
|
591
637
|
|
|
592
|
-
const fullConfig = await initConfig( {
|
|
638
|
+
const fullConfig = await initConfig( {
|
|
639
|
+
spinner,
|
|
640
|
+
debug,
|
|
641
|
+
customConfigPath: config.customConfigPath,
|
|
642
|
+
} );
|
|
593
643
|
const dockerComposeConfig = {
|
|
594
644
|
config: fullConfig.dockerComposeConfigPath,
|
|
595
645
|
log: debug,
|
|
@@ -22,16 +22,17 @@ const buildDockerComposeConfig = require( './build-docker-compose-config' );
|
|
|
22
22
|
* ./.wp-env.json, creates ~/.wp-env, ~/.wp-env/docker-compose.yml, and
|
|
23
23
|
* ~/.wp-env/Dockerfile.
|
|
24
24
|
*
|
|
25
|
-
* @param {Object}
|
|
26
|
-
* @param {Object}
|
|
27
|
-
* @param {boolean}
|
|
28
|
-
* @param {string}
|
|
29
|
-
* @param {string}
|
|
30
|
-
* @param {boolean}
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
25
|
+
* @param {Object} options
|
|
26
|
+
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
27
|
+
* @param {boolean} options.debug True if debug mode is enabled.
|
|
28
|
+
* @param {string} options.xdebug The Xdebug mode to set. Defaults to "off".
|
|
29
|
+
* @param {string} options.spx The SPX mode to set. Defaults to "off".
|
|
30
|
+
* @param {boolean} options.writeChanges If true, writes the parsed config to the
|
|
31
|
+
* required docker files like docker-compose
|
|
32
|
+
* and Dockerfile. By default, this is false
|
|
33
|
+
* and only the `start` command writes any
|
|
34
|
+
* changes.
|
|
35
|
+
* @param {string|null} options.customConfigPath Path to a custom .wp-env.json configuration file.
|
|
35
36
|
* @return {WPConfig} The-env config object.
|
|
36
37
|
*/
|
|
37
38
|
module.exports = async function initConfig( {
|
|
@@ -40,8 +41,9 @@ module.exports = async function initConfig( {
|
|
|
40
41
|
xdebug = 'off',
|
|
41
42
|
spx = 'off',
|
|
42
43
|
writeChanges = false,
|
|
44
|
+
customConfigPath = null,
|
|
43
45
|
} ) {
|
|
44
|
-
const config = await loadConfig( path.resolve( '.' ) );
|
|
46
|
+
const config = await loadConfig( path.resolve( '.' ), customConfigPath );
|
|
45
47
|
config.debug = debug;
|
|
46
48
|
|
|
47
49
|
// Adding this to the config allows the start command to understand that the
|