@wordpress/env 6.0.0 → 7.0.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 +109 -43
- package/lib/build-docker-compose-config.js +67 -96
- package/lib/cache.js +1 -0
- package/lib/cli.js +16 -3
- package/lib/commands/clean.js +14 -1
- package/lib/commands/destroy.js +12 -37
- package/lib/commands/index.js +1 -0
- package/lib/commands/install-path.js +1 -0
- package/lib/commands/logs.js +1 -0
- package/lib/commands/run.js +59 -3
- package/lib/commands/start.js +30 -8
- package/lib/commands/stop.js +1 -0
- package/lib/config/add-or-replace-port.js +12 -3
- package/lib/config/db-env.js +1 -0
- package/lib/config/detect-directory-type.js +1 -1
- package/lib/config/get-cache-directory.js +39 -0
- package/lib/config/get-config-from-environment-vars.js +86 -0
- package/lib/config/index.js +7 -5
- package/lib/config/load-config.js +92 -0
- package/lib/config/merge-configs.js +104 -0
- package/lib/config/parse-config.js +418 -157
- package/lib/config/parse-source-string.js +164 -0
- package/lib/config/post-process-config.js +202 -0
- package/lib/config/read-raw-config-file.js +7 -33
- package/lib/config/test/__snapshots__/config-integration.js.snap +271 -0
- package/lib/config/test/add-or-replace-port.js +29 -1
- package/lib/config/test/config-integration.js +143 -0
- package/lib/config/test/get-cache-directory.js +57 -0
- package/lib/config/test/merge-configs.js +111 -0
- package/lib/config/test/parse-config.js +342 -0
- package/lib/config/test/parse-source-string.js +154 -0
- package/lib/config/test/post-process-config.js +295 -0
- package/lib/config/test/read-raw-config-file.js +19 -63
- package/lib/config/test/validate-config.js +305 -0
- package/lib/config/validate-config.js +93 -54
- package/lib/env.js +2 -0
- package/lib/execute-after-setup.js +51 -0
- package/lib/get-host-user.js +31 -0
- package/lib/init-config.js +181 -63
- package/lib/md5.js +1 -0
- package/lib/parse-xdebug-mode.js +1 -0
- package/lib/retry.js +1 -0
- package/lib/test/__snapshots__/md5.js.snap +9 -0
- package/lib/test/build-docker-compose-config.js +134 -0
- package/lib/test/cache.js +154 -0
- package/lib/test/cli.js +149 -0
- package/lib/test/execute-after-setup.js +66 -0
- package/lib/test/md5.js +35 -0
- package/lib/test/parse-xdebug-mode.js +41 -0
- package/lib/wordpress.js +4 -19
- package/package.json +2 -2
- package/lib/config/config.js +0 -353
- package/lib/config/test/__snapshots__/config.js.snap +0 -83
- package/lib/config/test/config.js +0 -1241
package/lib/commands/destroy.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use strict';
|
|
1
2
|
/**
|
|
2
3
|
* External dependencies
|
|
3
4
|
*/
|
|
@@ -11,12 +12,11 @@ const inquirer = require( 'inquirer' );
|
|
|
11
12
|
* Promisified dependencies
|
|
12
13
|
*/
|
|
13
14
|
const rimraf = util.promisify( require( 'rimraf' ) );
|
|
14
|
-
const exec = util.promisify( require( 'child_process' ).exec );
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Internal dependencies
|
|
18
18
|
*/
|
|
19
|
-
const {
|
|
19
|
+
const { loadConfig } = require( '../config' );
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Destroy the development server.
|
|
@@ -26,9 +26,8 @@ const { readConfig } = require( '../../lib/config' );
|
|
|
26
26
|
* @param {boolean} options.debug True if debug mode is enabled.
|
|
27
27
|
*/
|
|
28
28
|
module.exports = async function destroy( { spinner, debug } ) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
configPath
|
|
29
|
+
const { dockerComposeConfigPath, workDirectoryPath } = await loadConfig(
|
|
30
|
+
path.resolve( '.' )
|
|
32
31
|
);
|
|
33
32
|
|
|
34
33
|
try {
|
|
@@ -39,7 +38,7 @@ module.exports = async function destroy( { spinner, debug } ) {
|
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
spinner.info(
|
|
42
|
-
'WARNING! This will remove Docker containers, volumes, and
|
|
41
|
+
'WARNING! This will remove Docker containers, volumes, networks, and images associated with the WordPress instance.'
|
|
43
42
|
);
|
|
44
43
|
|
|
45
44
|
const { yesDelete } = await inquirer.prompt( [
|
|
@@ -58,44 +57,20 @@ module.exports = async function destroy( { spinner, debug } ) {
|
|
|
58
57
|
return;
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
spinner.text = 'Removing
|
|
60
|
+
spinner.text = 'Removing docker images, volumes, and networks.';
|
|
62
61
|
|
|
63
|
-
await dockerCompose.
|
|
62
|
+
await dockerCompose.down( {
|
|
64
63
|
config: dockerComposeConfigPath,
|
|
65
|
-
commandOptions: [ '--
|
|
64
|
+
commandOptions: [ '--volumes', '--remove-orphans', '--rmi', 'all' ],
|
|
66
65
|
log: debug,
|
|
67
66
|
} );
|
|
68
67
|
|
|
69
|
-
const directoryHash = path.basename( workDirectoryPath );
|
|
70
|
-
|
|
71
|
-
spinner.text = 'Removing docker volumes.';
|
|
72
|
-
await removeDockerItems( 'volume', directoryHash );
|
|
73
|
-
|
|
74
|
-
spinner.text = 'Removing docker networks.';
|
|
75
|
-
await removeDockerItems( 'network', directoryHash );
|
|
76
|
-
|
|
77
68
|
spinner.text = 'Removing local files.';
|
|
78
|
-
|
|
69
|
+
// Note: there is a race condition where docker compose actually hasn't finished
|
|
70
|
+
// by this point, which causes rimraf to fail. We need to wait at least 2.5-5s,
|
|
71
|
+
// but using 10s in case it's dependant on the machine.
|
|
72
|
+
await new Promise( ( resolve ) => setTimeout( resolve, 10000 ) );
|
|
79
73
|
await rimraf( workDirectoryPath );
|
|
80
74
|
|
|
81
75
|
spinner.text = 'Removed WordPress environment.';
|
|
82
76
|
};
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Removes docker items, like networks or volumes, matching the given name.
|
|
86
|
-
*
|
|
87
|
-
* @param {string} itemType The item type, like "network" or "volume"
|
|
88
|
-
* @param {string} name Remove items whose name match this string.
|
|
89
|
-
*/
|
|
90
|
-
async function removeDockerItems( itemType, name ) {
|
|
91
|
-
const { stdout: items } = await exec(
|
|
92
|
-
`docker ${ itemType } ls -q --filter name=${ name }`
|
|
93
|
-
);
|
|
94
|
-
if ( items ) {
|
|
95
|
-
await exec(
|
|
96
|
-
`docker ${ itemType } rm ${ items
|
|
97
|
-
.split( '\n' ) // TODO: use os.EOL?
|
|
98
|
-
.join( ' ' ) }`
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
}
|
package/lib/commands/index.js
CHANGED
package/lib/commands/logs.js
CHANGED
package/lib/commands/run.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use strict';
|
|
1
2
|
/**
|
|
2
3
|
* External dependencies
|
|
3
4
|
*/
|
|
@@ -8,6 +9,8 @@ const path = require( 'path' );
|
|
|
8
9
|
* Internal dependencies
|
|
9
10
|
*/
|
|
10
11
|
const initConfig = require( '../init-config' );
|
|
12
|
+
const getHostUser = require( '../get-host-user' );
|
|
13
|
+
const { ValidationError } = require( '../config' );
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* @typedef {import('../config').WPConfig} WPConfig
|
|
@@ -30,6 +33,8 @@ module.exports = async function run( {
|
|
|
30
33
|
spinner,
|
|
31
34
|
debug,
|
|
32
35
|
} ) {
|
|
36
|
+
validateContainerExistence( container );
|
|
37
|
+
|
|
33
38
|
const config = await initConfig( { spinner, debug } );
|
|
34
39
|
|
|
35
40
|
command = command.join( ' ' );
|
|
@@ -42,6 +47,42 @@ module.exports = async function run( {
|
|
|
42
47
|
spinner.text = `Ran \`${ command }\` in '${ container }'.`;
|
|
43
48
|
};
|
|
44
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Validates the container option and throws if it is invalid.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} container The Docker container to run the command on.
|
|
54
|
+
*/
|
|
55
|
+
function validateContainerExistence( container ) {
|
|
56
|
+
// Give better errors for containers that we have removed.
|
|
57
|
+
if ( container === 'phpunit' ) {
|
|
58
|
+
throw new ValidationError(
|
|
59
|
+
"The 'phpunit' container has been removed. Please use 'wp-env run tests-cli --env-cwd=wp-content/path/to/plugin phpunit' instead."
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if ( container === 'composer' ) {
|
|
63
|
+
throw new ValidationError(
|
|
64
|
+
"The 'composer' container has been removed. Please use 'wp-env run cli --env-cwd=wp-content/path/to/plugin composer' instead."
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Provide better error output than Docker's "service does not exist" messaging.
|
|
69
|
+
const validContainers = [
|
|
70
|
+
'mysql',
|
|
71
|
+
'tests-mysql',
|
|
72
|
+
'wordpress',
|
|
73
|
+
'tests-wordpress',
|
|
74
|
+
'cli',
|
|
75
|
+
'tests-cli',
|
|
76
|
+
];
|
|
77
|
+
if ( ! validContainers.includes( container ) ) {
|
|
78
|
+
throw new ValidationError(
|
|
79
|
+
`The '${ container }' container does not exist. Valid selections are: ${ validContainers.join(
|
|
80
|
+
', '
|
|
81
|
+
) }`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
45
86
|
/**
|
|
46
87
|
* Runs an arbitrary command on the given Docker container.
|
|
47
88
|
*
|
|
@@ -52,16 +93,31 @@ module.exports = async function run( {
|
|
|
52
93
|
* @param {Object} spinner A CLI spinner which indicates progress.
|
|
53
94
|
*/
|
|
54
95
|
function spawnCommandDirectly( config, container, command, envCwd, spinner ) {
|
|
96
|
+
// Both the `wordpress` and `tests-wordpress` containers have the host's
|
|
97
|
+
// user so that they can maintain ownership parity with the host OS.
|
|
98
|
+
// We should run any commands as that user so that they are able
|
|
99
|
+
// to interact with the files mounted from the host.
|
|
100
|
+
const hostUser = getHostUser();
|
|
101
|
+
|
|
55
102
|
// We need to pass absolute paths to the container.
|
|
56
|
-
envCwd = path.resolve(
|
|
103
|
+
envCwd = path.resolve(
|
|
104
|
+
// Not all containers have the same starting working directory.
|
|
105
|
+
container === 'mysql' || container === 'tests-mysql'
|
|
106
|
+
? '/'
|
|
107
|
+
: '/var/www/html',
|
|
108
|
+
envCwd
|
|
109
|
+
);
|
|
57
110
|
|
|
111
|
+
const isTTY = process.stdout.isTTY;
|
|
58
112
|
const composeCommand = [
|
|
59
113
|
'-f',
|
|
60
114
|
config.dockerComposeConfigPath,
|
|
61
|
-
'
|
|
115
|
+
'exec',
|
|
116
|
+
! isTTY ? '-T' : '',
|
|
62
117
|
'-w',
|
|
63
118
|
envCwd,
|
|
64
|
-
'--
|
|
119
|
+
'--user',
|
|
120
|
+
hostUser.fullUser,
|
|
65
121
|
container,
|
|
66
122
|
...command.split( ' ' ), // The command will fail if passed as a complete string.
|
|
67
123
|
];
|
package/lib/commands/start.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use strict';
|
|
1
2
|
/**
|
|
2
3
|
* External dependencies
|
|
3
4
|
*/
|
|
@@ -30,6 +31,7 @@ const {
|
|
|
30
31
|
} = require( '../wordpress' );
|
|
31
32
|
const { didCacheChange, setCache } = require( '../cache' );
|
|
32
33
|
const md5 = require( '../md5' );
|
|
34
|
+
const { executeAfterSetup } = require( '../execute-after-setup' );
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
37
|
* @typedef {import('../config').WPConfig} WPConfig
|
|
@@ -41,11 +43,18 @@ const CONFIG_CACHE_KEY = 'config_checksum';
|
|
|
41
43
|
*
|
|
42
44
|
* @param {Object} options
|
|
43
45
|
* @param {Object} options.spinner A CLI spinner which indicates progress.
|
|
44
|
-
* @param {boolean} options.debug True if debug mode is enabled.
|
|
45
46
|
* @param {boolean} options.update If true, update sources.
|
|
46
47
|
* @param {string} options.xdebug The Xdebug mode to set.
|
|
48
|
+
* @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
|
|
49
|
+
* @param {boolean} options.debug True if debug mode is enabled.
|
|
47
50
|
*/
|
|
48
|
-
module.exports = async function start( {
|
|
51
|
+
module.exports = async function start( {
|
|
52
|
+
spinner,
|
|
53
|
+
update,
|
|
54
|
+
xdebug,
|
|
55
|
+
scripts,
|
|
56
|
+
debug,
|
|
57
|
+
} ) {
|
|
49
58
|
spinner.text = 'Reading configuration.';
|
|
50
59
|
await checkForLegacyInstall( spinner );
|
|
51
60
|
|
|
@@ -152,12 +161,20 @@ module.exports = async function start( { spinner, debug, update, xdebug } ) {
|
|
|
152
161
|
|
|
153
162
|
spinner.text = 'Starting WordPress.';
|
|
154
163
|
|
|
155
|
-
await dockerCompose.upMany(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
:
|
|
160
|
-
|
|
164
|
+
await dockerCompose.upMany(
|
|
165
|
+
[ 'wordpress', 'tests-wordpress', 'cli', 'tests-cli' ],
|
|
166
|
+
{
|
|
167
|
+
...dockerComposeConfig,
|
|
168
|
+
commandOptions: shouldConfigureWp
|
|
169
|
+
? [ '--build', '--force-recreate' ]
|
|
170
|
+
: [],
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// Make sure we've consumed the custom CLI dockerfile.
|
|
175
|
+
if ( shouldConfigureWp ) {
|
|
176
|
+
await dockerCompose.buildOne( [ 'cli' ], { ...dockerComposeConfig } );
|
|
177
|
+
}
|
|
161
178
|
|
|
162
179
|
// Only run WordPress install/configuration when config has changed.
|
|
163
180
|
if ( shouldConfigureWp ) {
|
|
@@ -186,6 +203,11 @@ module.exports = async function start( { spinner, debug, update, xdebug } ) {
|
|
|
186
203
|
} ),
|
|
187
204
|
] );
|
|
188
205
|
|
|
206
|
+
// Execute any configured command that should run after the environment has finished being set up.
|
|
207
|
+
if ( scripts ) {
|
|
208
|
+
executeAfterSetup( config, spinner );
|
|
209
|
+
}
|
|
210
|
+
|
|
189
211
|
// Set the cache key once everything has been configured.
|
|
190
212
|
await setCache( CONFIG_CACHE_KEY, configHash, {
|
|
191
213
|
workDirectoryPath,
|
package/lib/commands/stop.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use strict';
|
|
1
2
|
/**
|
|
2
3
|
* Internal dependencies
|
|
3
4
|
*/
|
|
@@ -6,9 +7,10 @@ const { ValidationError } = require( './validate-config' );
|
|
|
6
7
|
/**
|
|
7
8
|
* Adds or replaces the port to the given domain or URI.
|
|
8
9
|
*
|
|
9
|
-
* @param {string}
|
|
10
|
-
* @param {string}
|
|
11
|
-
* @param {boolean}
|
|
10
|
+
* @param {string} input The domain or URI to operate on.
|
|
11
|
+
* @param {number|string} port The port to append.
|
|
12
|
+
* @param {boolean} [replace] Indicates whether or not the port should be replaced if one is already present. Defaults to true.
|
|
13
|
+
*
|
|
12
14
|
* @return {string} The string with the port added or replaced.
|
|
13
15
|
*/
|
|
14
16
|
module.exports = function addOrReplacePort( input, port, replace = true ) {
|
|
@@ -27,6 +29,13 @@ module.exports = function addOrReplacePort( input, port, replace = true ) {
|
|
|
27
29
|
return input;
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
// There's never a reason to add the default ports.
|
|
33
|
+
// We use == to catch both string and number ports.
|
|
34
|
+
// eslint-disable-next-line eqeqeq
|
|
35
|
+
if ( port == 80 || port == 443 ) {
|
|
36
|
+
return input;
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
// Place the port in the correct location in the input.
|
|
31
40
|
return matches[ 1 ] + ':' + port + matches[ 3 ];
|
|
32
41
|
};
|
package/lib/config/db-env.js
CHANGED
|
@@ -18,7 +18,7 @@ const finished = util.promisify( stream.finished );
|
|
|
18
18
|
* Detects whether the given directory is a WordPress installation, a plugin or a theme.
|
|
19
19
|
*
|
|
20
20
|
* @param {string} directoryPath The directory to detect.
|
|
21
|
-
* @return {string|null} 'core' if the directory is a WordPress installation, 'plugin' if it is a plugin, 'theme' if it is a theme, or null if we can't tell.
|
|
21
|
+
* @return {Promise<string|null>} 'core' if the directory is a WordPress installation, 'plugin' if it is a plugin, 'theme' if it is a theme, or null if we can't tell.
|
|
22
22
|
*/
|
|
23
23
|
module.exports = async function detectDirectoryType( directoryPath ) {
|
|
24
24
|
// If we have a `wp-includes/version.php` file, then this is a Core install.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* External dependencies
|
|
4
|
+
*/
|
|
5
|
+
const path = require( 'path' );
|
|
6
|
+
const fs = require( 'fs' ).promises;
|
|
7
|
+
const os = require( 'os' );
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Gets the directory in which generated files are created.
|
|
11
|
+
*
|
|
12
|
+
* By default: '~/.wp-env/'. On Linux with snap packages: '~/wp-env/'. Can be
|
|
13
|
+
* overridden with the WP_ENV_HOME environment variable.
|
|
14
|
+
*
|
|
15
|
+
* @return {Promise<string>} The absolute path to the `wp-env` home directory.
|
|
16
|
+
*/
|
|
17
|
+
module.exports = async function getCacheDirectory() {
|
|
18
|
+
// Allow user to override download location.
|
|
19
|
+
if ( process.env.WP_ENV_HOME ) {
|
|
20
|
+
return path.resolve( process.env.WP_ENV_HOME );
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Installing docker with Snap Packages on Linux is common, but does not
|
|
25
|
+
* support hidden directories. Therefore we use a public directory when
|
|
26
|
+
* snap packages exist.
|
|
27
|
+
*
|
|
28
|
+
* @see https://github.com/WordPress/gutenberg/issues/20180#issuecomment-587046325
|
|
29
|
+
*/
|
|
30
|
+
let usesSnap;
|
|
31
|
+
try {
|
|
32
|
+
await fs.stat( '/snap' );
|
|
33
|
+
usesSnap = true;
|
|
34
|
+
} catch {
|
|
35
|
+
usesSnap = false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return path.resolve( os.homedir(), usesSnap ? 'wp-env' : '.wp-env' );
|
|
39
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Internal dependencies
|
|
4
|
+
*/
|
|
5
|
+
const {
|
|
6
|
+
parseSourceString,
|
|
7
|
+
includeTestsPath,
|
|
8
|
+
} = require( './parse-source-string' );
|
|
9
|
+
const { checkPort, checkVersion, checkString } = require( './validate-config' );
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {import('./parse-source-string').WPSource} WPSource
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Environment variable configuration.
|
|
17
|
+
*
|
|
18
|
+
* @typedef WPEnvironmentVariableConfig
|
|
19
|
+
* @property {?number} port An override for the development environment's port.
|
|
20
|
+
* @property {?number} testsPort An override for the testing environment's port.
|
|
21
|
+
* @property {?WPSource} coreSource An override for all environment's coreSource.
|
|
22
|
+
* @property {?string} phpVersion An override for all environment's PHP version.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Gets configuration options from environment variables.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} cacheDirectoryPath Path to the work directory located in ~/.wp-env.
|
|
29
|
+
*
|
|
30
|
+
* @return {WPEnvironmentVariableConfig} Any configuration options parsed from the environment variables.
|
|
31
|
+
*/
|
|
32
|
+
module.exports = function getConfigFromEnvironmentVars( cacheDirectoryPath ) {
|
|
33
|
+
const environmentConfig = {
|
|
34
|
+
port: getPortFromEnvironmentVariable( 'WP_ENV_PORT' ),
|
|
35
|
+
testsPort: getPortFromEnvironmentVariable( 'WP_ENV_TESTS_PORT' ),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if ( process.env.WP_ENV_CORE ) {
|
|
39
|
+
environmentConfig.coreSource = includeTestsPath(
|
|
40
|
+
parseSourceString( process.env.WP_ENV_CORE, {
|
|
41
|
+
cacheDirectoryPath,
|
|
42
|
+
} ),
|
|
43
|
+
{ cacheDirectoryPath }
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if ( process.env.WP_ENV_PHP_VERSION ) {
|
|
48
|
+
checkVersion(
|
|
49
|
+
'environment variable',
|
|
50
|
+
'WP_ENV_PHP_VERSION',
|
|
51
|
+
process.env.WP_ENV_PHP_VERSION
|
|
52
|
+
);
|
|
53
|
+
environmentConfig.phpVersion = process.env.WP_ENV_PHP_VERSION;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if ( process.env.WP_ENV_AFTER_SETUP ) {
|
|
57
|
+
checkString(
|
|
58
|
+
'environment variable',
|
|
59
|
+
'WP_ENV_AFTER_SETUP',
|
|
60
|
+
process.env.WP_ENV_AFTER_SETUP
|
|
61
|
+
);
|
|
62
|
+
environmentConfig.afterSetup = process.env.WP_ENV_AFTER_SETUP;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return environmentConfig;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Parses an environment variable which should be a port.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} varName The environment variable to check (e.g. WP_ENV_PORT).
|
|
72
|
+
*
|
|
73
|
+
* @return {number} The parsed port number
|
|
74
|
+
*/
|
|
75
|
+
function getPortFromEnvironmentVariable( varName ) {
|
|
76
|
+
if ( ! process.env[ varName ] ) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const port = parseInt( process.env[ varName ] );
|
|
81
|
+
|
|
82
|
+
// Throw an error if it is not parseable as a number.
|
|
83
|
+
checkPort( 'environment variable', varName, port );
|
|
84
|
+
|
|
85
|
+
return port;
|
|
86
|
+
}
|
package/lib/config/index.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
1
2
|
/**
|
|
2
3
|
* Internal dependencies
|
|
3
4
|
*/
|
|
4
|
-
const
|
|
5
|
+
const loadConfig = require( './load-config' );
|
|
5
6
|
const { ValidationError } = require( './validate-config' );
|
|
6
7
|
const dbEnv = require( './db-env' );
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
* @typedef {import('./config').WPConfig} WPConfig
|
|
10
|
-
* @typedef {import('./config').
|
|
11
|
-
* @typedef {import('./config').
|
|
10
|
+
* @typedef {import('./load-config').WPConfig} WPConfig
|
|
11
|
+
* @typedef {import('./parse-config').WPRootConfig} WPRootConfig
|
|
12
|
+
* @typedef {import('./parse-config').WPEnvironmentConfig} WPEnvironmentConfig
|
|
13
|
+
* @typedef {import('./parse-source-string').WPSource} WPSource
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
16
|
module.exports = {
|
|
15
17
|
ValidationError,
|
|
16
|
-
|
|
18
|
+
loadConfig,
|
|
17
19
|
dbEnv,
|
|
18
20
|
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* External dependencies
|
|
4
|
+
*/
|
|
5
|
+
const path = require( 'path' );
|
|
6
|
+
const fs = require( 'fs' ).promises;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Internal dependencies
|
|
10
|
+
*/
|
|
11
|
+
const getCacheDirectory = require( './get-cache-directory' );
|
|
12
|
+
const md5 = require( '../md5' );
|
|
13
|
+
const { parseConfig, getConfigFilePath } = require( './parse-config' );
|
|
14
|
+
const postProcessConfig = require( './post-process-config' );
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {import('./parse-config').WPRootConfig} WPRootConfig
|
|
18
|
+
* @typedef {import('./parse-config').WPEnvironmentConfig} WPEnvironmentConfig
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* wp-env configuration.
|
|
23
|
+
*
|
|
24
|
+
* @typedef WPConfig
|
|
25
|
+
* @property {string} name Name of the environment.
|
|
26
|
+
* @property {string} configDirectoryPath Path to the .wp-env.json file.
|
|
27
|
+
* @property {string} workDirectoryPath Path to the work directory located in ~/.wp-env.
|
|
28
|
+
* @property {string} dockerComposeConfigPath Path to the docker-compose.yml file.
|
|
29
|
+
* @property {boolean} detectedLocalConfig If true, wp-env detected local config and used it.
|
|
30
|
+
* @property {string} afterSetup The command(s) to run after configuring WordPress on start and clean.
|
|
31
|
+
* @property {Object.<string, WPEnvironmentConfig>} env Specific config for different environments.
|
|
32
|
+
* @property {boolean} debug True if debug mode is enabled.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Loads any configuration from a given directory.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} configDirectoryPath The directory we want to load the config from.
|
|
39
|
+
*
|
|
40
|
+
* @return {WPConfig} The config object we've loaded.
|
|
41
|
+
*/
|
|
42
|
+
module.exports = async function loadConfig( configDirectoryPath ) {
|
|
43
|
+
const configFilePath = getConfigFilePath( configDirectoryPath );
|
|
44
|
+
|
|
45
|
+
const cacheDirectoryPath = path.resolve(
|
|
46
|
+
await getCacheDirectory(),
|
|
47
|
+
md5( configFilePath )
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Parse any configuration we found in the given directory.
|
|
51
|
+
// This comes merged and prepared for internal consumption.
|
|
52
|
+
let config = await parseConfig( configDirectoryPath, cacheDirectoryPath );
|
|
53
|
+
|
|
54
|
+
// Make sure to perform any additional post-processing that
|
|
55
|
+
// may be needed before the config object is ready for
|
|
56
|
+
// consumption elsewhere in the tool.
|
|
57
|
+
config = postProcessConfig( config );
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
name: path.basename( configDirectoryPath ),
|
|
61
|
+
dockerComposeConfigPath: path.resolve(
|
|
62
|
+
cacheDirectoryPath,
|
|
63
|
+
'docker-compose.yml'
|
|
64
|
+
),
|
|
65
|
+
configDirectoryPath,
|
|
66
|
+
workDirectoryPath: cacheDirectoryPath,
|
|
67
|
+
detectedLocalConfig: await hasLocalConfig( [
|
|
68
|
+
configFilePath,
|
|
69
|
+
getConfigFilePath( configDirectoryPath, 'override' ),
|
|
70
|
+
] ),
|
|
71
|
+
afterSetup: config.afterSetup,
|
|
72
|
+
env: config.env,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Checks to see whether or not there is any configuration present in the directory.
|
|
78
|
+
*
|
|
79
|
+
* @param {string[]} configFilePaths The config files we want to check for existence.
|
|
80
|
+
*
|
|
81
|
+
* @return {Promise<boolean>} A promise indicating whether or not a local config is present.
|
|
82
|
+
*/
|
|
83
|
+
async function hasLocalConfig( configFilePaths ) {
|
|
84
|
+
for ( const filePath of configFilePaths ) {
|
|
85
|
+
try {
|
|
86
|
+
await fs.stat( filePath );
|
|
87
|
+
return true;
|
|
88
|
+
} catch {}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import('./parse-config').WPEnvironmentConfig} WPEnvironmentConfig
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Deep-merges the values in the given service environment. This allows us to
|
|
8
|
+
* merge the wp-config.php values instead of overwriting them.
|
|
9
|
+
*
|
|
10
|
+
* @param {WPEnvironmentConfig} defaultConfig The default config options.
|
|
11
|
+
* @param {...string} configs The config options to merge.
|
|
12
|
+
*
|
|
13
|
+
* @return {WPEnvironmentConfig} The merged config object.
|
|
14
|
+
*/
|
|
15
|
+
module.exports = function mergeConfigs( defaultConfig, ...configs ) {
|
|
16
|
+
// Start with our default config object. This has all
|
|
17
|
+
// of the options filled out already and we can use
|
|
18
|
+
// that to make performing the merge easier.
|
|
19
|
+
let config = defaultConfig;
|
|
20
|
+
|
|
21
|
+
// Merge the configs
|
|
22
|
+
for ( const merge of configs ) {
|
|
23
|
+
config = mergeConfig( config, merge );
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return config;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Merges a config object into another.
|
|
31
|
+
*
|
|
32
|
+
* @param {WPEnvironmentConfig} config The config object to be merged into.
|
|
33
|
+
* @param {WPEnvironmentConfig} toMerge The config object to merge.
|
|
34
|
+
*
|
|
35
|
+
* @return {WPEnvironmentConfig} The merged config object.
|
|
36
|
+
*/
|
|
37
|
+
function mergeConfig( config, toMerge ) {
|
|
38
|
+
// Begin by updating any of the config options that the object already has.
|
|
39
|
+
for ( const option in config ) {
|
|
40
|
+
// We don't need to do anything if the merge source doesn't have a property to give.
|
|
41
|
+
if ( toMerge[ option ] === undefined ) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
switch ( option ) {
|
|
46
|
+
// Some config options are merged together instead of entirely replaced.
|
|
47
|
+
case 'config':
|
|
48
|
+
case 'mappings': {
|
|
49
|
+
config[ option ] = Object.assign(
|
|
50
|
+
config[ option ],
|
|
51
|
+
toMerge[ option ]
|
|
52
|
+
);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Environment-specific config options are recursively merged.
|
|
57
|
+
case 'env': {
|
|
58
|
+
for ( const environment in config.env ) {
|
|
59
|
+
// Once again, we don't need to do anything if the merge source has nothing to give.
|
|
60
|
+
if ( toMerge.env[ environment ] === undefined ) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
config.env[ environment ] = mergeConfig(
|
|
65
|
+
config.env[ environment ],
|
|
66
|
+
toMerge.env[ environment ]
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
default: {
|
|
73
|
+
config[ option ] = toMerge[ option ];
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Next, add any new options that the config object doesn't already have.
|
|
80
|
+
for ( const option in toMerge ) {
|
|
81
|
+
// Environment-specific config options should be checked individually.
|
|
82
|
+
if ( option === 'env' ) {
|
|
83
|
+
for ( const environment in toMerge.env ) {
|
|
84
|
+
// The presence of the environment means it would have been merged above.
|
|
85
|
+
if ( config.env[ environment ] !== undefined ) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
config.env[ environment ] = toMerge[ environment ];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// As above, the presence of the option means it would have been merged above.
|
|
96
|
+
if ( config[ option ] !== undefined ) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
config[ option ] = toMerge[ option ];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return config;
|
|
104
|
+
}
|