@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,393 @@
1
+ 'use strict';
2
+ /* eslint-disable jest/no-conditional-expect */
3
+ /**
4
+ * Internal dependencies
5
+ */
6
+ const { parseConfig } = require( '../parse-config' );
7
+ const readRawConfigFile = require( '../read-raw-config-file' );
8
+ const { getLatestWordPressVersion } = require( '../../wordpress' );
9
+ const { ValidationError } = require( '../validate-config' );
10
+ const detectDirectoryType = require( '../detect-directory-type' );
11
+
12
+ jest.mock( 'got', () => jest.fn() );
13
+ jest.mock( '../read-raw-config-file', () => jest.fn() );
14
+ jest.mock( '../detect-directory-type', () => jest.fn() );
15
+ jest.mock( '../../wordpress', () => ( {
16
+ getLatestWordPressVersion: jest.fn(),
17
+ } ) );
18
+
19
+ /**
20
+ * Since our configurations are merged, we will want to refer to the parsed default config frequently.
21
+ */
22
+ const DEFAULT_CONFIG = {
23
+ port: 8888,
24
+ testsPort: 8889,
25
+ phpVersion: null,
26
+ coreSource: {
27
+ type: 'git',
28
+ url: 'https://github.com/WordPress/WordPress.git',
29
+ ref: '100.0.0',
30
+ path: '/cache/WordPress',
31
+ clonePath: '/cache/WordPress',
32
+ basename: 'WordPress',
33
+ testsPath: '/cache/tests-WordPress',
34
+ },
35
+ pluginSources: [],
36
+ themeSources: [],
37
+ config: {
38
+ FS_METHOD: 'direct',
39
+ WP_DEBUG: true,
40
+ SCRIPT_DEBUG: true,
41
+ WP_ENVIRONMENT_TYPE: 'local',
42
+ WP_PHP_BINARY: 'php',
43
+ WP_TESTS_EMAIL: 'admin@example.org',
44
+ WP_TESTS_TITLE: 'Test Blog',
45
+ WP_TESTS_DOMAIN: 'localhost',
46
+ WP_SITEURL: 'http://localhost',
47
+ WP_HOME: 'http://localhost',
48
+ },
49
+ mappings: {},
50
+ lifecycleScripts: {
51
+ afterStart: null,
52
+ afterClean: null,
53
+ afterDestroy: null,
54
+ },
55
+ env: {
56
+ development: {},
57
+ tests: {
58
+ config: {
59
+ WP_DEBUG: false,
60
+ SCRIPT_DEBUG: false,
61
+ },
62
+ },
63
+ },
64
+ };
65
+
66
+ describe( 'parseConfig', () => {
67
+ beforeEach( () => {
68
+ readRawConfigFile.mockResolvedValue( null );
69
+ detectDirectoryType.mockResolvedValue( null );
70
+ getLatestWordPressVersion.mockResolvedValue( '100.0.0' );
71
+ } );
72
+
73
+ afterEach( () => {
74
+ jest.clearAllMocks();
75
+ delete process.env.WP_ENV_PORT;
76
+ delete process.env.WP_ENV_TESTS_PORT;
77
+ delete process.env.WP_ENV_CORE;
78
+ delete process.env.WP_ENV_PHP_VERSION;
79
+ delete process.env.WP_ENV_LIFECYCLE_SCRIPT_AFTER_START;
80
+ } );
81
+
82
+ it( 'should return default config', async () => {
83
+ const parsed = await parseConfig( '/test/gutenberg', '/cache' );
84
+
85
+ expect( parsed ).toEqual( DEFAULT_CONFIG );
86
+ } );
87
+
88
+ it( 'should infer a core mounting default when ran from a WordPress directory', async () => {
89
+ detectDirectoryType.mockResolvedValue( 'core' );
90
+
91
+ const parsed = await parseConfig( '/test/gutenberg', '/cache' );
92
+
93
+ expect( parsed.pluginSources ).toHaveLength( 0 );
94
+ expect( parsed.themeSources ).toHaveLength( 0 );
95
+ expect( parsed.coreSource ).toMatchObject( { type: 'local' } );
96
+ } );
97
+
98
+ it( 'should infer a plugin mounting default when ran from a plugin directory', async () => {
99
+ detectDirectoryType.mockResolvedValue( 'plugin' );
100
+
101
+ const parsed = await parseConfig( '/test/gutenberg', '/cache' );
102
+
103
+ expect( parsed.coreSource ).toMatchObject( { type: 'git' } );
104
+ expect( parsed.themeSources ).toHaveLength( 0 );
105
+ expect( parsed.pluginSources ).toHaveLength( 1 );
106
+ expect( parsed.pluginSources[ 0 ] ).toMatchObject( { type: 'local' } );
107
+ } );
108
+
109
+ it( 'should infer a theme mounting default when ran from a theme directory', async () => {
110
+ detectDirectoryType.mockResolvedValue( 'theme' );
111
+
112
+ const parsed = await parseConfig( '/test/gutenberg', '/cache' );
113
+
114
+ expect( parsed.coreSource ).toMatchObject( { type: 'git' } );
115
+ expect( parsed.pluginSources ).toHaveLength( 0 );
116
+ expect( parsed.themeSources ).toHaveLength( 1 );
117
+ expect( parsed.themeSources[ 0 ] ).toMatchObject( { type: 'local' } );
118
+ } );
119
+
120
+ it( 'should merge configs with precedence', async () => {
121
+ readRawConfigFile.mockImplementation( async ( configFile ) => {
122
+ if ( configFile === '/test/gutenberg/.wp-env.json' ) {
123
+ return {
124
+ core: 'WordPress/WordPress#Test',
125
+ phpVersion: '1.0',
126
+ lifecycleScripts: {
127
+ afterStart: 'test',
128
+ },
129
+ env: {
130
+ development: {
131
+ port: 1234,
132
+ },
133
+ tests: {
134
+ port: 5678,
135
+ },
136
+ },
137
+ };
138
+ }
139
+
140
+ if ( configFile === '/test/gutenberg/.wp-env.override.json' ) {
141
+ return {
142
+ phpVersion: '2.0',
143
+ lifecycleScripts: {
144
+ afterDestroy: 'test',
145
+ },
146
+ env: {
147
+ tests: {
148
+ port: 1011,
149
+ },
150
+ },
151
+ };
152
+ }
153
+
154
+ throw new Error( 'Invalid File: ' + configFile );
155
+ } );
156
+
157
+ const parsed = await parseConfig( '/test/gutenberg', '/cache' );
158
+
159
+ const expected = {
160
+ ...DEFAULT_CONFIG,
161
+ coreSource: {
162
+ basename: 'WordPress',
163
+ path: '/cache/WordPress',
164
+ clonePath: '/cache/WordPress',
165
+ ref: 'Test',
166
+ testsPath: '/cache/tests-WordPress',
167
+ url: 'https://github.com/WordPress/WordPress.git',
168
+ type: 'git',
169
+ },
170
+ phpVersion: '2.0',
171
+ lifecycleScripts: {
172
+ ...DEFAULT_CONFIG.lifecycleScripts,
173
+ afterStart: 'test',
174
+ afterDestroy: 'test',
175
+ },
176
+ env: {
177
+ development: {
178
+ ...DEFAULT_CONFIG.env.development,
179
+ port: 1234,
180
+ },
181
+ tests: {
182
+ ...DEFAULT_CONFIG.env.tests,
183
+ port: 1011,
184
+ },
185
+ },
186
+ };
187
+ expect( parsed ).toEqual( expected );
188
+ } );
189
+
190
+ it( 'should parse core, plugin, theme, and mapping sources', async () => {
191
+ readRawConfigFile.mockImplementation( async ( configFile ) => {
192
+ if ( configFile === '/test/gutenberg/.wp-env.json' ) {
193
+ return {
194
+ core: 'WordPress/WordPress#Test',
195
+ plugins: [ 'WordPress/TestPlugin#Test' ],
196
+ themes: [ 'WordPress/TestTheme#Test' ],
197
+ mappings: {
198
+ '/var/www/html/wp-content/plugins/test-mapping':
199
+ 'WordPress/TestMapping#Test',
200
+ },
201
+ };
202
+ }
203
+
204
+ if ( configFile === '/test/gutenberg/.wp-env.override.json' ) {
205
+ return {};
206
+ }
207
+
208
+ throw new Error( 'Invalid File: ' + configFile );
209
+ } );
210
+
211
+ const parsed = await parseConfig( '/test/gutenberg', '/cache' );
212
+
213
+ expect( parsed ).toEqual( {
214
+ ...DEFAULT_CONFIG,
215
+ coreSource: {
216
+ basename: 'WordPress',
217
+ path: '/cache/WordPress',
218
+ clonePath: '/cache/WordPress',
219
+ ref: 'Test',
220
+ testsPath: '/cache/tests-WordPress',
221
+ url: 'https://github.com/WordPress/WordPress.git',
222
+ type: 'git',
223
+ },
224
+ pluginSources: [
225
+ {
226
+ basename: 'TestPlugin',
227
+ path: '/cache/TestPlugin',
228
+ clonePath: '/cache/TestPlugin',
229
+ ref: 'Test',
230
+ url: 'https://github.com/WordPress/TestPlugin.git',
231
+ type: 'git',
232
+ },
233
+ ],
234
+ themeSources: [
235
+ {
236
+ basename: 'TestTheme',
237
+ path: '/cache/TestTheme',
238
+ clonePath: '/cache/TestTheme',
239
+ ref: 'Test',
240
+ url: 'https://github.com/WordPress/TestTheme.git',
241
+ type: 'git',
242
+ },
243
+ ],
244
+ mappings: {
245
+ '/var/www/html/wp-content/plugins/test-mapping': {
246
+ basename: 'TestMapping',
247
+ path: '/cache/TestMapping',
248
+ clonePath: '/cache/TestMapping',
249
+ ref: 'Test',
250
+ url: 'https://github.com/WordPress/TestMapping.git',
251
+ type: 'git',
252
+ },
253
+ },
254
+ } );
255
+ } );
256
+
257
+ it( 'should override with environment variables', async () => {
258
+ process.env.WP_ENV_PORT = 123;
259
+ process.env.WP_ENV_TESTS_PORT = 456;
260
+ process.env.WP_ENV_CORE = 'WordPress/WordPress#test';
261
+ process.env.WP_ENV_PHP_VERSION = '3.0';
262
+ process.env.WP_ENV_LIFECYCLE_SCRIPT_AFTER_START = 'test after';
263
+
264
+ const parsed = await parseConfig( '/test/gutenberg', '/cache' );
265
+
266
+ expect( parsed ).toEqual( {
267
+ ...DEFAULT_CONFIG,
268
+ port: 123,
269
+ testsPort: 456,
270
+ coreSource: {
271
+ basename: 'WordPress',
272
+ path: '/cache/WordPress',
273
+ clonePath: '/cache/WordPress',
274
+ ref: 'test',
275
+ testsPath: '/cache/tests-WordPress',
276
+ url: 'https://github.com/WordPress/WordPress.git',
277
+ type: 'git',
278
+ },
279
+ phpVersion: '3.0',
280
+ lifecycleScripts: {
281
+ ...DEFAULT_CONFIG.lifecycleScripts,
282
+ afterStart: 'test after',
283
+ },
284
+ env: {
285
+ development: {
286
+ port: 123,
287
+ phpVersion: '3.0',
288
+ coreSource: {
289
+ basename: 'WordPress',
290
+ path: '/cache/WordPress',
291
+ clonePath: '/cache/WordPress',
292
+ ref: 'test',
293
+ testsPath: '/cache/tests-WordPress',
294
+ url: 'https://github.com/WordPress/WordPress.git',
295
+ type: 'git',
296
+ },
297
+ },
298
+ tests: {
299
+ port: 456,
300
+ phpVersion: '3.0',
301
+ coreSource: {
302
+ basename: 'WordPress',
303
+ path: '/cache/WordPress',
304
+ clonePath: '/cache/WordPress',
305
+ ref: 'test',
306
+ testsPath: '/cache/tests-WordPress',
307
+ url: 'https://github.com/WordPress/WordPress.git',
308
+ type: 'git',
309
+ },
310
+ config: {
311
+ WP_DEBUG: false,
312
+ SCRIPT_DEBUG: false,
313
+ },
314
+ },
315
+ },
316
+ } );
317
+ } );
318
+
319
+ it( 'throws when latest WordPress version needed but not found', async () => {
320
+ getLatestWordPressVersion.mockResolvedValue( null );
321
+
322
+ expect.assertions( 1 );
323
+ try {
324
+ await parseConfig( '/test/gutenberg', '/cache' );
325
+ } catch ( error ) {
326
+ expect( error ).toEqual(
327
+ new ValidationError(
328
+ 'Could not find the latest WordPress version. There may be a network issue.'
329
+ )
330
+ );
331
+ }
332
+ } );
333
+
334
+ it( 'throws for unknown config options', async () => {
335
+ readRawConfigFile.mockImplementation( async ( configFile ) => {
336
+ if ( configFile === '/test/gutenberg/.wp-env.json' ) {
337
+ return {
338
+ test: 'test',
339
+ };
340
+ }
341
+
342
+ if ( configFile === '/test/gutenberg/.wp-env.override.json' ) {
343
+ return {};
344
+ }
345
+
346
+ throw new Error( 'Invalid File: ' + configFile );
347
+ } );
348
+
349
+ expect.assertions( 1 );
350
+ try {
351
+ await parseConfig( '/test/gutenberg', '/cache' );
352
+ } catch ( error ) {
353
+ expect( error ).toEqual(
354
+ new ValidationError(
355
+ `Invalid /test/gutenberg/.wp-env.json: "test" is not a configuration option.`
356
+ )
357
+ );
358
+ }
359
+ } );
360
+
361
+ it( 'throws for root-only config options', async () => {
362
+ readRawConfigFile.mockImplementation( async ( configFile ) => {
363
+ if ( configFile === '/test/gutenberg/.wp-env.json' ) {
364
+ return {
365
+ env: {
366
+ development: {
367
+ // Only the root can have environment-specific configurations.
368
+ env: {},
369
+ },
370
+ },
371
+ };
372
+ }
373
+
374
+ if ( configFile === '/test/gutenberg/.wp-env.override.json' ) {
375
+ return {};
376
+ }
377
+
378
+ throw new Error( 'Invalid File: ' + configFile );
379
+ } );
380
+
381
+ expect.assertions( 1 );
382
+ try {
383
+ await parseConfig( '/test/gutenberg', '/cache' );
384
+ } catch ( error ) {
385
+ expect( error ).toEqual(
386
+ new ValidationError(
387
+ `Invalid /test/gutenberg/.wp-env.json: "development.env" is not a configuration option.`
388
+ )
389
+ );
390
+ }
391
+ } );
392
+ } );
393
+ /* eslint-enable jest/no-conditional-expect */
@@ -0,0 +1,154 @@
1
+ 'use strict';
2
+ /* eslint-disable jest/no-conditional-expect */
3
+ /**
4
+ * External dependencies
5
+ */
6
+ const path = require( 'path' );
7
+ const { homedir } = require( 'os' );
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ const { ValidationError } = require( '../validate-config' );
13
+ const { parseSourceString } = require( '../parse-source-string' );
14
+
15
+ jest.mock( 'os', () => ( {
16
+ homedir: jest.fn(),
17
+ } ) );
18
+
19
+ describe( 'parseSourceString', () => {
20
+ const options = {
21
+ cacheDirectoryPath: '/test/cache',
22
+ };
23
+
24
+ beforeEach( () => {
25
+ homedir.mockReturnValue( '/home/test' );
26
+ } );
27
+
28
+ it( 'should do nothing when given an empty source', () => {
29
+ expect( parseSourceString( null, options ) ).toEqual( null );
30
+ } );
31
+
32
+ it( 'should throw when source not parseable', () => {
33
+ expect.assertions( 1 );
34
+ try {
35
+ parseSourceString( 'test://test', options );
36
+ } catch ( error ) {
37
+ expect( error ).toEqual(
38
+ new ValidationError(
39
+ 'Invalid or unrecognized source: "test://test".'
40
+ )
41
+ );
42
+ }
43
+ } );
44
+
45
+ describe( 'local sources', () => {
46
+ it( 'should parse relative directories', () => {
47
+ expect( parseSourceString( '.', options ) ).toEqual( {
48
+ basename: path.basename( path.resolve( '.' ) ),
49
+ path: path.resolve( '.' ),
50
+ type: 'local',
51
+ } );
52
+ } );
53
+
54
+ it( 'should parse home directories', () => {
55
+ expect( parseSourceString( '~/test', options ) ).toEqual( {
56
+ basename: 'test',
57
+ path: '/home/test/test',
58
+ type: 'local',
59
+ } );
60
+ } );
61
+
62
+ it( 'should parse absolute directories', () => {
63
+ expect( parseSourceString( '/absolute/test', options ) ).toEqual( {
64
+ basename: 'test',
65
+ path: '/absolute/test',
66
+ type: 'local',
67
+ } );
68
+ } );
69
+ } );
70
+
71
+ describe( 'zip sources', () => {
72
+ it( 'should parse WordPress.org sources', () => {
73
+ expect(
74
+ parseSourceString(
75
+ 'http://downloads.wordpress.org/plugin/gutenberg.zip',
76
+ options
77
+ )
78
+ ).toEqual( {
79
+ basename: 'gutenberg',
80
+ path: '/test/cache/gutenberg',
81
+ type: 'zip',
82
+ url: 'http://downloads.wordpress.org/plugin/gutenberg.zip',
83
+ } );
84
+ } );
85
+
86
+ it( 'should parse other sources', () => {
87
+ expect(
88
+ parseSourceString( 'http://test.com/testing.zip', options )
89
+ ).toEqual( {
90
+ basename: 'testing',
91
+ path: '/test/cache/testing',
92
+ type: 'zip',
93
+ url: 'http://test.com/testing.zip',
94
+ } );
95
+ } );
96
+ } );
97
+
98
+ describe( 'Git SSH sources', () => {
99
+ it( 'should parse ssh protocol', () => {
100
+ expect(
101
+ parseSourceString( 'ssh://test/test.git#trunk', options )
102
+ ).toEqual( {
103
+ basename: 'test',
104
+ path: '/test/cache/test',
105
+ clonePath: '/test/cache/test',
106
+ ref: 'trunk',
107
+ type: 'git',
108
+ url: 'ssh://test/test.git',
109
+ } );
110
+ } );
111
+
112
+ it( 'should parse git+ssh protocol', () => {
113
+ expect(
114
+ parseSourceString( 'git+ssh://test/test.git#trunk', options )
115
+ ).toEqual( {
116
+ basename: 'test',
117
+ path: '/test/cache/test',
118
+ clonePath: '/test/cache/test',
119
+ ref: 'trunk',
120
+ type: 'git',
121
+ url: 'git+ssh://test/test.git',
122
+ } );
123
+ } );
124
+
125
+ it( 'should work without ref', () => {
126
+ expect(
127
+ parseSourceString( 'ssh://test/test.git', options )
128
+ ).toEqual( {
129
+ basename: 'test',
130
+ path: '/test/cache/test',
131
+ clonePath: '/test/cache/test',
132
+ ref: undefined,
133
+ type: 'git',
134
+ url: 'ssh://test/test.git',
135
+ } );
136
+ } );
137
+ } );
138
+
139
+ describe( 'GitHub sources', () => {
140
+ it( 'should parse', () => {
141
+ expect(
142
+ parseSourceString( 'WordPress/gutenberg#trunk', options )
143
+ ).toEqual( {
144
+ basename: 'gutenberg',
145
+ path: '/test/cache/gutenberg',
146
+ clonePath: '/test/cache/gutenberg',
147
+ ref: 'trunk',
148
+ type: 'git',
149
+ url: 'https://github.com/WordPress/gutenberg.git',
150
+ } );
151
+ } );
152
+ } );
153
+ } );
154
+ /* eslint-enable jest/no-conditional-expect */