@wordpress/env 6.0.0 → 8.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 (55) hide show
  1. package/README.md +130 -66
  2. package/lib/build-docker-compose-config.js +67 -96
  3. package/lib/cache.js +1 -0
  4. package/lib/cli.js +39 -10
  5. package/lib/commands/clean.js +13 -1
  6. package/lib/commands/destroy.js +21 -42
  7. package/lib/commands/index.js +1 -0
  8. package/lib/commands/install-path.js +1 -0
  9. package/lib/commands/logs.js +1 -0
  10. package/lib/commands/run.js +45 -21
  11. package/lib/commands/start.js +29 -8
  12. package/lib/commands/stop.js +1 -0
  13. package/lib/config/add-or-replace-port.js +12 -3
  14. package/lib/config/db-env.js +1 -0
  15. package/lib/config/detect-directory-type.js +1 -1
  16. package/lib/config/get-cache-directory.js +39 -0
  17. package/lib/config/get-config-from-environment-vars.js +106 -0
  18. package/lib/config/index.js +7 -5
  19. package/lib/config/load-config.js +92 -0
  20. package/lib/config/merge-configs.js +105 -0
  21. package/lib/config/parse-config.js +501 -157
  22. package/lib/config/parse-source-string.js +164 -0
  23. package/lib/config/post-process-config.js +202 -0
  24. package/lib/config/read-raw-config-file.js +7 -33
  25. package/lib/config/test/__snapshots__/config-integration.js.snap +295 -0
  26. package/lib/config/test/add-or-replace-port.js +29 -1
  27. package/lib/config/test/config-integration.js +164 -0
  28. package/lib/config/test/get-cache-directory.js +57 -0
  29. package/lib/config/test/merge-configs.js +121 -0
  30. package/lib/config/test/parse-config.js +393 -0
  31. package/lib/config/test/parse-source-string.js +154 -0
  32. package/lib/config/test/post-process-config.js +299 -0
  33. package/lib/config/test/read-raw-config-file.js +19 -63
  34. package/lib/config/test/validate-config.js +363 -0
  35. package/lib/config/validate-config.js +119 -54
  36. package/lib/env.js +2 -0
  37. package/lib/execute-lifecycle-script.js +86 -0
  38. package/lib/get-host-user.js +27 -0
  39. package/lib/init-config.js +186 -63
  40. package/lib/md5.js +1 -0
  41. package/lib/parse-xdebug-mode.js +1 -0
  42. package/lib/retry.js +1 -0
  43. package/lib/test/__snapshots__/md5.js.snap +9 -0
  44. package/lib/test/build-docker-compose-config.js +134 -0
  45. package/lib/test/cache.js +154 -0
  46. package/lib/test/cli.js +149 -0
  47. package/lib/test/execute-lifecycle-script.js +59 -0
  48. package/lib/test/md5.js +35 -0
  49. package/lib/test/parse-xdebug-mode.js +41 -0
  50. package/lib/validate-run-container.js +41 -0
  51. package/lib/wordpress.js +4 -19
  52. package/package.json +2 -2
  53. package/lib/config/config.js +0 -353
  54. package/lib/config/test/__snapshots__/config.js.snap +0 -83
  55. package/lib/config/test/config.js +0 -1241
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+ /**
3
+ * Internal dependencies
4
+ */
5
+ const cli = require( '../cli' );
6
+ const env = require( '../env' );
7
+
8
+ /**
9
+ * Mocked dependencies
10
+ */
11
+ jest.spyOn( process, 'exit' ).mockImplementation( () => {} );
12
+ jest.mock( 'ora', () => () => ( {
13
+ start() {
14
+ return { text: '', succeed: jest.fn(), fail: jest.fn() };
15
+ },
16
+ } ) );
17
+ jest.mock( '../env', () => {
18
+ const actual = jest.requireActual( '../env' );
19
+ return {
20
+ start: jest.fn( Promise.resolve.bind( Promise ) ),
21
+ stop: jest.fn( Promise.resolve.bind( Promise ) ),
22
+ clean: jest.fn( Promise.resolve.bind( Promise ) ),
23
+ run: jest.fn( Promise.resolve.bind( Promise ) ),
24
+ ValidationError: actual.ValidationError,
25
+ LifecycleScriptError: actual.LifecycleScriptError,
26
+ };
27
+ } );
28
+
29
+ describe( 'env cli', () => {
30
+ beforeEach( jest.clearAllMocks );
31
+
32
+ it( 'parses start commands.', () => {
33
+ cli().parse( [ 'start' ] );
34
+ const { spinner } = env.start.mock.calls[ 0 ][ 0 ];
35
+ expect( spinner.text ).toBe( '' );
36
+ } );
37
+
38
+ it( 'parses stop commands.', () => {
39
+ cli().parse( [ 'stop' ] );
40
+ const { spinner } = env.stop.mock.calls[ 0 ][ 0 ];
41
+ expect( spinner.text ).toBe( '' );
42
+ } );
43
+
44
+ it( 'parses clean commands for the default environment.', () => {
45
+ cli().parse( [ 'clean' ] );
46
+ const { environment, spinner } = env.clean.mock.calls[ 0 ][ 0 ];
47
+ expect( environment ).toBe( 'tests' );
48
+ expect( spinner.text ).toBe( '' );
49
+ } );
50
+ it( 'parses clean commands for all environments.', () => {
51
+ cli().parse( [ 'clean', 'all' ] );
52
+ const { environment, spinner } = env.clean.mock.calls[ 0 ][ 0 ];
53
+ expect( environment ).toBe( 'all' );
54
+ expect( spinner.text ).toBe( '' );
55
+ } );
56
+ it( 'parses clean commands for the development environment.', () => {
57
+ cli().parse( [ 'clean', 'development' ] );
58
+ const { environment, spinner } = env.clean.mock.calls[ 0 ][ 0 ];
59
+ expect( environment ).toBe( 'development' );
60
+ expect( spinner.text ).toBe( '' );
61
+ } );
62
+ it( 'parses clean commands for the tests environment.', () => {
63
+ cli().parse( [ 'clean', 'tests' ] );
64
+ const { environment, spinner } = env.clean.mock.calls[ 0 ][ 0 ];
65
+ expect( environment ).toBe( 'tests' );
66
+ expect( spinner.text ).toBe( '' );
67
+ } );
68
+
69
+ it( 'parses run commands without arguments.', () => {
70
+ cli().parse( [ 'run', 'tests-wordpress', 'test' ] );
71
+ const { container, command, spinner } = env.run.mock.calls[ 0 ][ 0 ];
72
+ expect( container ).toBe( 'tests-wordpress' );
73
+ expect( command ).toStrictEqual( [ 'test' ] );
74
+ expect( spinner.text ).toBe( '' );
75
+ } );
76
+ it( 'parses run commands with variadic arguments.', () => {
77
+ cli().parse( [ 'run', 'tests-wordpress', 'test', 'test1', '--test2' ] );
78
+ const { container, command, spinner } = env.run.mock.calls[ 0 ][ 0 ];
79
+ expect( container ).toBe( 'tests-wordpress' );
80
+ expect( command ).toStrictEqual( [ 'test', 'test1', '--test2' ] );
81
+ expect( spinner.text ).toBe( '' );
82
+ } );
83
+
84
+ it( 'handles successful commands with messages.', async () => {
85
+ env.start.mockResolvedValueOnce( 'success message' );
86
+ cli().parse( [ 'start' ] );
87
+ const { spinner } = env.start.mock.calls[ 0 ][ 0 ];
88
+ await env.start.mock.results[ 0 ].value;
89
+ expect( spinner.succeed ).toHaveBeenCalledWith(
90
+ expect.stringMatching( /^success message \(in \d+s \d+ms\)$/ )
91
+ );
92
+ } );
93
+ it( 'handles successful commands with spinner text.', async () => {
94
+ env.start.mockResolvedValueOnce();
95
+ cli().parse( [ 'start' ] );
96
+ const { spinner } = env.start.mock.calls[ 0 ][ 0 ];
97
+ spinner.text = 'success spinner text';
98
+ await env.start.mock.results[ 0 ].value;
99
+ expect( spinner.succeed ).toHaveBeenCalledWith(
100
+ expect.stringMatching( /^success spinner text \(in \d+s \d+ms\)$/ )
101
+ );
102
+ } );
103
+
104
+ it( 'handles failed commands with messages.', async () => {
105
+ env.start.mockRejectedValueOnce( {
106
+ message: 'failure message',
107
+ } );
108
+ const consoleError = console.error;
109
+ console.error = jest.fn();
110
+ const processExit = process.exit;
111
+ process.exit = jest.fn();
112
+
113
+ cli().parse( [ 'start' ] );
114
+ const { spinner } = env.start.mock.calls[ 0 ][ 0 ];
115
+ await env.start.mock.results[ 0 ].value.catch( () => {} );
116
+
117
+ expect( spinner.fail ).toHaveBeenCalledWith( 'failure message' );
118
+ expect( console.error ).toHaveBeenCalled();
119
+ expect( process.exit ).toHaveBeenCalledWith( 1 );
120
+ console.error = consoleError;
121
+ process.exit = processExit;
122
+ } );
123
+ it( 'handles failed docker commands with errors.', async () => {
124
+ env.start.mockRejectedValueOnce( {
125
+ err: 'failure error',
126
+ out: 'message',
127
+ exitCode: 1,
128
+ } );
129
+ const consoleError = console.error;
130
+ console.error = jest.fn();
131
+ const processExit = process.exit;
132
+ process.exit = jest.fn();
133
+ const stderr = process.stderr.write;
134
+ process.stderr.write = jest.fn();
135
+
136
+ cli().parse( [ 'start' ] );
137
+ const { spinner } = env.start.mock.calls[ 0 ][ 0 ];
138
+ await env.start.mock.results[ 0 ].value.catch( () => {} );
139
+
140
+ expect( spinner.fail ).toHaveBeenCalledWith(
141
+ 'Error while running docker-compose command.'
142
+ );
143
+ expect( process.stderr.write ).toHaveBeenCalledWith( 'failure error' );
144
+ expect( process.exit ).toHaveBeenCalledWith( 1 );
145
+ console.error = consoleError;
146
+ process.exit = processExit;
147
+ process.stderr.write = stderr;
148
+ } );
149
+ } );
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+ /* eslint-disable jest/no-conditional-expect */
3
+ /**
4
+ * Internal dependencies
5
+ */
6
+ const {
7
+ LifecycleScriptError,
8
+ executeLifecycleScript,
9
+ } = require( '../execute-lifecycle-script' );
10
+
11
+ describe( 'executeLifecycleScript', () => {
12
+ const spinner = {
13
+ info: jest.fn(),
14
+ };
15
+
16
+ afterEach( () => {
17
+ jest.clearAllMocks();
18
+ } );
19
+
20
+ it( 'should do nothing without event option when debugging', async () => {
21
+ await executeLifecycleScript(
22
+ 'test',
23
+ { lifecycleScripts: { test: null }, debug: true },
24
+ spinner
25
+ );
26
+
27
+ expect( spinner.info ).not.toHaveBeenCalled();
28
+ } );
29
+
30
+ it( 'should run event option and print output when debugging', async () => {
31
+ await executeLifecycleScript(
32
+ 'test',
33
+ { lifecycleScripts: { test: 'node -v' }, debug: true },
34
+ spinner
35
+ );
36
+
37
+ expect( spinner.info ).toHaveBeenCalledWith(
38
+ expect.stringMatching( /test Script:\nv[0-9]/ )
39
+ );
40
+ } );
41
+
42
+ it( 'should throw LifecycleScriptError when process errors', async () => {
43
+ try {
44
+ await executeLifecycleScript(
45
+ 'test',
46
+ {
47
+ lifecycleScripts: {
48
+ test: 'node -vvvvvvv',
49
+ },
50
+ },
51
+ spinner
52
+ );
53
+ } catch ( error ) {
54
+ expect( error ).toBeInstanceOf( LifecycleScriptError );
55
+ expect( error.message ).toMatch( /test Error:\n.*bad option/ );
56
+ }
57
+ } );
58
+ } );
59
+ /* eslint-enable jest/no-conditional-expect */
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+ /**
3
+ * Internal dependencies
4
+ */
5
+ const md5 = require( '../md5' );
6
+
7
+ describe( 'md5', () => {
8
+ it( 'creates a hash of a string', () => {
9
+ const result = md5( 'hello' );
10
+ expect( result ).toMatchSnapshot();
11
+ } );
12
+ it( 'creates a hash of a object', () => {
13
+ const result = md5( { foo: 'xyz', test: { anotherFoo: 123 } } );
14
+ expect( result ).toMatchSnapshot();
15
+ } );
16
+ it( 'creates a hash of a number', () => {
17
+ const result = md5( 123789 );
18
+ expect( result ).toMatchSnapshot();
19
+ } );
20
+ it( 'creates a hash of null', () => {
21
+ const result = md5( null );
22
+ expect( result ).toMatchSnapshot();
23
+ } );
24
+ it( 'uses the same hash for the same values', () => {
25
+ const testObj = {
26
+ test: 1,
27
+ otherTest: {
28
+ foo: 'hello',
29
+ },
30
+ };
31
+ const result1 = md5( testObj );
32
+ const result2 = md5( { ...testObj } );
33
+ expect( result1 ).toBe( result2 );
34
+ } );
35
+ } );
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+ /**
3
+ * Internal dependencies
4
+ */
5
+ const parseXdebugMode = require( '../parse-xdebug-mode' );
6
+
7
+ describe( 'parseXdebugMode', () => {
8
+ it( 'throws an error if the passed value is neither a string nor undefined', () => {
9
+ const errorMessage = 'is not a mode recognized by Xdebug';
10
+ expect( () => parseXdebugMode( true ) ).toThrow( errorMessage );
11
+ expect( () => parseXdebugMode( false ) ).toThrow( errorMessage );
12
+ expect( () => parseXdebugMode( 1 ) ).toThrow( errorMessage );
13
+ } );
14
+
15
+ it( 'sets the Xdebug mode to "off" if no --xdebug flag is passed', () => {
16
+ const result = parseXdebugMode( undefined );
17
+ expect( result ).toEqual( 'off' );
18
+ } );
19
+
20
+ it( 'sets the Xdebug mode to "debug" if no mode is specified', () => {
21
+ const result = parseXdebugMode( '' );
22
+ expect( result ).toEqual( 'debug' );
23
+ } );
24
+
25
+ it( 'throws an error if a given mode is not recognized, including the invalid mode in the output', () => {
26
+ const fakeMode = 'fake-mode-123';
27
+ expect.assertions( 2 );
28
+ // Single mode:
29
+ expect( () => parseXdebugMode( fakeMode ) ).toThrow( fakeMode );
30
+
31
+ // Many modes:
32
+ expect( () =>
33
+ parseXdebugMode( `debug,profile,${ fakeMode }` )
34
+ ).toThrow( fakeMode );
35
+ } );
36
+
37
+ it( 'returns all modes passed', () => {
38
+ const result = parseXdebugMode( 'debug,profile,trace' );
39
+ expect( result ).toEqual( 'debug,profile,trace' );
40
+ } );
41
+ } );
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * A list of the containers that we can use `run` on.
5
+ */
6
+ const RUN_CONTAINERS = [
7
+ 'mysql',
8
+ 'tests-mysql',
9
+ 'wordpress',
10
+ 'tests-wordpress',
11
+ 'cli',
12
+ 'tests-cli',
13
+ ];
14
+
15
+ /**
16
+ * Custom parsing and validation for the "run" command's container argument.
17
+ *
18
+ * @param {string} value The user-set container.
19
+ *
20
+ * @return {string} The container name to use.
21
+ */
22
+ function validateRunContainer( value ) {
23
+ // Give special errors for deprecated containers.
24
+ if ( value === 'phpunit' ) {
25
+ throw new Error(
26
+ "The 'phpunit' container has been removed. Please use 'wp-env run tests-cli --env-cwd=wp-content/path/to/plugin phpunit' instead."
27
+ );
28
+ }
29
+ if ( value === 'composer' ) {
30
+ throw new Error(
31
+ "The 'composer' container has been removed. Please use 'wp-env run cli --env-cwd=wp-content/path/to/plugin composer' instead."
32
+ );
33
+ }
34
+
35
+ return value;
36
+ }
37
+
38
+ module.exports = {
39
+ RUN_CONTAINERS,
40
+ validateRunContainer,
41
+ };
package/lib/wordpress.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
  /**
2
3
  * External dependencies
3
4
  */
@@ -14,7 +15,7 @@ const copyDir = util.promisify( require( 'copy-dir' ) );
14
15
 
15
16
  /**
16
17
  * @typedef {import('./config').WPConfig} WPConfig
17
- * @typedef {import('./config').WPServiceConfig} WPServiceConfig
18
+ * @typedef {import('./config').WPEnvironmentConfig} WPEnvironmentConfig
18
19
  * @typedef {import('./config').WPSource} WPSource
19
20
  * @typedef {'development'|'tests'} WPEnvironment
20
21
  * @typedef {'development'|'tests'|'all'} WPEnvironmentSelection
@@ -86,6 +87,7 @@ async function configureWordPress( environment, config, spinner ) {
86
87
  [ 'bash', '-c', setupCommands.join( ' && ' ) ],
87
88
  {
88
89
  config: config.dockerComposeConfigPath,
90
+ commandOptions: [ '--rm' ],
89
91
  log: config.debug,
90
92
  }
91
93
  );
@@ -147,30 +149,13 @@ async function setupWordPressDirectories( config ) {
147
149
  config.env.development.coreSource.path,
148
150
  config.env.development.coreSource.testsPath
149
151
  );
150
- await createUploadsDir( config.env.development.coreSource.testsPath );
151
152
  }
152
-
153
- const checkedPaths = {};
154
- for ( const { coreSource } of Object.values( config.env ) ) {
155
- if ( coreSource && ! checkedPaths[ coreSource.path ] ) {
156
- await createUploadsDir( coreSource.path );
157
- checkedPaths[ coreSource.path ] = true;
158
- }
159
- }
160
- }
161
-
162
- async function createUploadsDir( corePath ) {
163
- // Ensure the tests uploads folder is writeable for travis,
164
- // creating the folder if necessary.
165
- const uploadPath = path.join( corePath, 'wp-content/uploads' );
166
- await fs.mkdir( uploadPath, { recursive: true } );
167
- await fs.chmod( uploadPath, 0o0767 );
168
153
  }
169
154
 
170
155
  /**
171
156
  * Returns true if all given environment configs have the same core source.
172
157
  *
173
- * @param {WPServiceConfig[]} envs An array of environments to check.
158
+ * @param {WPEnvironmentConfig[]} envs An array of environments to check.
174
159
  *
175
160
  * @return {boolean} True if all the environments have the same core source.
176
161
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/env",
3
- "version": "6.0.0",
3
+ "version": "8.0.0",
4
4
  "description": "A zero-config, self contained local WordPress environment for development and testing.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -51,5 +51,5 @@
51
51
  "scripts": {
52
52
  "test": "echo \"Error: run tests from root\" && exit 1"
53
53
  },
54
- "gitHead": "6df0c62d43b8901414ccd22ffbe56eaa99d012a6"
54
+ "gitHead": "c7c79cb11b677adcbf06cf5f8cfb6c5ec1699f19"
55
55
  }