@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.
Files changed (37) hide show
  1. package/README.md +158 -75
  2. package/lib/cli.js +85 -24
  3. package/lib/commands/clean.js +16 -38
  4. package/lib/commands/cleanup.js +79 -0
  5. package/lib/commands/destroy.js +33 -41
  6. package/lib/commands/index.js +6 -2
  7. package/lib/commands/logs.js +20 -62
  8. package/lib/commands/reset.js +46 -0
  9. package/lib/commands/run.js +16 -117
  10. package/lib/commands/start.js +78 -243
  11. package/lib/commands/status.js +160 -0
  12. package/lib/commands/stop.js +17 -19
  13. package/lib/config/get-config-from-environment-vars.js +2 -5
  14. package/lib/config/load-config.js +35 -5
  15. package/lib/config/parse-config.js +53 -21
  16. package/lib/config/post-process-config.js +38 -16
  17. package/lib/config/test/__snapshots__/config-integration.js.snap +16 -0
  18. package/lib/config/test/parse-config.js +33 -0
  19. package/lib/config/test/post-process-config.js +52 -0
  20. package/lib/download-sources.js +9 -53
  21. package/lib/{build-docker-compose-config.js → runtime/docker/build-docker-compose-config.js} +167 -128
  22. package/lib/runtime/docker/download-sources.js +63 -0
  23. package/lib/{download-wp-phpunit.js → runtime/docker/download-wp-phpunit.js} +4 -1
  24. package/lib/runtime/docker/index.js +863 -0
  25. package/lib/{init-config.js → runtime/docker/init-config.js} +22 -14
  26. package/lib/runtime/docker/wordpress.js +309 -0
  27. package/lib/runtime/errors.js +28 -0
  28. package/lib/runtime/index.js +91 -0
  29. package/lib/runtime/playground/blueprint-builder.js +158 -0
  30. package/lib/runtime/playground/index.js +530 -0
  31. package/lib/test/build-docker-compose-config.js +150 -3
  32. package/lib/test/cli.js +49 -13
  33. package/lib/wordpress.js +1 -297
  34. package/package.json +6 -3
  35. package/lib/commands/install-path.js +0 -21
  36. /package/lib/{get-host-user.js → runtime/docker/get-host-user.js} +0 -0
  37. /package/lib/{validate-run-container.js → runtime/docker/validate-run-container.js} +0 -0
@@ -2,14 +2,14 @@
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' );
11
- const { configureWordPress, resetDatabase } = require( '../wordpress' );
12
10
  const { executeLifecycleScript } = require( '../execute-lifecycle-script' );
11
+ const { loadConfig } = require( '../config' );
12
+ const { getRuntime, detectRuntime } = require( '../runtime' );
13
13
 
14
14
  /**
15
15
  * @typedef {import('../wordpress').WPEnvironment} WPEnvironment
@@ -17,57 +17,35 @@ const { executeLifecycleScript } = require( '../execute-lifecycle-script' );
17
17
  */
18
18
 
19
19
  /**
20
- * Wipes the development server's database, the tests server's database, or both.
20
+ * @deprecated Use `reset` instead.
21
+ *
22
+ * Resets the development server's database, the tests server's database, or both.
21
23
  *
22
24
  * @param {Object} options
23
- * @param {WPEnvironmentSelection} options.environment The environment to clean. Either 'development', 'tests', or 'all'.
25
+ * @param {WPEnvironmentSelection} options.environment The environment to reset. Either 'development', 'tests', or 'all'.
24
26
  * @param {Object} options.spinner A CLI spinner which indicates progress.
25
27
  * @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
26
28
  * @param {boolean} options.debug True if debug mode is enabled.
29
+ * @param {string|null} options.config Path to a custom .wp-env.json configuration file.
27
30
  */
28
31
  module.exports = async function clean( {
29
32
  environment,
30
33
  spinner,
31
34
  scripts,
32
35
  debug,
36
+ config: customConfigPath,
33
37
  } ) {
34
- const config = await initConfig( { spinner, debug } );
35
-
36
- const description = `${ environment } environment${
37
- environment === 'all' ? 's' : ''
38
- }`;
39
- spinner.text = `Cleaning ${ description }.`;
40
-
41
- const tasks = [];
42
-
43
- // Start the database first to avoid race conditions where all tasks create
44
- // different docker networks with the same name.
45
- await dockerCompose.upOne( 'mysql', {
46
- config: config.dockerComposeConfigPath,
47
- log: config.debug,
48
- } );
38
+ spinner.warn( 'The `clean` command is deprecated. Use `reset` instead.' );
49
39
 
50
- if ( environment === 'all' || environment === 'development' ) {
51
- tasks.push(
52
- resetDatabase( 'development', config )
53
- .then( () => configureWordPress( 'development', config ) )
54
- .catch( () => {} )
55
- );
56
- }
57
-
58
- if ( environment === 'all' || environment === 'tests' ) {
59
- tasks.push(
60
- resetDatabase( 'tests', config )
61
- .then( () => configureWordPress( 'tests', config ) )
62
- .catch( () => {} )
63
- );
64
- }
40
+ const config = await loadConfig( path.resolve( '.' ), customConfigPath );
41
+ const runtime = getRuntime(
42
+ await detectRuntime( config.workDirectoryPath )
43
+ );
65
44
 
66
- await Promise.all( tasks );
45
+ await runtime.clean( config, { environment, spinner, debug } );
67
46
 
47
+ // Execute afterClean for backwards compatibility.
68
48
  if ( scripts ) {
69
49
  await executeLifecycleScript( 'afterClean', config, spinner );
70
50
  }
71
-
72
- spinner.text = `Cleaned ${ description }.`;
73
51
  };
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+ /**
3
+ * External dependencies
4
+ */
5
+ const fs = require( 'fs' ).promises;
6
+ const path = require( 'path' );
7
+ const { confirm } = require( '@inquirer/prompts' );
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ const { loadConfig } = require( '../config' );
13
+ const { executeLifecycleScript } = require( '../execute-lifecycle-script' );
14
+ const { getRuntime, detectRuntime } = require( '../runtime' );
15
+
16
+ /**
17
+ * Cleanup the development server.
18
+ *
19
+ * Removes Docker containers, volumes, networks, and local files,
20
+ * but preserves Docker images for faster re-starts.
21
+ *
22
+ * @param {Object} options
23
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
24
+ * @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
25
+ * @param {boolean} options.force If true, skips the confirmation prompt.
26
+ * @param {boolean} options.debug True if debug mode is enabled.
27
+ * @param {string|null} options.config Path to a custom .wp-env.json configuration file.
28
+ */
29
+ module.exports = async function cleanup( {
30
+ spinner,
31
+ scripts,
32
+ force,
33
+ debug,
34
+ config: customConfigPath,
35
+ } ) {
36
+ const config = await loadConfig( path.resolve( '.' ), customConfigPath );
37
+
38
+ try {
39
+ await fs.readdir( config.workDirectoryPath );
40
+ } catch {
41
+ spinner.text = 'Could not find any files to remove.';
42
+ return;
43
+ }
44
+
45
+ const runtime = getRuntime(
46
+ await detectRuntime( config.workDirectoryPath )
47
+ );
48
+
49
+ spinner.info( runtime.getCleanupWarningMessage() );
50
+
51
+ let yesDelete = force;
52
+ if ( ! force ) {
53
+ try {
54
+ yesDelete = await confirm( {
55
+ message: 'Are you sure you want to continue?',
56
+ default: false,
57
+ } );
58
+ } catch ( error ) {
59
+ if ( error.name === 'ExitPromptError' ) {
60
+ console.log( 'Cancelled.' );
61
+ process.exit( 1 );
62
+ }
63
+ throw error;
64
+ }
65
+ }
66
+
67
+ spinner.start();
68
+
69
+ if ( ! yesDelete ) {
70
+ spinner.text = 'Cancelled.';
71
+ return;
72
+ }
73
+
74
+ await runtime.cleanup( config, { spinner, debug } );
75
+
76
+ if ( scripts ) {
77
+ await executeLifecycleScript( 'afterCleanup', config, spinner );
78
+ }
79
+ };
@@ -2,32 +2,35 @@
2
2
  /**
3
3
  * External dependencies
4
4
  */
5
- const { v2: dockerCompose } = require( 'docker-compose' );
6
5
  const fs = require( 'fs' ).promises;
7
6
  const path = require( 'path' );
8
7
  const { confirm } = require( '@inquirer/prompts' );
9
8
 
10
- /**
11
- * Promisified dependencies
12
- */
13
- const { rimraf } = require( 'rimraf' );
14
-
15
9
  /**
16
10
  * Internal dependencies
17
11
  */
18
12
  const { loadConfig } = require( '../config' );
19
13
  const { executeLifecycleScript } = require( '../execute-lifecycle-script' );
14
+ const { getRuntime, detectRuntime } = require( '../runtime' );
20
15
 
21
16
  /**
22
17
  * Destroy the development server.
23
18
  *
24
- * @param {Object} options
25
- * @param {Object} options.spinner A CLI spinner which indicates progress.
26
- * @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
27
- * @param {boolean} options.debug True if debug mode is enabled.
19
+ * @param {Object} options
20
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
21
+ * @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
22
+ * @param {boolean} options.force If true, skips the confirmation prompt.
23
+ * @param {boolean} options.debug True if debug mode is enabled.
24
+ * @param {string|null} options.config Path to a custom .wp-env.json configuration file.
28
25
  */
29
- module.exports = async function destroy( { spinner, scripts, debug } ) {
30
- const config = await loadConfig( path.resolve( '.' ) );
26
+ module.exports = async function destroy( {
27
+ spinner,
28
+ scripts,
29
+ force,
30
+ debug,
31
+ config: customConfigPath,
32
+ } ) {
33
+ const config = await loadConfig( path.resolve( '.' ), customConfigPath );
31
34
 
32
35
  try {
33
36
  await fs.readdir( config.workDirectoryPath );
@@ -36,22 +39,26 @@ module.exports = async function destroy( { spinner, scripts, debug } ) {
36
39
  return;
37
40
  }
38
41
 
39
- spinner.info(
40
- 'WARNING! This will remove Docker containers, volumes, networks, and images associated with the WordPress instance.'
42
+ const runtime = getRuntime(
43
+ await detectRuntime( config.workDirectoryPath )
41
44
  );
42
45
 
43
- let yesDelete = false;
44
- try {
45
- yesDelete = await confirm( {
46
- message: 'Are you sure you want to continue?',
47
- default: false,
48
- } );
49
- } catch ( error ) {
50
- if ( error.name === 'ExitPromptError' ) {
51
- console.log( 'Cancelled.' );
52
- process.exit( 1 );
46
+ spinner.info( runtime.getDestroyWarningMessage() );
47
+
48
+ let yesDelete = force;
49
+ if ( ! force ) {
50
+ try {
51
+ yesDelete = await confirm( {
52
+ message: 'Are you sure you want to continue?',
53
+ default: false,
54
+ } );
55
+ } catch ( error ) {
56
+ if ( error.name === 'ExitPromptError' ) {
57
+ console.log( 'Cancelled.' );
58
+ process.exit( 1 );
59
+ }
60
+ throw error;
53
61
  }
54
- throw error;
55
62
  }
56
63
 
57
64
  spinner.start();
@@ -61,24 +68,9 @@ module.exports = async function destroy( { spinner, scripts, debug } ) {
61
68
  return;
62
69
  }
63
70
 
64
- spinner.text = 'Removing docker images, volumes, and networks.';
65
-
66
- await dockerCompose.down( {
67
- config: config.dockerComposeConfigPath,
68
- commandOptions: [ '--volumes', '--remove-orphans', '--rmi', 'all' ],
69
- log: debug,
70
- } );
71
-
72
- spinner.text = 'Removing local files.';
73
- // Note: there is a race condition where docker compose actually hasn't finished
74
- // by this point, which causes rimraf to fail. We need to wait at least 2.5-5s,
75
- // but using 10s in case it's dependant on the machine.
76
- await new Promise( ( resolve ) => setTimeout( resolve, 10000 ) );
77
- await rimraf( config.workDirectoryPath );
71
+ await runtime.destroy( config, { spinner, debug } );
78
72
 
79
73
  if ( scripts ) {
80
74
  await executeLifecycleScript( 'afterDestroy', config, spinner );
81
75
  }
82
-
83
- spinner.text = 'Removed WordPress environment.';
84
76
  };
@@ -4,18 +4,22 @@
4
4
  */
5
5
  const start = require( './start' );
6
6
  const stop = require( './stop' );
7
+ const reset = require( './reset' );
7
8
  const clean = require( './clean' );
8
9
  const run = require( './run' );
9
10
  const destroy = require( './destroy' );
11
+ const cleanup = require( './cleanup' );
10
12
  const logs = require( './logs' );
11
- const installPath = require( './install-path' );
13
+ const status = require( './status' );
12
14
 
13
15
  module.exports = {
14
16
  start,
15
17
  stop,
18
+ reset,
16
19
  clean,
17
20
  run,
18
21
  destroy,
22
+ cleanup,
19
23
  logs,
20
- installPath,
24
+ status,
21
25
  };
@@ -2,76 +2,34 @@
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
  * Displays the Docker & PHP logs on the given environment.
14
15
  *
15
- * @param {Object} options
16
- * @param {Object} options.environment The environment to run the command in (develop or tests).
17
- * @param {Object} options.watch If true, follow along with log output.
18
- * @param {Object} options.spinner A CLI spinner which indicates progress.
19
- * @param {boolean} options.debug True if debug mode is enabled.
16
+ * @param {Object} options
17
+ * @param {Object} options.environment The environment to run the command in (develop or tests).
18
+ * @param {Object} options.watch If true, follow along with log output.
19
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
20
+ * @param {boolean} options.debug True if debug mode is enabled.
21
+ * @param {string|null} options.config Path to a custom .wp-env.json configuration file.
20
22
  */
21
- module.exports = async function logs( { environment, watch, spinner, debug } ) {
22
- const config = await initConfig( { spinner, debug } );
23
-
24
- // If we show text while watching the logs, it will continue showing up every
25
- // few lines in the logs as they happen, which isn't a good look. So only
26
- // show the message if we are not watching the logs.
27
- if ( ! watch ) {
28
- spinner.text = `Showing logs for the ${ environment } environment.`;
29
- }
30
-
31
- const servicesToWatch =
32
- environment === 'all'
33
- ? [ 'tests-wordpress', 'wordpress' ]
34
- : [ environment === 'tests' ? 'tests-wordpress' : 'wordpress' ];
35
-
36
- const output = await Promise.all( [
37
- ...servicesToWatch.map( ( service ) =>
38
- dockerCompose.logs( service, {
39
- config: config.dockerComposeConfigPath,
40
- log: watch, // Must log inline if we are watching the log output.
41
- commandOptions: watch ? [ '--follow' ] : [],
42
- } )
43
- ),
44
- ] );
45
-
46
- // Combine the results from each docker output.
47
- const result = output.reduce(
48
- ( acc, current ) => {
49
- if ( current.out ) {
50
- acc.out = acc.out.concat( current.out );
51
- }
52
- if ( current.err ) {
53
- acc.err = acc.err.concat( current.err );
54
- }
55
- if ( current.exitCode !== 0 ) {
56
- acc.hasNon0ExitCode = true;
57
- }
58
- return acc;
59
- },
60
- { out: '', err: '', hasNon0ExitCode: false }
23
+ module.exports = async function logs( {
24
+ environment,
25
+ watch,
26
+ spinner,
27
+ debug,
28
+ config: customConfigPath,
29
+ } ) {
30
+ const config = await loadConfig( path.resolve( '.' ), customConfigPath );
31
+ const runtime = getRuntime(
32
+ await detectRuntime( config.workDirectoryPath )
61
33
  );
62
-
63
- if ( result.out.length ) {
64
- console.log(
65
- process.stdout.isTTY ? `\n\n${ result.out }\n\n` : result.out
66
- );
67
- } else if ( result.err.length ) {
68
- console.error(
69
- process.stdout.isTTY ? `\n\n${ result.err }\n\n` : result.err
70
- );
71
- if ( result.hasNon0ExitCode ) {
72
- throw result.err;
73
- }
74
- }
75
-
76
- spinner.text = 'Finished showing logs.';
34
+ await runtime.logs( config, { environment, watch, spinner, debug } );
77
35
  };
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+ /**
3
+ * External dependencies
4
+ */
5
+ const path = require( 'path' );
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ const { executeLifecycleScript } = require( '../execute-lifecycle-script' );
11
+ const { loadConfig } = require( '../config' );
12
+ const { getRuntime, detectRuntime } = require( '../runtime' );
13
+
14
+ /**
15
+ * @typedef {import('../wordpress').WPEnvironment} WPEnvironment
16
+ * @typedef {import('../wordpress').WPEnvironmentSelection} WPEnvironmentSelection
17
+ */
18
+
19
+ /**
20
+ * Resets the development server's database, the tests server's database, or both.
21
+ *
22
+ * @param {Object} options
23
+ * @param {WPEnvironmentSelection} options.environment The environment to reset. Either 'development', 'tests', or 'all'.
24
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
25
+ * @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
26
+ * @param {boolean} options.debug True if debug mode is enabled.
27
+ * @param {string|null} options.config Path to a custom .wp-env.json configuration file.
28
+ */
29
+ module.exports = async function reset( {
30
+ environment,
31
+ spinner,
32
+ scripts,
33
+ debug,
34
+ config: customConfigPath,
35
+ } ) {
36
+ const config = await loadConfig( path.resolve( '.' ), customConfigPath );
37
+ const runtime = getRuntime(
38
+ await detectRuntime( config.workDirectoryPath )
39
+ );
40
+
41
+ await runtime.clean( config, { environment, spinner, debug } );
42
+
43
+ if ( scripts ) {
44
+ await executeLifecycleScript( 'afterReset', config, spinner );
45
+ }
46
+ };
@@ -2,29 +2,25 @@
2
2
  /**
3
3
  * External dependencies
4
4
  */
5
- const { spawn } = require( 'child_process' );
6
5
  const path = require( 'path' );
7
6
 
8
7
  /**
9
8
  * Internal dependencies
10
9
  */
11
- const initConfig = require( '../init-config' );
12
- const getHostUser = require( '../get-host-user' );
13
-
14
- /**
15
- * @typedef {import('../config').WPConfig} WPConfig
16
- */
10
+ const { loadConfig } = require( '../config' );
11
+ const { getRuntime, detectRuntime } = require( '../runtime' );
17
12
 
18
13
  /**
19
14
  * Runs an arbitrary command on the given Docker container.
20
15
  *
21
- * @param {Object} options
22
- * @param {string} options.container The Docker container to run the command on.
23
- * @param {string[]} options.command The command to run.
24
- * @param {string[]} options.'--' Any arguments that were passed after a double dash.
25
- * @param {string} options.envCwd The working directory for the command to be executed from.
26
- * @param {Object} options.spinner A CLI spinner which indicates progress.
27
- * @param {boolean} options.debug True if debug mode is enabled.
16
+ * @param {Object} options
17
+ * @param {string} options.container The Docker container to run the command on.
18
+ * @param {string[]} options.command The command to run.
19
+ * @param {string[]} options.'--' Any arguments that were passed after a double dash.
20
+ * @param {string} options.envCwd The working directory for the command to be executed from.
21
+ * @param {Object} options.spinner A CLI spinner which indicates progress.
22
+ * @param {boolean} options.debug True if debug mode is enabled.
23
+ * @param {string|null} options.config Path to a custom .wp-env.json configuration file.
28
24
  */
29
25
  module.exports = async function run( {
30
26
  container,
@@ -33,8 +29,12 @@ module.exports = async function run( {
33
29
  envCwd,
34
30
  spinner,
35
31
  debug,
32
+ config: customConfigPath,
36
33
  } ) {
37
- const config = await initConfig( { spinner, debug } );
34
+ const config = await loadConfig( path.resolve( '.' ), customConfigPath );
35
+ const runtime = getRuntime(
36
+ await detectRuntime( config.workDirectoryPath )
37
+ );
38
38
 
39
39
  // Include any double dashed arguments in the command so that we can pass them to Docker.
40
40
  // This lets users pass options that the command defines without them being parsed.
@@ -42,106 +42,5 @@ module.exports = async function run( {
42
42
  command.push( ...doubleDashedArgs );
43
43
  }
44
44
 
45
- // Shows a contextual tip for the given command.
46
- const joinedCommand = command.join( ' ' );
47
- showCommandTips( joinedCommand, container, spinner );
48
-
49
- await spawnCommandDirectly( config, container, command, envCwd, spinner );
50
-
51
- spinner.text = `Ran \`${ joinedCommand }\` in '${ container }'.`;
45
+ await runtime.run( config, { container, command, envCwd, spinner, debug } );
52
46
  };
53
-
54
- /**
55
- * Runs an arbitrary command on the given Docker container.
56
- *
57
- * @param {WPConfig} config The wp-env configuration.
58
- * @param {string} container The Docker container to run the command on.
59
- * @param {string[]} command The command to run.
60
- * @param {string} envCwd The working directory for the command to be executed from.
61
- * @param {Object} spinner A CLI spinner which indicates progress.
62
- */
63
- function spawnCommandDirectly( config, container, command, envCwd, spinner ) {
64
- // Both the `wordpress` and `tests-wordpress` containers have the host's
65
- // user so that they can maintain ownership parity with the host OS.
66
- // We should run any commands as that user so that they are able
67
- // to interact with the files mounted from the host.
68
- const hostUser = getHostUser();
69
-
70
- // Since Docker requires absolute paths, we should resolve the input to a POSIX path.
71
- // This is needed because Windows resolves relative paths from the C: drive.
72
- envCwd = path.posix.resolve(
73
- // Not all containers have the same starting working directory.
74
- container === 'mysql' || container === 'tests-mysql'
75
- ? '/'
76
- : '/var/www/html',
77
- // Remove spaces and single quotes from both ends of the path.
78
- // This is needed because Windows treats single quotes as a literal character.
79
- envCwd.trim().replace( /^'|'$/g, '' )
80
- );
81
-
82
- const composeCommand = [
83
- 'compose',
84
- '-f',
85
- config.dockerComposeConfigPath,
86
- 'exec',
87
- '-w',
88
- envCwd,
89
- '--user',
90
- hostUser.fullUser,
91
- ];
92
-
93
- if ( ! process.stdout.isTTY ) {
94
- composeCommand.push( '-T' );
95
- }
96
-
97
- composeCommand.push( container, ...command );
98
-
99
- return new Promise( ( resolve, reject ) => {
100
- // Note: since the npm docker-compose package uses the -T option, we
101
- // cannot use it to spawn an interactive command. Thus, we run docker-
102
- // compose on the CLI directly.
103
- const childProc = spawn(
104
- 'docker',
105
- composeCommand,
106
- { stdio: 'inherit' },
107
- spinner
108
- );
109
- childProc.on( 'error', reject );
110
- childProc.on( 'exit', ( code ) => {
111
- // Code 130 is set if the user tries to exit with ctrl-c before using
112
- // ctrl-d (so it is not an error which should fail the script.)
113
- if ( code === 0 || code === 130 ) {
114
- resolve();
115
- } else {
116
- reject( `Command failed with exit code ${ code }` );
117
- }
118
- } );
119
- } );
120
- }
121
-
122
- /**
123
- * This shows a contextual tip for the command being run. Certain commands (like
124
- * bash) may have weird behavior (exit with ctrl-d instead of ctrl-c or ctrl-z),
125
- * so we want the user to have that information without having to ask someone.
126
- *
127
- * @param {string} joinedCommand The command for which to show a tip joined by spaces.
128
- * @param {string} container The container the command will be run on.
129
- * @param {Object} spinner A spinner object to show progress.
130
- */
131
- function showCommandTips( joinedCommand, container, spinner ) {
132
- if ( ! joinedCommand.length ) {
133
- return;
134
- }
135
-
136
- const tip = `Starting '${ joinedCommand }' on the ${ container } container. ${ ( () => {
137
- switch ( joinedCommand ) {
138
- case 'bash':
139
- return 'Exit bash with ctrl-d.';
140
- case 'wp shell':
141
- return 'Exit the WordPress shell with ctrl-c.';
142
- default:
143
- return '';
144
- }
145
- } )() }\n`;
146
- spinner.info( tip );
147
- }