@wordpress/env 7.0.0 → 8.1.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 +38 -40
- package/bin/wp-env +18 -2
- package/lib/cli.js +29 -8
- package/lib/commands/clean.js +2 -3
- package/lib/commands/destroy.js +11 -7
- package/lib/commands/run.js +28 -60
- package/lib/commands/start.js +5 -6
- package/lib/config/get-config-from-environment-vars.js +34 -14
- package/lib/config/load-config.js +2 -2
- package/lib/config/merge-configs.js +2 -1
- package/lib/config/parse-config.js +123 -40
- package/lib/config/post-process-config.js +3 -3
- package/lib/config/test/__snapshots__/config-integration.js.snap +28 -4
- package/lib/config/test/config-integration.js +29 -8
- package/lib/config/test/merge-configs.js +10 -0
- package/lib/config/test/parse-config.js +105 -54
- package/lib/config/test/parse-source-string.js +1 -1
- package/lib/config/test/post-process-config.js +6 -2
- package/lib/config/test/validate-config.js +80 -22
- package/lib/config/validate-config.js +34 -8
- package/lib/env.js +2 -2
- package/lib/execute-lifecycle-script.js +86 -0
- package/lib/get-host-user.js +4 -8
- package/lib/init-config.js +11 -6
- package/lib/test/cli.js +1 -1
- package/lib/test/execute-lifecycle-script.js +59 -0
- package/lib/validate-run-container.js +41 -0
- package/package.json +2 -2
- package/lib/execute-after-setup.js +0 -51
- package/lib/test/execute-after-setup.js +0 -66
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/* eslint-disable jest/no-conditional-expect */
|
|
3
|
-
/**
|
|
4
|
-
* External dependencies
|
|
5
|
-
*/
|
|
6
|
-
const path = require( 'path' );
|
|
7
|
-
|
|
8
3
|
/**
|
|
9
4
|
* Internal dependencies
|
|
10
5
|
*/
|
|
@@ -40,6 +35,7 @@ const DEFAULT_CONFIG = {
|
|
|
40
35
|
pluginSources: [],
|
|
41
36
|
themeSources: [],
|
|
42
37
|
config: {
|
|
38
|
+
FS_METHOD: 'direct',
|
|
43
39
|
WP_DEBUG: true,
|
|
44
40
|
SCRIPT_DEBUG: true,
|
|
45
41
|
WP_ENVIRONMENT_TYPE: 'local',
|
|
@@ -51,7 +47,11 @@ const DEFAULT_CONFIG = {
|
|
|
51
47
|
WP_HOME: 'http://localhost',
|
|
52
48
|
},
|
|
53
49
|
mappings: {},
|
|
54
|
-
|
|
50
|
+
lifecycleScripts: {
|
|
51
|
+
afterStart: null,
|
|
52
|
+
afterClean: null,
|
|
53
|
+
afterDestroy: null,
|
|
54
|
+
},
|
|
55
55
|
env: {
|
|
56
56
|
development: {},
|
|
57
57
|
tests: {
|
|
@@ -76,11 +76,11 @@ describe( 'parseConfig', () => {
|
|
|
76
76
|
delete process.env.WP_ENV_TESTS_PORT;
|
|
77
77
|
delete process.env.WP_ENV_CORE;
|
|
78
78
|
delete process.env.WP_ENV_PHP_VERSION;
|
|
79
|
-
delete process.env.
|
|
79
|
+
delete process.env.WP_ENV_LIFECYCLE_SCRIPT_AFTER_START;
|
|
80
80
|
} );
|
|
81
81
|
|
|
82
82
|
it( 'should return default config', async () => {
|
|
83
|
-
const parsed = await parseConfig( '
|
|
83
|
+
const parsed = await parseConfig( '/test/gutenberg', '/cache' );
|
|
84
84
|
|
|
85
85
|
expect( parsed ).toEqual( DEFAULT_CONFIG );
|
|
86
86
|
} );
|
|
@@ -88,60 +88,44 @@ describe( 'parseConfig', () => {
|
|
|
88
88
|
it( 'should infer a core mounting default when ran from a WordPress directory', async () => {
|
|
89
89
|
detectDirectoryType.mockResolvedValue( 'core' );
|
|
90
90
|
|
|
91
|
-
const parsed = await parseConfig( '
|
|
91
|
+
const parsed = await parseConfig( '/test/gutenberg', '/cache' );
|
|
92
92
|
|
|
93
|
-
expect( parsed ).
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
basename: 'gutenberg',
|
|
97
|
-
path: path.resolve( '.' ),
|
|
98
|
-
testsPath: '/cache/tests-gutenberg',
|
|
99
|
-
type: 'local',
|
|
100
|
-
},
|
|
101
|
-
} );
|
|
93
|
+
expect( parsed.pluginSources ).toHaveLength( 0 );
|
|
94
|
+
expect( parsed.themeSources ).toHaveLength( 0 );
|
|
95
|
+
expect( parsed.coreSource ).toMatchObject( { type: 'local' } );
|
|
102
96
|
} );
|
|
103
97
|
|
|
104
98
|
it( 'should infer a plugin mounting default when ran from a plugin directory', async () => {
|
|
105
99
|
detectDirectoryType.mockResolvedValue( 'plugin' );
|
|
106
100
|
|
|
107
|
-
const parsed = await parseConfig( '
|
|
101
|
+
const parsed = await parseConfig( '/test/gutenberg', '/cache' );
|
|
108
102
|
|
|
109
|
-
expect( parsed ).
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
basename: 'gutenberg',
|
|
114
|
-
path: path.resolve( '.' ),
|
|
115
|
-
type: 'local',
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
} );
|
|
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' } );
|
|
119
107
|
} );
|
|
120
108
|
|
|
121
109
|
it( 'should infer a theme mounting default when ran from a theme directory', async () => {
|
|
122
110
|
detectDirectoryType.mockResolvedValue( 'theme' );
|
|
123
111
|
|
|
124
|
-
const parsed = await parseConfig( '
|
|
112
|
+
const parsed = await parseConfig( '/test/gutenberg', '/cache' );
|
|
125
113
|
|
|
126
|
-
expect( parsed ).
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
basename: 'gutenberg',
|
|
131
|
-
path: path.resolve( '.' ),
|
|
132
|
-
type: 'local',
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
} );
|
|
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' } );
|
|
136
118
|
} );
|
|
137
119
|
|
|
138
120
|
it( 'should merge configs with precedence', async () => {
|
|
139
121
|
readRawConfigFile.mockImplementation( async ( configFile ) => {
|
|
140
|
-
if ( configFile ===
|
|
122
|
+
if ( configFile === '/test/gutenberg/.wp-env.json' ) {
|
|
141
123
|
return {
|
|
142
124
|
core: 'WordPress/WordPress#Test',
|
|
143
125
|
phpVersion: '1.0',
|
|
144
|
-
|
|
126
|
+
lifecycleScripts: {
|
|
127
|
+
afterStart: 'test',
|
|
128
|
+
},
|
|
145
129
|
env: {
|
|
146
130
|
development: {
|
|
147
131
|
port: 1234,
|
|
@@ -153,9 +137,12 @@ describe( 'parseConfig', () => {
|
|
|
153
137
|
};
|
|
154
138
|
}
|
|
155
139
|
|
|
156
|
-
if ( configFile ===
|
|
140
|
+
if ( configFile === '/test/gutenberg/.wp-env.override.json' ) {
|
|
157
141
|
return {
|
|
158
142
|
phpVersion: '2.0',
|
|
143
|
+
lifecycleScripts: {
|
|
144
|
+
afterDestroy: 'test',
|
|
145
|
+
},
|
|
159
146
|
env: {
|
|
160
147
|
tests: {
|
|
161
148
|
port: 1011,
|
|
@@ -167,7 +154,7 @@ describe( 'parseConfig', () => {
|
|
|
167
154
|
throw new Error( 'Invalid File: ' + configFile );
|
|
168
155
|
} );
|
|
169
156
|
|
|
170
|
-
const parsed = await parseConfig( '
|
|
157
|
+
const parsed = await parseConfig( '/test/gutenberg', '/cache' );
|
|
171
158
|
|
|
172
159
|
const expected = {
|
|
173
160
|
...DEFAULT_CONFIG,
|
|
@@ -181,7 +168,11 @@ describe( 'parseConfig', () => {
|
|
|
181
168
|
type: 'git',
|
|
182
169
|
},
|
|
183
170
|
phpVersion: '2.0',
|
|
184
|
-
|
|
171
|
+
lifecycleScripts: {
|
|
172
|
+
...DEFAULT_CONFIG.lifecycleScripts,
|
|
173
|
+
afterStart: 'test',
|
|
174
|
+
afterDestroy: 'test',
|
|
175
|
+
},
|
|
185
176
|
env: {
|
|
186
177
|
development: {
|
|
187
178
|
...DEFAULT_CONFIG.env.development,
|
|
@@ -198,7 +189,7 @@ describe( 'parseConfig', () => {
|
|
|
198
189
|
|
|
199
190
|
it( 'should parse core, plugin, theme, and mapping sources', async () => {
|
|
200
191
|
readRawConfigFile.mockImplementation( async ( configFile ) => {
|
|
201
|
-
if ( configFile ===
|
|
192
|
+
if ( configFile === '/test/gutenberg/.wp-env.json' ) {
|
|
202
193
|
return {
|
|
203
194
|
core: 'WordPress/WordPress#Test',
|
|
204
195
|
plugins: [ 'WordPress/TestPlugin#Test' ],
|
|
@@ -210,16 +201,14 @@ describe( 'parseConfig', () => {
|
|
|
210
201
|
};
|
|
211
202
|
}
|
|
212
203
|
|
|
213
|
-
if (
|
|
214
|
-
configFile === path.resolve( '.', './.wp-env.override.json' )
|
|
215
|
-
) {
|
|
204
|
+
if ( configFile === '/test/gutenberg/.wp-env.override.json' ) {
|
|
216
205
|
return {};
|
|
217
206
|
}
|
|
218
207
|
|
|
219
208
|
throw new Error( 'Invalid File: ' + configFile );
|
|
220
209
|
} );
|
|
221
210
|
|
|
222
|
-
const parsed = await parseConfig( '
|
|
211
|
+
const parsed = await parseConfig( '/test/gutenberg', '/cache' );
|
|
223
212
|
|
|
224
213
|
expect( parsed ).toEqual( {
|
|
225
214
|
...DEFAULT_CONFIG,
|
|
@@ -270,9 +259,9 @@ describe( 'parseConfig', () => {
|
|
|
270
259
|
process.env.WP_ENV_TESTS_PORT = 456;
|
|
271
260
|
process.env.WP_ENV_CORE = 'WordPress/WordPress#test';
|
|
272
261
|
process.env.WP_ENV_PHP_VERSION = '3.0';
|
|
273
|
-
process.env.
|
|
262
|
+
process.env.WP_ENV_LIFECYCLE_SCRIPT_AFTER_START = 'test after';
|
|
274
263
|
|
|
275
|
-
const parsed = await parseConfig( '
|
|
264
|
+
const parsed = await parseConfig( '/test/gutenberg', '/cache' );
|
|
276
265
|
|
|
277
266
|
expect( parsed ).toEqual( {
|
|
278
267
|
...DEFAULT_CONFIG,
|
|
@@ -288,7 +277,10 @@ describe( 'parseConfig', () => {
|
|
|
288
277
|
type: 'git',
|
|
289
278
|
},
|
|
290
279
|
phpVersion: '3.0',
|
|
291
|
-
|
|
280
|
+
lifecycleScripts: {
|
|
281
|
+
...DEFAULT_CONFIG.lifecycleScripts,
|
|
282
|
+
afterStart: 'test after',
|
|
283
|
+
},
|
|
292
284
|
env: {
|
|
293
285
|
development: {
|
|
294
286
|
port: 123,
|
|
@@ -329,7 +321,7 @@ describe( 'parseConfig', () => {
|
|
|
329
321
|
|
|
330
322
|
expect.assertions( 1 );
|
|
331
323
|
try {
|
|
332
|
-
await parseConfig( '
|
|
324
|
+
await parseConfig( '/test/gutenberg', '/cache' );
|
|
333
325
|
} catch ( error ) {
|
|
334
326
|
expect( error ).toEqual(
|
|
335
327
|
new ValidationError(
|
|
@@ -338,5 +330,64 @@ describe( 'parseConfig', () => {
|
|
|
338
330
|
);
|
|
339
331
|
}
|
|
340
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
|
+
} );
|
|
341
392
|
} );
|
|
342
393
|
/* eslint-enable jest/no-conditional-expect */
|
|
@@ -45,7 +45,7 @@ describe( 'parseSourceString', () => {
|
|
|
45
45
|
describe( 'local sources', () => {
|
|
46
46
|
it( 'should parse relative directories', () => {
|
|
47
47
|
expect( parseSourceString( '.', options ) ).toEqual( {
|
|
48
|
-
basename: '
|
|
48
|
+
basename: path.basename( path.resolve( '.' ) ),
|
|
49
49
|
path: path.resolve( '.' ),
|
|
50
50
|
type: 'local',
|
|
51
51
|
} );
|
|
@@ -156,7 +156,9 @@ describe( 'postProcessConfig', () => {
|
|
|
156
156
|
const processed = postProcessConfig( {
|
|
157
157
|
port: 8888,
|
|
158
158
|
testsPort: 8889,
|
|
159
|
-
|
|
159
|
+
lifecycleScripts: {
|
|
160
|
+
afterStart: 'test',
|
|
161
|
+
},
|
|
160
162
|
env: {
|
|
161
163
|
development: {},
|
|
162
164
|
tests: {},
|
|
@@ -166,7 +168,9 @@ describe( 'postProcessConfig', () => {
|
|
|
166
168
|
expect( processed ).toEqual( {
|
|
167
169
|
port: 8888,
|
|
168
170
|
testsPort: 8889,
|
|
169
|
-
|
|
171
|
+
lifecycleScripts: {
|
|
172
|
+
afterStart: 'test',
|
|
173
|
+
},
|
|
170
174
|
env: {
|
|
171
175
|
development: {
|
|
172
176
|
port: 8888,
|
|
@@ -117,7 +117,7 @@ describe( 'validate-config', () => {
|
|
|
117
117
|
describe( 'checkObjectWithValues', () => {
|
|
118
118
|
it( 'throws when not an object', () => {
|
|
119
119
|
expect( () =>
|
|
120
|
-
checkObjectWithValues( 'test.json', 'test', 'test', [] )
|
|
120
|
+
checkObjectWithValues( 'test.json', 'test', 'test', [], false )
|
|
121
121
|
).toThrow(
|
|
122
122
|
new ValidationError(
|
|
123
123
|
'Invalid test.json: "test" must be an object.'
|
|
@@ -125,7 +125,13 @@ describe( 'validate-config', () => {
|
|
|
125
125
|
);
|
|
126
126
|
|
|
127
127
|
expect( () =>
|
|
128
|
-
checkObjectWithValues(
|
|
128
|
+
checkObjectWithValues(
|
|
129
|
+
'test.json',
|
|
130
|
+
'test',
|
|
131
|
+
[ 'test' ],
|
|
132
|
+
[],
|
|
133
|
+
false
|
|
134
|
+
)
|
|
129
135
|
).toThrow(
|
|
130
136
|
new ValidationError(
|
|
131
137
|
'Invalid test.json: "test" must be an object.'
|
|
@@ -139,33 +145,42 @@ describe( 'validate-config', () => {
|
|
|
139
145
|
'test.json',
|
|
140
146
|
'test',
|
|
141
147
|
{ test: 'test' },
|
|
142
|
-
[]
|
|
148
|
+
[],
|
|
149
|
+
false
|
|
143
150
|
)
|
|
144
151
|
).toThrow(
|
|
145
152
|
new ValidationError(
|
|
146
|
-
'Invalid test.json: "test.test" must be
|
|
153
|
+
'Invalid test.json: "test.test" must be of type: .'
|
|
147
154
|
)
|
|
148
155
|
);
|
|
149
156
|
} );
|
|
150
157
|
|
|
151
158
|
it( 'throws when type is not allowed', () => {
|
|
152
159
|
expect( () =>
|
|
153
|
-
checkObjectWithValues(
|
|
154
|
-
'
|
|
155
|
-
|
|
160
|
+
checkObjectWithValues(
|
|
161
|
+
'test.json',
|
|
162
|
+
'test',
|
|
163
|
+
{ test: 'test' },
|
|
164
|
+
[ 'number' ],
|
|
165
|
+
false
|
|
166
|
+
)
|
|
156
167
|
).toThrow(
|
|
157
168
|
new ValidationError(
|
|
158
|
-
'Invalid test.json: "test.test" must be
|
|
169
|
+
'Invalid test.json: "test.test" must be of type: number.'
|
|
159
170
|
)
|
|
160
171
|
);
|
|
161
172
|
|
|
162
173
|
expect( () =>
|
|
163
|
-
checkObjectWithValues(
|
|
164
|
-
'
|
|
165
|
-
|
|
174
|
+
checkObjectWithValues(
|
|
175
|
+
'test.json',
|
|
176
|
+
'test',
|
|
177
|
+
{ test: 1 },
|
|
178
|
+
[ 'string' ],
|
|
179
|
+
false
|
|
180
|
+
)
|
|
166
181
|
).toThrow(
|
|
167
182
|
new ValidationError(
|
|
168
|
-
'Invalid test.json: "test.test" must be
|
|
183
|
+
'Invalid test.json: "test.test" must be of type: string.'
|
|
169
184
|
)
|
|
170
185
|
);
|
|
171
186
|
|
|
@@ -174,32 +189,65 @@ describe( 'validate-config', () => {
|
|
|
174
189
|
'test.json',
|
|
175
190
|
'test',
|
|
176
191
|
{ test: [ 'test' ] },
|
|
177
|
-
[ 'object', 'string' ]
|
|
192
|
+
[ 'object', 'string' ],
|
|
193
|
+
false
|
|
194
|
+
)
|
|
195
|
+
).toThrow(
|
|
196
|
+
new ValidationError(
|
|
197
|
+
'Invalid test.json: "test.test" must be of type: object or string.'
|
|
198
|
+
)
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect( () =>
|
|
202
|
+
checkObjectWithValues(
|
|
203
|
+
'test.json',
|
|
204
|
+
'test',
|
|
205
|
+
{ test: null },
|
|
206
|
+
[ 'object' ],
|
|
207
|
+
true
|
|
178
208
|
)
|
|
179
209
|
).toThrow(
|
|
180
210
|
new ValidationError(
|
|
181
|
-
'Invalid test.json: "test.test" must be
|
|
211
|
+
'Invalid test.json: "test.test" must be of type: object.'
|
|
182
212
|
)
|
|
183
213
|
);
|
|
184
214
|
} );
|
|
185
215
|
|
|
186
216
|
it( 'passes when type is allowed', () => {
|
|
187
217
|
expect( () =>
|
|
188
|
-
checkObjectWithValues(
|
|
189
|
-
'
|
|
190
|
-
|
|
218
|
+
checkObjectWithValues(
|
|
219
|
+
'test.json',
|
|
220
|
+
'test',
|
|
221
|
+
{ test: 'test' },
|
|
222
|
+
[ 'string' ],
|
|
223
|
+
false
|
|
224
|
+
)
|
|
225
|
+
).not.toThrow();
|
|
226
|
+
expect( () =>
|
|
227
|
+
checkObjectWithValues(
|
|
228
|
+
'test.json',
|
|
229
|
+
'test',
|
|
230
|
+
{ test: '' },
|
|
231
|
+
[ 'string' ],
|
|
232
|
+
true
|
|
233
|
+
)
|
|
191
234
|
).not.toThrow();
|
|
192
235
|
expect( () =>
|
|
193
|
-
checkObjectWithValues(
|
|
194
|
-
'
|
|
195
|
-
|
|
236
|
+
checkObjectWithValues(
|
|
237
|
+
'test.json',
|
|
238
|
+
'test',
|
|
239
|
+
{ test: 1 },
|
|
240
|
+
[ 'number' ],
|
|
241
|
+
false
|
|
242
|
+
)
|
|
196
243
|
).not.toThrow();
|
|
197
244
|
expect( () =>
|
|
198
245
|
checkObjectWithValues(
|
|
199
246
|
'test.json',
|
|
200
247
|
'test',
|
|
201
248
|
{ test: { nested: 'test' } },
|
|
202
|
-
[ 'object' ]
|
|
249
|
+
[ 'object' ],
|
|
250
|
+
false
|
|
203
251
|
)
|
|
204
252
|
).not.toThrow();
|
|
205
253
|
expect( () =>
|
|
@@ -207,7 +255,17 @@ describe( 'validate-config', () => {
|
|
|
207
255
|
'test.json',
|
|
208
256
|
'test',
|
|
209
257
|
{ test: [ 'test' ] },
|
|
210
|
-
[ 'array' ]
|
|
258
|
+
[ 'array' ],
|
|
259
|
+
false
|
|
260
|
+
)
|
|
261
|
+
).not.toThrow();
|
|
262
|
+
expect( () =>
|
|
263
|
+
checkObjectWithValues(
|
|
264
|
+
'test.json',
|
|
265
|
+
'test',
|
|
266
|
+
{ test: null },
|
|
267
|
+
[ 'null' ],
|
|
268
|
+
false
|
|
211
269
|
)
|
|
212
270
|
).not.toThrow();
|
|
213
271
|
} );
|
|
@@ -74,8 +74,15 @@ function checkStringArray( configFile, configKey, array ) {
|
|
|
74
74
|
* @param {string} configKey The configuration key we're validating.
|
|
75
75
|
* @param {string[]} obj The object that we're checking.
|
|
76
76
|
* @param {string[]} allowTypes The types that are allowed.
|
|
77
|
+
* @param {boolean} allowEmpty Indicates whether or not empty values are allowed.
|
|
77
78
|
*/
|
|
78
|
-
function checkObjectWithValues(
|
|
79
|
+
function checkObjectWithValues(
|
|
80
|
+
configFile,
|
|
81
|
+
configKey,
|
|
82
|
+
obj,
|
|
83
|
+
allowTypes,
|
|
84
|
+
allowEmpty
|
|
85
|
+
) {
|
|
79
86
|
if ( allowTypes === undefined ) {
|
|
80
87
|
allowTypes = [];
|
|
81
88
|
}
|
|
@@ -87,18 +94,37 @@ function checkObjectWithValues( configFile, configKey, obj, allowTypes ) {
|
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
for ( const key in obj ) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
// Some values need to be uniquely validated.
|
|
98
|
+
switch ( obj[ key ] ) {
|
|
99
|
+
case null:
|
|
100
|
+
case undefined: {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
default: {
|
|
105
|
+
if ( ! obj[ key ] && ! allowEmpty ) {
|
|
106
|
+
throw new ValidationError(
|
|
107
|
+
`Invalid ${ configFile }: "${ configKey }.${ key }" must not be empty.`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
94
111
|
}
|
|
95
112
|
|
|
96
|
-
//
|
|
97
|
-
|
|
113
|
+
// Some types need to be uniquely identified.
|
|
114
|
+
let type;
|
|
115
|
+
if ( obj[ key ] === undefined ) {
|
|
116
|
+
type = 'undefined';
|
|
117
|
+
} else if ( obj[ key ] === null ) {
|
|
118
|
+
type = 'null';
|
|
119
|
+
} else if ( Array.isArray( obj[ key ] ) ) {
|
|
120
|
+
type = 'array';
|
|
121
|
+
} else {
|
|
122
|
+
type = typeof obj[ key ];
|
|
123
|
+
}
|
|
98
124
|
|
|
99
125
|
if ( ! allowTypes.includes( type ) ) {
|
|
100
126
|
throw new ValidationError(
|
|
101
|
-
`Invalid ${ configFile }: "${ configKey }.${ key }" must be
|
|
127
|
+
`Invalid ${ configFile }: "${ configKey }.${ key }" must be of type: ${ allowTypes.join(
|
|
102
128
|
' or '
|
|
103
129
|
) }.`
|
|
104
130
|
);
|
package/lib/env.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* Internal dependencies
|
|
4
4
|
*/
|
|
5
5
|
const { ValidationError } = require( './config' );
|
|
6
|
-
const {
|
|
6
|
+
const { LifecycleScriptError } = require( './execute-lifecycle-script' );
|
|
7
7
|
const commands = require( './commands' );
|
|
8
8
|
|
|
9
9
|
module.exports = {
|
|
10
10
|
...commands,
|
|
11
11
|
ValidationError,
|
|
12
|
-
|
|
12
|
+
LifecycleScriptError,
|
|
13
13
|
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* External dependencies
|
|
4
|
+
*/
|
|
5
|
+
const { exec } = require( 'child_process' );
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('./config').WPConfig} WPConfig
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Error subtype which indicates that the lifecycle script failed.
|
|
13
|
+
*/
|
|
14
|
+
class LifecycleScriptError extends Error {
|
|
15
|
+
constructor( event, stderr ) {
|
|
16
|
+
super( `${ event } Error:\n${ stderr }` );
|
|
17
|
+
|
|
18
|
+
this.event = event;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Executes any defined life cycle script.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} event The lifecycle event to run the script for.
|
|
26
|
+
* @param {WPConfig} config The config object to use.
|
|
27
|
+
* @param {Object} spinner A CLI spinner which indciates progress.
|
|
28
|
+
*
|
|
29
|
+
* @return {Promise} Resolves when the script has completed and rejects when there is an error.
|
|
30
|
+
*/
|
|
31
|
+
function executeLifecycleScript( event, config, spinner ) {
|
|
32
|
+
if ( ! config.lifecycleScripts[ event ] ) {
|
|
33
|
+
return Promise.resolve();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return new Promise( ( resolve, reject ) => {
|
|
37
|
+
// We're going to append the script output to the spinner while it's executing.
|
|
38
|
+
const spinnerMessage = `Executing ${ event } Script`;
|
|
39
|
+
spinner.text = spinnerMessage;
|
|
40
|
+
|
|
41
|
+
// Execute the script asynchronously so that it won't block the spinner.
|
|
42
|
+
const childProc = exec( config.lifecycleScripts[ event ], {
|
|
43
|
+
encoding: 'utf-8',
|
|
44
|
+
stdio: 'pipe',
|
|
45
|
+
env: process.env,
|
|
46
|
+
} );
|
|
47
|
+
|
|
48
|
+
// Collect all of the output so that we can make use of it.
|
|
49
|
+
let output = '';
|
|
50
|
+
childProc.stdout.on( 'data', ( data ) => {
|
|
51
|
+
output += data;
|
|
52
|
+
|
|
53
|
+
// Keep the spinner updated with the command output.
|
|
54
|
+
spinner.text = `${ spinnerMessage }\n${ output }`;
|
|
55
|
+
} );
|
|
56
|
+
|
|
57
|
+
// Listen for any error output so we can display it if the command fails.
|
|
58
|
+
let error = '';
|
|
59
|
+
childProc.stderr.on( 'data', ( data ) => {
|
|
60
|
+
error += data;
|
|
61
|
+
} );
|
|
62
|
+
|
|
63
|
+
// Pass any process creation errors directly up.
|
|
64
|
+
childProc.on( 'error', reject );
|
|
65
|
+
|
|
66
|
+
// Handle the completion of the command based on whether it was successful or not.
|
|
67
|
+
childProc.on( 'exit', ( code ) => {
|
|
68
|
+
if ( code === 0 ) {
|
|
69
|
+
// Keep the output of the command in debug mode.
|
|
70
|
+
if ( config.debug ) {
|
|
71
|
+
spinner.info( `${ event } Script:\n${ output.trimEnd() }` );
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
resolve();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
reject( new LifecycleScriptError( event, error.trimEnd() ) );
|
|
79
|
+
} );
|
|
80
|
+
} );
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = {
|
|
84
|
+
LifecycleScriptError,
|
|
85
|
+
executeLifecycleScript,
|
|
86
|
+
};
|
package/lib/get-host-user.js
CHANGED
|
@@ -13,14 +13,10 @@ module.exports = function getHostUser() {
|
|
|
13
13
|
const hostUser = os.userInfo();
|
|
14
14
|
|
|
15
15
|
// On Windows the uid and gid will be -1. Since there isn't a great way to handle this,
|
|
16
|
-
// we're just going to
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// when dealing with this configuration that file ownership
|
|
21
|
-
// has the same kind of magic handling that macOS uses.
|
|
22
|
-
const uid = ( hostUser.uid === -1 ? 0 : hostUser.uid ).toString();
|
|
23
|
-
const gid = ( hostUser.gid === -1 ? 0 : hostUser.gid ).toString();
|
|
16
|
+
// we're just going to assign them to 1000. Docker Desktop already takes care of
|
|
17
|
+
// permission-related issues using magic, so this should be fine.
|
|
18
|
+
const uid = ( hostUser.uid === -1 ? 1000 : hostUser.uid ).toString();
|
|
19
|
+
const gid = ( hostUser.gid === -1 ? 1000 : hostUser.gid ).toString();
|
|
24
20
|
|
|
25
21
|
return {
|
|
26
22
|
name: hostUser.username,
|