@wordpress/env 10.38.1-next.v.0 → 11.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 +158 -75
- package/lib/cli.js +85 -24
- package/lib/commands/clean.js +16 -38
- package/lib/commands/cleanup.js +79 -0
- package/lib/commands/destroy.js +33 -41
- package/lib/commands/index.js +6 -2
- package/lib/commands/logs.js +20 -62
- package/lib/commands/reset.js +46 -0
- package/lib/commands/run.js +16 -117
- package/lib/commands/start.js +78 -243
- package/lib/commands/status.js +160 -0
- package/lib/commands/stop.js +17 -19
- package/lib/config/get-config-from-environment-vars.js +2 -5
- package/lib/config/load-config.js +35 -5
- package/lib/config/parse-config.js +53 -21
- package/lib/config/post-process-config.js +38 -16
- package/lib/config/test/__snapshots__/config-integration.js.snap +16 -0
- package/lib/config/test/parse-config.js +33 -0
- package/lib/config/test/post-process-config.js +52 -0
- package/lib/download-sources.js +9 -53
- package/lib/{build-docker-compose-config.js → runtime/docker/build-docker-compose-config.js} +167 -128
- package/lib/runtime/docker/download-sources.js +63 -0
- package/lib/{download-wp-phpunit.js → runtime/docker/download-wp-phpunit.js} +4 -1
- package/lib/runtime/docker/index.js +863 -0
- package/lib/{init-config.js → runtime/docker/init-config.js} +22 -14
- package/lib/runtime/docker/wordpress.js +309 -0
- package/lib/runtime/errors.js +28 -0
- package/lib/runtime/index.js +91 -0
- package/lib/runtime/playground/blueprint-builder.js +158 -0
- package/lib/runtime/playground/index.js +530 -0
- package/lib/test/build-docker-compose-config.js +150 -3
- package/lib/test/cli.js +49 -13
- package/lib/wordpress.js +1 -297
- package/package.json +6 -3
- package/lib/commands/install-path.js +0 -21
- /package/lib/{get-host-user.js → runtime/docker/get-host-user.js} +0 -0
- /package/lib/{validate-run-container.js → runtime/docker/validate-run-container.js} +0 -0
|
@@ -10,11 +10,11 @@ const yaml = require( 'js-yaml' );
|
|
|
10
10
|
/**
|
|
11
11
|
* Internal dependencies
|
|
12
12
|
*/
|
|
13
|
-
const { loadConfig, ValidationError } = require( '
|
|
13
|
+
const { loadConfig, ValidationError } = require( '../../config' );
|
|
14
14
|
const buildDockerComposeConfig = require( './build-docker-compose-config' );
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* @typedef {import('
|
|
17
|
+
* @typedef {import('../../config').WPConfig} WPConfig
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -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
|
|
@@ -87,10 +89,16 @@ module.exports = async function initConfig( {
|
|
|
87
89
|
yaml.dump( dockerComposeConfig )
|
|
88
90
|
);
|
|
89
91
|
|
|
90
|
-
// Write
|
|
92
|
+
// Write Dockerfiles for each service we provided.
|
|
91
93
|
// (WordPress and CLI services, then a development and test environment for each.)
|
|
92
94
|
for ( const imageType of [ 'WordPress', 'CLI' ] ) {
|
|
93
95
|
for ( const envType of [ 'development', 'tests' ] ) {
|
|
96
|
+
if (
|
|
97
|
+
envType === 'tests' &&
|
|
98
|
+
config.testsEnvironment === false
|
|
99
|
+
) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
94
102
|
await writeFile(
|
|
95
103
|
path.resolve(
|
|
96
104
|
config.workDirectoryPath,
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* External dependencies
|
|
4
|
+
*/
|
|
5
|
+
const util = require( 'util' );
|
|
6
|
+
const { v2: dockerCompose } = require( 'docker-compose' );
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Promisified dependencies
|
|
10
|
+
*/
|
|
11
|
+
const copyDir = util.promisify( require( 'copy-dir' ) );
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Internal dependencies
|
|
15
|
+
*/
|
|
16
|
+
const { readWordPressVersion } = require( '../../wordpress' );
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {import('../../config').WPConfig} WPConfig
|
|
20
|
+
* @typedef {import('../../config').WPEnvironmentConfig} WPEnvironmentConfig
|
|
21
|
+
* @typedef {import('../../config').WPSource} WPSource
|
|
22
|
+
* @typedef {'development'|'tests'} WPEnvironment
|
|
23
|
+
* @typedef {'development'|'tests'|'all'} WPEnvironmentSelection
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Utility function to check if a WordPress version is lower than another version.
|
|
28
|
+
*
|
|
29
|
+
* This is a non-comprehensive check only intended for this usage, to avoid pulling in a full semver library.
|
|
30
|
+
* It only considers the major and minor portions of the version and ignores the rest. Additionally, it assumes that
|
|
31
|
+
* the minor version is always a single digit (i.e. 0-9).
|
|
32
|
+
*
|
|
33
|
+
* Do not use this function for general version comparison, as it will not work for all cases.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} version The version to check.
|
|
36
|
+
* @param {string} compareVersion The compare version to check whether the version is lower than.
|
|
37
|
+
* @return {boolean} True if the version is lower than the compare version, false otherwise.
|
|
38
|
+
*/
|
|
39
|
+
function isWPMajorMinorVersionLower( version, compareVersion ) {
|
|
40
|
+
const versionNumber = Number.parseFloat(
|
|
41
|
+
version.match( /^[0-9]+(\.[0-9]+)?/ )[ 0 ]
|
|
42
|
+
);
|
|
43
|
+
const compareVersionNumber = Number.parseFloat(
|
|
44
|
+
compareVersion.match( /^[0-9]+(\.[0-9]+)?/ )[ 0 ]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return versionNumber < compareVersionNumber;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Configures WordPress for the given environment by installing WordPress,
|
|
52
|
+
* activating all plugins, and activating the first theme. These steps are
|
|
53
|
+
* performed sequentially so as to not overload the WordPress instance.
|
|
54
|
+
*
|
|
55
|
+
* @param {WPEnvironment} environment The environment to configure. Either 'development' or 'tests'.
|
|
56
|
+
* @param {WPConfig} config The wp-env config object.
|
|
57
|
+
* @param {Object} spinner A CLI spinner which indicates progress.
|
|
58
|
+
*/
|
|
59
|
+
async function configureWordPress( environment, config, spinner ) {
|
|
60
|
+
let wpVersion = '';
|
|
61
|
+
try {
|
|
62
|
+
wpVersion = await readWordPressVersion(
|
|
63
|
+
config.env[ environment ].coreSource,
|
|
64
|
+
spinner,
|
|
65
|
+
config.debug
|
|
66
|
+
);
|
|
67
|
+
} catch ( err ) {
|
|
68
|
+
// Ignore error.
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Create a project-specific wp-cli configuration, important for the `rewrite` command.
|
|
72
|
+
// Don't overwrite existing configuration.
|
|
73
|
+
const cliConfigCommand = `[ -f /var/www/html/wp-cli.yml ] || (
|
|
74
|
+
exec > /var/www/html/wp-cli.yml
|
|
75
|
+
echo "apache_modules:"
|
|
76
|
+
echo " - mod_rewrite"
|
|
77
|
+
)`;
|
|
78
|
+
|
|
79
|
+
const isMultisite = config.env[ environment ].multisite;
|
|
80
|
+
|
|
81
|
+
const installMethod = isMultisite ? 'multisite-install' : 'install';
|
|
82
|
+
const installCommand = `wp core ${ installMethod } --url="${ config.env[ environment ].config.WP_SITEURL }" --title="${ config.name }" --admin_user=admin --admin_password=password --admin_email=wordpress@example.com --skip-email`;
|
|
83
|
+
|
|
84
|
+
// -eo pipefail exits the command as soon as anything fails in bash.
|
|
85
|
+
const setupCommands = [
|
|
86
|
+
'set -eo pipefail',
|
|
87
|
+
cliConfigCommand,
|
|
88
|
+
installCommand,
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
// Bootstrap .htaccess for multisite
|
|
92
|
+
if ( isMultisite ) {
|
|
93
|
+
// Using a subshell with `exec` was the best tradeoff I could come up
|
|
94
|
+
// with between readability of this source and compatibility with the
|
|
95
|
+
// way that all strings in `setupCommands` are later joined with '&&'.
|
|
96
|
+
setupCommands.push(
|
|
97
|
+
`(
|
|
98
|
+
exec > /var/www/html/.htaccess
|
|
99
|
+
echo 'RewriteEngine On'
|
|
100
|
+
echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'
|
|
101
|
+
echo 'RewriteBase /'
|
|
102
|
+
echo 'RewriteRule ^index\\.php$ - [L]'
|
|
103
|
+
echo ''
|
|
104
|
+
echo '# add a trailing slash to /wp-admin'
|
|
105
|
+
echo 'RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]'
|
|
106
|
+
echo ''
|
|
107
|
+
echo 'RewriteCond %{REQUEST_FILENAME} -f [OR]'
|
|
108
|
+
echo 'RewriteCond %{REQUEST_FILENAME} -d'
|
|
109
|
+
echo 'RewriteRule ^ - [L]'
|
|
110
|
+
echo 'RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]'
|
|
111
|
+
echo 'RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\\.php)$ $2 [L]'
|
|
112
|
+
echo 'RewriteRule . index.php [L]'
|
|
113
|
+
)`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// WordPress versions below 5.1 didn't use proper spacing in wp-config.
|
|
118
|
+
const configAnchor =
|
|
119
|
+
wpVersion && isWPMajorMinorVersionLower( wpVersion, '5.1' )
|
|
120
|
+
? `"define('WP_DEBUG',"`
|
|
121
|
+
: `"define( 'WP_DEBUG',"`;
|
|
122
|
+
|
|
123
|
+
// Set wp-config.php values.
|
|
124
|
+
for ( let [ key, value ] of Object.entries(
|
|
125
|
+
config.env[ environment ].config
|
|
126
|
+
) ) {
|
|
127
|
+
// Allow the configuration to skip a default constant by specifying it as null.
|
|
128
|
+
if ( null === value ) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Add quotes around string values to work with multi-word strings better.
|
|
133
|
+
value = typeof value === 'string' ? `"${ value }"` : value;
|
|
134
|
+
setupCommands.push(
|
|
135
|
+
`wp config set ${ key } ${ value } --anchor=${ configAnchor }${
|
|
136
|
+
typeof value !== 'string' ? ' --raw' : ''
|
|
137
|
+
}`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Activate all plugins.
|
|
142
|
+
for ( const pluginSource of config.env[ environment ].pluginSources ) {
|
|
143
|
+
setupCommands.push( `wp plugin activate ${ pluginSource.basename }` );
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if ( config.debug ) {
|
|
147
|
+
spinner.info(
|
|
148
|
+
`Running the following setup commands on the ${ environment } instance:\n - ${ setupCommands.join(
|
|
149
|
+
'\n - '
|
|
150
|
+
) }\n`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Execute all setup commands in a batch.
|
|
155
|
+
await dockerCompose.run(
|
|
156
|
+
environment === 'development' ? 'cli' : 'tests-cli',
|
|
157
|
+
[ 'bash', '-c', setupCommands.join( ' && ' ) ],
|
|
158
|
+
{
|
|
159
|
+
config: config.dockerComposeConfigPath,
|
|
160
|
+
commandOptions: [ '--rm' ],
|
|
161
|
+
log: config.debug,
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// WordPress versions below 5.1 didn't use proper spacing in wp-config.
|
|
166
|
+
// Additionally, WordPress versions below 5.4 used `dirname( __FILE__ )` instead of `__DIR__`.
|
|
167
|
+
let abspathDef = `define( 'ABSPATH', __DIR__ . '\\/' );`;
|
|
168
|
+
if ( wpVersion && isWPMajorMinorVersionLower( wpVersion, '5.1' ) ) {
|
|
169
|
+
abspathDef = `define('ABSPATH', dirname(__FILE__) . '\\/');`;
|
|
170
|
+
} else if ( wpVersion && isWPMajorMinorVersionLower( wpVersion, '5.4' ) ) {
|
|
171
|
+
abspathDef = `define( 'ABSPATH', dirname( __FILE__ ) . '\\/' );`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// WordPress' PHPUnit suite expects a `wp-tests-config.php` in
|
|
175
|
+
// the directory that the test suite is contained within.
|
|
176
|
+
// Make sure ABSPATH points to the WordPress install.
|
|
177
|
+
await dockerCompose.exec(
|
|
178
|
+
environment === 'development' ? 'wordpress' : 'tests-wordpress',
|
|
179
|
+
[
|
|
180
|
+
'sh',
|
|
181
|
+
'-c',
|
|
182
|
+
`sed -e "/^require.*wp-settings.php/d" -e "s/${ abspathDef }/define( 'ABSPATH', '\\/var\\/www\\/html\\/' );\\n\\tdefine( 'WP_DEFAULT_THEME', 'default' );/" /var/www/html/wp-config.php > /wordpress-phpunit/wp-tests-config.php`,
|
|
183
|
+
],
|
|
184
|
+
{
|
|
185
|
+
config: config.dockerComposeConfigPath,
|
|
186
|
+
log: config.debug,
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Resets the development server's database, the tests server's database, or both.
|
|
193
|
+
*
|
|
194
|
+
* @param {WPEnvironmentSelection} environment The environment to clean. Either 'development', 'tests', or 'all'.
|
|
195
|
+
* @param {WPConfig} config The wp-env config object.
|
|
196
|
+
*/
|
|
197
|
+
async function resetDatabase(
|
|
198
|
+
environment,
|
|
199
|
+
{ dockerComposeConfigPath, debug }
|
|
200
|
+
) {
|
|
201
|
+
const options = {
|
|
202
|
+
config: dockerComposeConfigPath,
|
|
203
|
+
commandOptions: [ '--rm' ],
|
|
204
|
+
log: debug,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const tasks = [];
|
|
208
|
+
|
|
209
|
+
if ( environment === 'all' || environment === 'development' ) {
|
|
210
|
+
tasks.push( dockerCompose.run( 'cli', 'wp db reset --yes', options ) );
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if ( environment === 'all' || environment === 'tests' ) {
|
|
214
|
+
tasks.push(
|
|
215
|
+
dockerCompose.run( 'tests-cli', 'wp db reset --yes', options )
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
await Promise.all( tasks );
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Sets up WordPress directories, copying core files if needed.
|
|
224
|
+
*
|
|
225
|
+
* @param {WPConfig} config The wp-env config object.
|
|
226
|
+
*/
|
|
227
|
+
async function setupWordPressDirectories( config ) {
|
|
228
|
+
if (
|
|
229
|
+
config.env.development.coreSource &&
|
|
230
|
+
hasSameCoreSource( [ config.env.development, config.env.tests ] )
|
|
231
|
+
) {
|
|
232
|
+
await copyCoreFiles(
|
|
233
|
+
config.env.development.coreSource.path,
|
|
234
|
+
config.env.development.coreSource.testsPath
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Returns true if all given environment configs have the same core source.
|
|
241
|
+
*
|
|
242
|
+
* @param {WPEnvironmentConfig[]} envs An array of environments to check.
|
|
243
|
+
*
|
|
244
|
+
* @return {boolean} True if all the environments have the same core source.
|
|
245
|
+
*/
|
|
246
|
+
function hasSameCoreSource( envs ) {
|
|
247
|
+
if ( envs.length < 2 ) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
return ! envs.some( ( env ) =>
|
|
251
|
+
areCoreSourcesDifferent( envs[ 0 ].coreSource, env.coreSource )
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Checks if two core sources are different.
|
|
257
|
+
*
|
|
258
|
+
* @param {WPSource} coreSource1 First core source.
|
|
259
|
+
* @param {WPSource} coreSource2 Second core source.
|
|
260
|
+
* @return {boolean} True if the sources are different.
|
|
261
|
+
*/
|
|
262
|
+
function areCoreSourcesDifferent( coreSource1, coreSource2 ) {
|
|
263
|
+
if (
|
|
264
|
+
( ! coreSource1 && coreSource2 ) ||
|
|
265
|
+
( coreSource1 && ! coreSource2 )
|
|
266
|
+
) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if ( coreSource1 && coreSource2 && coreSource1.path !== coreSource2.path ) {
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Copies a WordPress installation, taking care to ignore large directories
|
|
279
|
+
* (.git, node_modules) and configuration files (wp-config.php).
|
|
280
|
+
*
|
|
281
|
+
* @param {string} fromPath Path to the WordPress directory to copy.
|
|
282
|
+
* @param {string} toPath Destination path.
|
|
283
|
+
*/
|
|
284
|
+
async function copyCoreFiles( fromPath, toPath ) {
|
|
285
|
+
await copyDir( fromPath, toPath, {
|
|
286
|
+
filter( stat, filepath, filename ) {
|
|
287
|
+
if ( stat === 'symbolicLink' ) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
if ( stat === 'directory' && filename === '.git' ) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
if ( stat === 'directory' && filename === 'node_modules' ) {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
if ( stat === 'file' && filename === 'wp-config.php' ) {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
return true;
|
|
300
|
+
},
|
|
301
|
+
} );
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
module.exports = {
|
|
305
|
+
configureWordPress,
|
|
306
|
+
resetDatabase,
|
|
307
|
+
setupWordPressDirectories,
|
|
308
|
+
hasSameCoreSource,
|
|
309
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error thrown when a command is not supported by the current runtime.
|
|
5
|
+
*/
|
|
6
|
+
class UnsupportedCommandError extends Error {
|
|
7
|
+
constructor( command ) {
|
|
8
|
+
super(
|
|
9
|
+
`The '${ command }' command is not supported in the Playground runtime at the moment.`
|
|
10
|
+
);
|
|
11
|
+
this.name = 'UnsupportedCommandError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
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
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
UnsupportedCommandError,
|
|
27
|
+
EnvironmentNotInitializedError,
|
|
28
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Internal dependencies
|
|
5
|
+
*/
|
|
6
|
+
const DockerRuntime = require( './docker' );
|
|
7
|
+
const PlaygroundRuntime = require( './playground' );
|
|
8
|
+
const {
|
|
9
|
+
UnsupportedCommandError,
|
|
10
|
+
EnvironmentNotInitializedError,
|
|
11
|
+
} = require( './errors' );
|
|
12
|
+
const { setCache, getCache } = require( '../cache' );
|
|
13
|
+
|
|
14
|
+
const RUNTIME_CACHE_KEY = 'runtime';
|
|
15
|
+
|
|
16
|
+
const runtimes = {
|
|
17
|
+
docker: DockerRuntime,
|
|
18
|
+
playground: PlaygroundRuntime,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get a runtime instance by name.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} name Runtime name ('docker' or 'playground').
|
|
25
|
+
* @return {Object} Runtime instance.
|
|
26
|
+
*/
|
|
27
|
+
function getRuntime( name ) {
|
|
28
|
+
const RuntimeClass = runtimes[ name ];
|
|
29
|
+
if ( ! RuntimeClass ) {
|
|
30
|
+
throw new Error( `Unknown runtime: ${ name }` );
|
|
31
|
+
}
|
|
32
|
+
return new RuntimeClass();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get all available runtime names.
|
|
37
|
+
*
|
|
38
|
+
* @return {string[]} Array of runtime names.
|
|
39
|
+
*/
|
|
40
|
+
function getAvailableRuntimes() {
|
|
41
|
+
return Object.keys( runtimes );
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
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.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} workDirectoryPath Path to the wp-env work directory.
|
|
70
|
+
* @return {Promise<string>} Runtime name ('docker' or 'playground').
|
|
71
|
+
* @throws {EnvironmentNotInitializedError} If environment not initialized.
|
|
72
|
+
*/
|
|
73
|
+
async function detectRuntime( workDirectoryPath ) {
|
|
74
|
+
const savedRuntime = await getSavedRuntime( workDirectoryPath );
|
|
75
|
+
if ( ! savedRuntime ) {
|
|
76
|
+
throw new EnvironmentNotInitializedError();
|
|
77
|
+
}
|
|
78
|
+
return savedRuntime;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
getRuntime,
|
|
83
|
+
getAvailableRuntimes,
|
|
84
|
+
detectRuntime,
|
|
85
|
+
saveRuntime,
|
|
86
|
+
getSavedRuntime,
|
|
87
|
+
DockerRuntime,
|
|
88
|
+
PlaygroundRuntime,
|
|
89
|
+
UnsupportedCommandError,
|
|
90
|
+
EnvironmentNotInitializedError,
|
|
91
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builds a Playground Blueprint from wp-env configuration.
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} config The wp-env config object.
|
|
7
|
+
* @return {Object} Playground Blueprint JSON object.
|
|
8
|
+
*/
|
|
9
|
+
function buildBlueprint( config ) {
|
|
10
|
+
const envConfig = config.env.development;
|
|
11
|
+
|
|
12
|
+
const blueprint = {
|
|
13
|
+
$schema: 'https://playground.wordpress.net/blueprint-schema.json',
|
|
14
|
+
landingPage: '/wp-admin/',
|
|
15
|
+
preferredVersions: {
|
|
16
|
+
php: envConfig.phpVersion || '8.2',
|
|
17
|
+
wp: 'latest',
|
|
18
|
+
},
|
|
19
|
+
steps: [],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Login step - matches wp-env default credentials
|
|
23
|
+
blueprint.steps.push( {
|
|
24
|
+
step: 'login',
|
|
25
|
+
username: 'admin',
|
|
26
|
+
password: 'password',
|
|
27
|
+
} );
|
|
28
|
+
|
|
29
|
+
// Add plugins
|
|
30
|
+
for ( const plugin of envConfig.pluginSources || [] ) {
|
|
31
|
+
if ( plugin.type === 'local' || plugin.type === 'git' ) {
|
|
32
|
+
// Local and git plugins are mounted via CLI args, just activate
|
|
33
|
+
blueprint.steps.push( {
|
|
34
|
+
step: 'activatePlugin',
|
|
35
|
+
pluginPath: `/wordpress/wp-content/plugins/${ plugin.basename }`,
|
|
36
|
+
} );
|
|
37
|
+
} else if ( plugin.type === 'zip' && plugin.url ) {
|
|
38
|
+
blueprint.steps.push( {
|
|
39
|
+
step: 'installPlugin',
|
|
40
|
+
pluginData: { resource: 'url', url: plugin.url },
|
|
41
|
+
options: { activate: true },
|
|
42
|
+
} );
|
|
43
|
+
} else {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Plugin source "${ plugin.basename || plugin.path }" of type "${
|
|
46
|
+
plugin.type
|
|
47
|
+
}" ` +
|
|
48
|
+
`is not supported with Playground runtime. Only local, git, and zip plugins are supported.`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Note: Themes are mounted via CLI args but NOT activated.
|
|
54
|
+
// This matches Docker runtime behavior where WordPress uses its default theme.
|
|
55
|
+
// Users can activate themes manually or via wp-cli if needed.
|
|
56
|
+
|
|
57
|
+
// Configure wp-config constants
|
|
58
|
+
const wpConfigConsts = {};
|
|
59
|
+
for ( const [ key, value ] of Object.entries( envConfig.config || {} ) ) {
|
|
60
|
+
if ( value !== null ) {
|
|
61
|
+
wpConfigConsts[ key ] = value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if ( Object.keys( wpConfigConsts ).length > 0 ) {
|
|
65
|
+
blueprint.steps.push( {
|
|
66
|
+
step: 'defineWpConfigConsts',
|
|
67
|
+
consts: wpConfigConsts,
|
|
68
|
+
} );
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Handle multisite
|
|
72
|
+
if ( envConfig.multisite ) {
|
|
73
|
+
blueprint.steps.push( {
|
|
74
|
+
step: 'enableMultisite',
|
|
75
|
+
} );
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return blueprint;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get mount arguments for the Playground CLI.
|
|
83
|
+
*
|
|
84
|
+
* @param {Object} config The wp-env config object.
|
|
85
|
+
* @return {string[]} Array of mount arguments.
|
|
86
|
+
*/
|
|
87
|
+
function getMountArgs( config ) {
|
|
88
|
+
const args = [];
|
|
89
|
+
const envConfig = config.env.development;
|
|
90
|
+
|
|
91
|
+
// Mount plugins
|
|
92
|
+
for ( const plugin of envConfig.pluginSources || [] ) {
|
|
93
|
+
if ( plugin.type === 'local' || plugin.type === 'git' ) {
|
|
94
|
+
args.push(
|
|
95
|
+
'--mount-dir',
|
|
96
|
+
plugin.path,
|
|
97
|
+
`/wordpress/wp-content/plugins/${ plugin.basename }`
|
|
98
|
+
);
|
|
99
|
+
} else if ( plugin.type !== 'zip' ) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Plugin source "${ plugin.basename || plugin.path }" of type "${
|
|
102
|
+
plugin.type
|
|
103
|
+
}" ` +
|
|
104
|
+
`is not supported with Playground runtime. Only local, git, and zip plugins are supported.`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Mount themes
|
|
110
|
+
// All theme types (local, git, zip) can be mounted after downloading/extraction
|
|
111
|
+
for ( const theme of envConfig.themeSources || [] ) {
|
|
112
|
+
args.push(
|
|
113
|
+
'--mount-dir',
|
|
114
|
+
theme.path,
|
|
115
|
+
`/wordpress/wp-content/themes/${ theme.basename }`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Mount custom mappings
|
|
120
|
+
// All source types (local, git, zip) can be mounted after downloading/extraction
|
|
121
|
+
for ( const [ wpDir, source ] of Object.entries(
|
|
122
|
+
envConfig.mappings || {}
|
|
123
|
+
) ) {
|
|
124
|
+
args.push( '--mount-dir', source.path, `/wordpress/${ wpDir }` );
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Translate core source to Playground's --wp flag or mount it.
|
|
128
|
+
if ( envConfig.coreSource ) {
|
|
129
|
+
if ( envConfig.coreSource.type === 'zip' && envConfig.coreSource.url ) {
|
|
130
|
+
// For zip URLs, let Playground download WordPress natively.
|
|
131
|
+
args.push( '--wp', envConfig.coreSource.url );
|
|
132
|
+
} else if ( envConfig.coreSource.type === 'git' ) {
|
|
133
|
+
// For git sources, pass the version ref to Playground's --wp flag.
|
|
134
|
+
// e.g., WordPress/WordPress#6.5 → --wp 6.5
|
|
135
|
+
// WordPress/WordPress (no ref) → default "latest", no flag needed.
|
|
136
|
+
if ( envConfig.coreSource.ref ) {
|
|
137
|
+
args.push( '--wp', envConfig.coreSource.ref );
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
// For local sources, mount the directory and tell Playground to
|
|
141
|
+
// use the existing files instead of downloading its own copy.
|
|
142
|
+
args.push(
|
|
143
|
+
'--mount-dir-before-install',
|
|
144
|
+
envConfig.coreSource.path,
|
|
145
|
+
'/wordpress',
|
|
146
|
+
'--wordpress-install-mode',
|
|
147
|
+
'install-from-existing-files-if-needed'
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return args;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = {
|
|
156
|
+
buildBlueprint,
|
|
157
|
+
getMountArgs,
|
|
158
|
+
};
|