@wordpress/env 10.38.1-next.v.0 → 10.39.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.
@@ -2,42 +2,21 @@
2
2
  /**
3
3
  * External dependencies
4
4
  */
5
- const { v2: dockerCompose } = require( 'docker-compose' );
6
- const util = require( 'util' );
7
- const path = require( 'path' );
8
5
  const fs = require( 'fs' ).promises;
6
+ const path = require( 'path' );
9
7
  const { confirm } = require( '@inquirer/prompts' );
10
-
11
- /**
12
- * Promisified dependencies
13
- */
14
- const sleep = util.promisify( setTimeout );
15
8
  const { rimraf } = require( 'rimraf' );
16
- const exec = util.promisify( require( 'child_process' ).exec );
17
9
 
18
10
  /**
19
11
  * Internal dependencies
20
12
  */
21
- const retry = require( '../retry' );
22
- const stop = require( './stop' );
23
- const initConfig = require( '../init-config' );
24
- const downloadSources = require( '../download-sources' );
25
- const downloadWPPHPUnit = require( '../download-wp-phpunit' );
26
- const {
27
- checkDatabaseConnection,
28
- configureWordPress,
29
- setupWordPressDirectories,
30
- readWordPressVersion,
31
- canAccessWPORG,
32
- } = require( '../wordpress' );
33
- const { didCacheChange, setCache } = require( '../cache' );
34
- const md5 = require( '../md5' );
13
+ const { loadConfig } = require( '../config' );
35
14
  const { executeLifecycleScript } = require( '../execute-lifecycle-script' );
15
+ const { getRuntime } = require( '../runtime' );
36
16
 
37
17
  /**
38
18
  * @typedef {import('../config').WPConfig} WPConfig
39
19
  */
40
- const CONFIG_CACHE_KEY = 'config_checksum';
41
20
 
42
21
  /**
43
22
  * Starts the development server.
@@ -49,6 +28,7 @@ const CONFIG_CACHE_KEY = 'config_checksum';
49
28
  * @param {string} options.spx The SPX mode to set.
50
29
  * @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
51
30
  * @param {boolean} options.debug True if debug mode is enabled.
31
+ * @param {string} options.runtime The runtime to use ('docker' or 'playground').
52
32
  */
53
33
  module.exports = async function start( {
54
34
  spinner,
@@ -57,17 +37,19 @@ module.exports = async function start( {
57
37
  spx,
58
38
  scripts,
59
39
  debug,
40
+ runtime: runtimeName = 'docker',
60
41
  } ) {
61
42
  spinner.text = 'Reading configuration.';
62
- await checkForLegacyInstall( spinner );
63
43
 
64
- const config = await initConfig( {
65
- spinner,
66
- debug,
67
- xdebug,
68
- spx,
69
- writeChanges: true,
70
- } );
44
+ const runtime = getRuntime( runtimeName );
45
+
46
+ // Check for legacy Docker installs (Docker-specific UI concern)
47
+ if ( runtimeName === 'docker' ) {
48
+ await checkForLegacyInstall( spinner );
49
+ }
50
+
51
+ const config = await loadConfig( path.resolve( '.' ) );
52
+ config.debug = debug;
71
53
 
72
54
  if ( ! config.detectedLocalConfig ) {
73
55
  const { configDirectoryPath } = config;
@@ -77,230 +59,43 @@ module.exports = async function start( {
77
59
  spinner.start();
78
60
  }
79
61
 
80
- // Check if the hash of the config has changed. If so, run configuration.
81
- const configHash = md5( config );
82
- const { workDirectoryPath, dockerComposeConfigPath } = config;
83
- const shouldConfigureWp =
84
- ( update ||
85
- ( await didCacheChange( CONFIG_CACHE_KEY, configHash, {
86
- workDirectoryPath,
87
- } ) ) ) &&
88
- // Don't reconfigure everything when we can't connect to the internet because
89
- // the majority of update tasks involve connecting to the internet. (Such
90
- // as downloading sources and pulling docker images.)
91
- ( await canAccessWPORG() );
92
-
93
- const dockerComposeConfig = {
94
- config: dockerComposeConfigPath,
95
- log: config.debug,
96
- };
97
-
98
- if ( ! ( await canAccessWPORG() ) ) {
99
- spinner.info( 'wp-env is offline' );
100
- }
101
-
102
- /**
103
- * If the Docker image is already running and the `wp-env` files have been
104
- * deleted, the start command will not complete successfully. Stopping
105
- * the container before continuing allows the docker entrypoint script,
106
- * which restores the files, to run again when we start the containers.
107
- *
108
- * Additionally, this serves as a way to restart the container entirely
109
- * should the need arise.
110
- *
111
- * @see https://github.com/WordPress/gutenberg/pull/20253#issuecomment-587228440
112
- */
113
- if ( shouldConfigureWp ) {
114
- await stop( { spinner, debug } );
115
- // Update the images before starting the services again.
116
- spinner.text = 'Updating docker images.';
117
-
118
- const directoryHash = path.basename( workDirectoryPath );
119
-
120
- // Note: when the base docker image is updated, we want that operation to
121
- // also update WordPress. Since we store wordpress/tests-wordpress files
122
- // as docker volumes, simply updating the image will not change those
123
- // files. Thus, we need to remove those volumes in order for the files
124
- // to be updated when pulling the new images.
125
- const volumesToRemove = `${ directoryHash }_wordpress ${ directoryHash }_tests-wordpress`;
126
-
127
- try {
128
- if ( config.debug ) {
129
- spinner.text = `Removing the WordPress volumes: ${ volumesToRemove }`;
130
- }
131
- await exec( `docker volume rm ${ volumesToRemove }` );
132
- } catch {
133
- // Note: we do not care about this error condition because it will
134
- // mostly happen when the volume already exists. This error would not
135
- // stop wp-env from working correctly.
136
- }
137
-
138
- await dockerCompose.pullAll( dockerComposeConfig );
139
- spinner.text = 'Downloading sources.';
140
- }
141
-
142
- await Promise.all( [
143
- dockerCompose.upOne( 'mysql', {
144
- ...dockerComposeConfig,
145
- commandOptions: shouldConfigureWp
146
- ? [ '--build', '--force-recreate' ]
147
- : [],
148
- } ),
149
- shouldConfigureWp && downloadSources( config, spinner ),
150
- ] );
151
-
152
- if ( shouldConfigureWp ) {
153
- spinner.text = 'Setting up WordPress directories';
154
-
155
- await setupWordPressDirectories( config );
156
-
157
- // Use the WordPress versions to download the PHPUnit suite.
158
- const wpVersions = await Promise.all( [
159
- readWordPressVersion(
160
- config.env.development.coreSource,
161
- spinner,
162
- debug
163
- ),
164
- readWordPressVersion( config.env.tests.coreSource, spinner, debug ),
165
- ] );
166
- await downloadWPPHPUnit(
167
- config,
168
- { development: wpVersions[ 0 ], tests: wpVersions[ 1 ] },
169
- spinner,
170
- debug
62
+ // Display Playground limitations info
63
+ if ( runtimeName === 'playground' ) {
64
+ spinner.info(
65
+ 'Note: Playground runtime does not support a separate tests environment. Only the development environment will be started.\n'
171
66
  );
67
+ spinner.start();
172
68
  }
173
69
 
174
- spinner.text = 'Starting WordPress.';
175
-
176
- await dockerCompose.upMany(
177
- [ 'wordpress', 'tests-wordpress', 'cli', 'tests-cli' ],
178
- {
179
- ...dockerComposeConfig,
180
- commandOptions: shouldConfigureWp
181
- ? [ '--build', '--force-recreate' ]
182
- : [],
183
- }
184
- );
185
-
186
- if ( config.env.development.phpmyadminPort ) {
187
- await dockerCompose.upOne( 'phpmyadmin', {
188
- ...dockerComposeConfig,
189
- commandOptions: shouldConfigureWp
190
- ? [ '--build', '--force-recreate' ]
191
- : [],
192
- } );
193
- }
194
-
195
- if ( config.env.tests.phpmyadminPort ) {
196
- await dockerCompose.upOne( 'tests-phpmyadmin', {
197
- ...dockerComposeConfig,
198
- commandOptions: shouldConfigureWp
199
- ? [ '--build', '--force-recreate' ]
200
- : [],
70
+ let result;
71
+ try {
72
+ result = await runtime.start( config, {
73
+ spinner,
74
+ update,
75
+ xdebug,
76
+ spx,
77
+ debug,
201
78
  } );
202
- }
203
-
204
- // Make sure we've consumed the custom CLI dockerfile.
205
- if ( shouldConfigureWp ) {
206
- await dockerCompose.buildOne( [ 'cli' ], { ...dockerComposeConfig } );
207
- }
208
-
209
- // Only run WordPress install/configuration when config has changed.
210
- if ( shouldConfigureWp ) {
211
- spinner.text = 'Configuring WordPress.';
212
-
79
+ } catch ( error ) {
80
+ // Attempt to stop any partially-started environment so that
81
+ // processes do not linger after a failed start.
213
82
  try {
214
- await checkDatabaseConnection( config );
215
- } catch ( error ) {
216
- // Wait 30 seconds for MySQL to accept connections.
217
- await retry( () => checkDatabaseConnection( config ), {
218
- times: 30,
219
- delay: 1000,
220
- } );
221
-
222
- // It takes 3-4 seconds for MySQL to be ready after it starts accepting connections.
223
- await sleep( 4000 );
83
+ await runtime.stop( config, { spinner } );
84
+ } catch {
85
+ // Ignore cleanup errors.
224
86
  }
225
-
226
- // Retry WordPress installation in case MySQL *still* wasn't ready.
227
- await Promise.all( [
228
- retry( () => configureWordPress( 'development', config, spinner ), {
229
- times: 2,
230
- } ),
231
- retry( () => configureWordPress( 'tests', config, spinner ), {
232
- times: 2,
233
- } ),
234
- ] );
235
-
236
- // Set the cache key once everything has been configured.
237
- await setCache( CONFIG_CACHE_KEY, configHash, {
238
- workDirectoryPath,
239
- } );
87
+ throw error;
240
88
  }
241
89
 
242
90
  if ( scripts ) {
243
91
  await executeLifecycleScript( 'afterStart', config, spinner );
244
92
  }
245
93
 
246
- const siteUrl = config.env.development.config.WP_SITEURL;
247
- const testsSiteUrl = config.env.tests.config.WP_SITEURL;
248
-
249
- const mySQLPort = await getPublicDockerPort(
250
- 'mysql',
251
- 3306,
252
- dockerComposeConfig
253
- );
254
-
255
- const testsMySQLPort = await getPublicDockerPort(
256
- 'tests-mysql',
257
- 3306,
258
- dockerComposeConfig
259
- );
260
-
261
- const phpmyadminPort = config.env.development.phpmyadminPort
262
- ? await getPublicDockerPort( 'phpmyadmin', 80, dockerComposeConfig )
263
- : null;
264
-
265
- const testsPhpmyadminPort = config.env.tests.phpmyadminPort
266
- ? await getPublicDockerPort(
267
- 'tests-phpmyadmin',
268
- 80,
269
- dockerComposeConfig
270
- )
271
- : null;
272
-
273
- spinner.prefixText = [
274
- 'WordPress development site started' +
275
- ( siteUrl ? ` at ${ siteUrl }` : '.' ),
276
- 'WordPress test site started' +
277
- ( testsSiteUrl ? ` at ${ testsSiteUrl }` : '.' ),
278
- `MySQL is listening on port ${ mySQLPort }`,
279
- `MySQL for automated testing is listening on port ${ testsMySQLPort }`,
280
- phpmyadminPort &&
281
- `phpMyAdmin started at http://localhost:${ phpmyadminPort }`,
282
- testsPhpmyadminPort &&
283
- `phpMyAdmin for automated testing started at http://localhost:${ testsPhpmyadminPort }`,
284
- ]
285
- .filter( Boolean )
286
- .join( '\n' );
94
+ spinner.prefixText = result.message;
287
95
  spinner.prefixText += '\n\n';
288
96
  spinner.text = 'Done!';
289
97
  };
290
98
 
291
- async function getPublicDockerPort(
292
- service,
293
- containerPort,
294
- dockerComposeConfig
295
- ) {
296
- const { out: address } = await dockerCompose.port(
297
- service,
298
- containerPort,
299
- dockerComposeConfig
300
- );
301
- return address.split( ':' ).pop().trim();
302
- }
303
-
304
99
  /**
305
100
  * Checks for legacy installs and provides
306
101
  * the user the option to delete them.
@@ -2,12 +2,13 @@
2
2
  /**
3
3
  * External dependencies
4
4
  */
5
- const { v2: dockerCompose } = require( 'docker-compose' );
5
+ const path = require( 'path' );
6
6
 
7
7
  /**
8
8
  * Internal dependencies
9
9
  */
10
- const initConfig = require( '../init-config' );
10
+ const { loadConfig } = require( '../config' );
11
+ const { getRuntime, detectRuntime } = require( '../runtime' );
11
12
 
12
13
  /**
13
14
  * Stops the development server.
@@ -17,17 +18,7 @@ const initConfig = require( '../init-config' );
17
18
  * @param {boolean} options.debug True if debug mode is enabled.
18
19
  */
19
20
  module.exports = async function stop( { spinner, debug } ) {
20
- const { dockerComposeConfigPath } = await initConfig( {
21
- spinner,
22
- debug,
23
- } );
24
-
25
- spinner.text = 'Stopping WordPress.';
26
-
27
- await dockerCompose.down( {
28
- config: dockerComposeConfigPath,
29
- log: debug,
30
- } );
31
-
32
- spinner.text = 'Stopped WordPress.';
21
+ const config = await loadConfig( path.resolve( '.' ) );
22
+ const runtime = getRuntime( detectRuntime( config.workDirectoryPath ) );
23
+ await runtime.stop( config, { spinner, debug } );
33
24
  };
@@ -2,11 +2,11 @@
2
2
  /**
3
3
  * External dependencies
4
4
  */
5
- const util = require( 'util' );
6
- const SimpleGit = require( 'simple-git' );
7
5
  const fs = require( 'fs' );
8
- const got = require( 'got' );
9
6
  const path = require( 'path' );
7
+ const util = require( 'util' );
8
+ const got = require( 'got' );
9
+ const SimpleGit = require( 'simple-git' );
10
10
 
11
11
  /**
12
12
  * Promisified dependencies
@@ -16,59 +16,9 @@ const extractZip = util.promisify( require( 'extract-zip' ) );
16
16
  const { rimraf } = require( 'rimraf' );
17
17
 
18
18
  /**
19
- * @typedef {import('./config').WPConfig} WPConfig
20
19
  * @typedef {import('./config').WPSource} WPSource
21
20
  */
22
21
 
23
- /**
24
- * Download each source for each environment. If the same source is used in
25
- * multiple environments, it will only be downloaded once.
26
- *
27
- * @param {WPConfig} config The wp-env configuration object.
28
- * @param {Object} spinner The spinner object to show progress.
29
- * @return {Promise} Returns a promise which resolves when the downloads finish.
30
- */
31
- module.exports = function downloadSources( config, spinner ) {
32
- const progresses = {};
33
- const getProgressSetter = ( id ) => ( progress ) => {
34
- progresses[ id ] = progress;
35
- spinner.text =
36
- 'Downloading WordPress.\n' +
37
- Object.entries( progresses )
38
- .map(
39
- ( [ key, value ] ) =>
40
- ` - ${ key }: ${ ( value * 100 ).toFixed( 0 ) }%`
41
- )
42
- .join( '\n' );
43
- };
44
-
45
- // Will contain a unique array of sources to download.
46
- const sources = [];
47
- const addedSources = {};
48
- const addSource = ( source ) => {
49
- if ( source && source.url && ! addedSources[ source.url ] ) {
50
- sources.push( source );
51
- addedSources[ source.url ] = true;
52
- }
53
- };
54
-
55
- for ( const env of Object.values( config.env ) ) {
56
- env.pluginSources.forEach( addSource );
57
- env.themeSources.forEach( addSource );
58
- Object.values( env.mappings ).forEach( addSource );
59
- addSource( env.coreSource );
60
- }
61
-
62
- return Promise.all(
63
- sources.map( ( source ) =>
64
- downloadSource( source, {
65
- onProgress: getProgressSetter( source.basename ),
66
- spinner,
67
- } )
68
- )
69
- );
70
- };
71
-
72
22
  /**
73
23
  * Downloads the given source if necessary. The specific action taken depends
74
24
  * on the source type. The source is downloaded to source.path.
@@ -204,3 +154,9 @@ async function downloadZipSource( source, { onProgress, spinner, debug } ) {
204
154
 
205
155
  onProgress( 1 );
206
156
  }
157
+
158
+ module.exports = {
159
+ downloadSource,
160
+ downloadGitSource,
161
+ downloadZipSource,
162
+ };
@@ -9,12 +9,12 @@ const path = require( 'path' );
9
9
  * Internal dependencies
10
10
  */
11
11
  const { hasSameCoreSource } = require( './wordpress' );
12
- const { dbEnv } = require( './config' );
12
+ const { dbEnv } = require( '../../config' );
13
13
  const getHostUser = require( './get-host-user' );
14
14
 
15
15
  /**
16
- * @typedef {import('./config').WPConfig} WPConfig
17
- * @typedef {import('./config').WPEnvironmentConfig} WPEnvironmentConfig
16
+ * @typedef {import('../../config').WPConfig} WPConfig
17
+ * @typedef {import('../../config').WPEnvironmentConfig} WPEnvironmentConfig
18
18
  */
19
19
 
20
20
  /**
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+ /**
3
+ * Internal dependencies
4
+ */
5
+ const { downloadSource } = require( '../../download-sources' );
6
+
7
+ /**
8
+ * @typedef {import('./config').WPConfig} WPConfig
9
+ * @typedef {import('./config').WPSource} WPSource
10
+ */
11
+
12
+ /**
13
+ * Download each source for each environment. If the same source is used in
14
+ * multiple environments, it will only be downloaded once.
15
+ *
16
+ * @param {WPConfig} config The wp-env configuration object.
17
+ * @param {Object} spinner The spinner object to show progress.
18
+ * @return {Promise} Returns a promise which resolves when the downloads finish.
19
+ */
20
+ module.exports = function downloadSources( config, spinner ) {
21
+ const progresses = {};
22
+ const getProgressSetter = ( id ) => ( progress ) => {
23
+ progresses[ id ] = progress;
24
+ spinner.text =
25
+ 'Downloading WordPress.\n' +
26
+ Object.entries( progresses )
27
+ .map(
28
+ ( [ key, value ] ) =>
29
+ ` - ${ key }: ${ ( value * 100 ).toFixed( 0 ) }%`
30
+ )
31
+ .join( '\n' );
32
+ };
33
+
34
+ // Will contain a unique array of sources to download.
35
+ const sources = [];
36
+ const addedSources = {};
37
+ const addSource = ( source ) => {
38
+ if ( source && source.url && ! addedSources[ source.url ] ) {
39
+ sources.push( source );
40
+ addedSources[ source.url ] = true;
41
+ }
42
+ };
43
+
44
+ for ( const env of Object.values( config.env ) ) {
45
+ env.pluginSources.forEach( addSource );
46
+ env.themeSources.forEach( addSource );
47
+ Object.values( env.mappings ).forEach( addSource );
48
+ addSource( env.coreSource );
49
+ }
50
+
51
+ return Promise.all(
52
+ sources.map( ( source ) =>
53
+ downloadSource( source, {
54
+ onProgress: getProgressSetter( source.basename ),
55
+ spinner,
56
+ } )
57
+ )
58
+ );
59
+ };