@@ -17,6 +17,9 @@ const VERBOSE_LOGS = !!process.env['VERBOSE_LOGS'];
17
17
// Set to true if you want the /tmp folder created to persist after running `bazel test`
18
18
const KEEP_TMP = false ;
19
19
20
+ // bazelisk requires a $HOME environment variable for its cache
21
+ process . env [ 'HOME' ] = tmp . dirSync ( { keep : KEEP_TMP , unsafeCleanup : ! KEEP_TMP } ) . name ;
22
+
20
23
function fail ( ...m ) {
21
24
console . error ( ) ;
22
25
console . error ( `[${ path . basename ( __filename ) } ]` ) ;
@@ -131,6 +134,22 @@ function copyToTmp(files) {
131
134
tmp . dirSync ( { keep : KEEP_TMP , unsafeCleanup : ! KEEP_TMP } ) . name ) ;
132
135
}
133
136
137
+ /**
138
+ * Expands environment variables in a string of the form ${FOO_BAR}.
139
+ */
140
+ function expandEnv ( s ) {
141
+ if ( ! s ) return s ;
142
+ const reg = / \$ \{ ( \w + ) \} / g;
143
+ return s . replace ( reg , ( matched ) => {
144
+ const varName = matched . substring ( 2 , matched . length - 1 ) ;
145
+ if ( process . env . hasOwnProperty ( varName ) ) {
146
+ return process . env [ varName ] ;
147
+ } else {
148
+ throw `Failed to expand unbound environment variable '${ varName } ' in '${ s } '` ;
149
+ }
150
+ } ) ;
151
+ }
152
+
134
153
/**
135
154
* TestRunner handles setting up the integration test and executing
136
155
* the test commands based on the config.
@@ -153,31 +172,37 @@ class TestRunner {
153
172
// and quoted arguments that contain spaces
154
173
const split = command . split ( ' ' ) ;
155
174
let binary = split [ 0 ] ;
156
- const args = split . slice ( 1 ) ;
175
+ const args = split . slice ( 1 ) . map ( a => expandEnv ( a ) ) ;
157
176
switch ( binary ) {
158
177
case 'patch-package-json' : {
159
178
let packageJsonFile = 'package.json' ;
160
179
if ( args . length > 0 ) {
161
180
packageJsonFile = args [ 0 ] ;
162
181
}
163
- log ( `running test command ${ this . successful + 1 } of ${ this . config . commands . length } : patching '${ packageJsonFile } ' in '${ this . testRoot } '` ) ;
182
+ log ( `running test command ${ this . successful + 1 } of ${
183
+ this . config . commands . length } : patching '${ packageJsonFile } ' in '${ this . testRoot } '`) ;
164
184
this . _patchPackageJson ( packageJsonFile ) ;
165
185
} break ;
166
186
167
187
default : {
168
188
if ( binary . startsWith ( 'external/' ) ) {
169
189
binary = `../${ binary . substring ( 'external/' . length ) } ` ;
170
190
}
171
- const runfilesBinary = runfiles . resolveWorkspaceRelative ( binary ) ;
172
- binary = fs . existsSync ( runfilesBinary ) ? runfilesBinary : binary ;
173
- log ( `running test command ${ this . successful + 1 } of ${ this . config . commands . length } : '${ binary } ${ args . join ( ' ' ) } ' in '${ this . testRoot } '` ) ;
191
+ try {
192
+ const runfilesBinary = runfiles . resolveWorkspaceRelative ( binary ) ;
193
+ binary = ( runfilesBinary && fs . existsSync ( runfilesBinary ) ) ? runfilesBinary : binary ;
194
+ } catch ( e ) {
195
+ }
196
+ log ( `running test command ${ this . successful + 1 } of ${ this . config . commands . length } : '${
197
+ binary } ${ args . join ( ' ' ) } ' in '${ this . testRoot } '`) ;
174
198
const spawnedProcess = spawnSync ( binary , args , { cwd : this . testRoot , stdio : 'inherit' } ) ;
175
199
if ( spawnedProcess . error ) {
176
- fail (
177
- `test command ${ testRunner . successful + 1 } ' ${ binary } ${ args . join ( ' ' ) } ' failed with ${ spawnedProcess . error . code } `) ;
200
+ fail ( `test command ${ testRunner . successful + 1 } ' ${ binary } ${
201
+ args . join ( ' ' ) } ' failed with ${ spawnedProcess . error . code } `) ;
178
202
}
179
203
if ( spawnedProcess . status ) {
180
- log ( `test command ${ testRunner . successful + 1 } '${ binary } ${ args . join ( ' ' ) } ' failed with status code ${ spawnedProcess . status } ` ) ;
204
+ log ( `test command ${ testRunner . successful + 1 } '${ binary } ${
205
+ args . join ( ' ' ) } ' failed with status code ${ spawnedProcess . status } `) ;
181
206
return spawnedProcess . status ;
182
207
}
183
208
}
@@ -206,25 +231,29 @@ class TestRunner {
206
231
if ( contents . dependencies && contents . dependencies [ key ] ) {
207
232
replacements ++ ;
208
233
contents . dependencies [ key ] = replacement ;
209
- log ( `overriding dependencies['${ key } '] npm package with 'file:${ path } ' in package.json file` ) ;
234
+ log ( `overriding dependencies['${ key } '] npm package with 'file:${
235
+ path } ' in package.json file`) ;
210
236
}
211
237
if ( contents . devDependencies && contents . devDependencies [ key ] ) {
212
238
replacements ++ ;
213
239
contents . devDependencies [ key ] = replacement ;
214
- log ( `overriding devDependencies['${ key } '] npm package with 'file:${ path } ' in package.json file` ) ;
240
+ log ( `overriding devDependencies['${ key } '] npm package with 'file:${
241
+ path } ' in package.json file`) ;
215
242
}
216
243
if ( contents . resolutions && contents . resolutions [ key ] ) {
217
244
replacements ++ ;
218
245
contents . resolutions [ key ] = replacement ;
219
- log ( `overriding resolutions['${ key } '] npm package with 'file:${ path } ' in package.json file` ) ;
246
+ log ( `overriding resolutions['${ key } '] npm package with 'file:${
247
+ path } ' in package.json file`) ;
220
248
}
221
249
// TODO: handle other formats for resolutions such as `some-package/${key}` or
222
250
// `some-package/**/${key}`
223
251
const altKey = `**/${ key } ` ;
224
252
if ( contents . resolutions && contents . resolutions [ altKey ] ) {
225
253
replacements ++ ;
226
254
contents . resolutions [ altKey ] = replacement ;
227
- log ( `overriding resolutions['${ altKey } '] npm package with 'file:${ path } ' in package.json file` ) ;
255
+ log ( `overriding resolutions['${ altKey } '] npm package with 'file:${
256
+ path } ' in package.json file`) ;
228
257
}
229
258
}
230
259
// check packages that must be replaced
@@ -244,8 +273,8 @@ class TestRunner {
244
273
const contentsEncoded = JSON . stringify ( contents , null , 2 ) ;
245
274
log ( `package.json file:\n${ contentsEncoded } ` ) ;
246
275
if ( failedPackages . length ) {
247
- fail (
248
- `expected replacements of npm packages ${ JSON . stringify ( failedPackages ) } not found; add these to the npm_packages attribute`) ;
276
+ fail ( `expected replacements of npm packages ${
277
+ JSON . stringify ( failedPackages ) } not found; add these to the npm_packages attribute`) ;
249
278
}
250
279
if ( replacements ) {
251
280
fs . writeFileSync ( packageJson , contentsEncoded ) ;
@@ -295,7 +324,8 @@ class TestRunner {
295
324
log ( `configuring test in-place under ${ this . testRoot } ` ) ;
296
325
} else {
297
326
this . testRoot = copyToTmp ( this . config . testFiles ) ;
298
- log ( `test files from '${ rootDirectory ( this . config . testFiles ) } ' copied to tmp folder ${ this . testRoot } ` ) ;
327
+ log ( `test files from '${ rootDirectory ( this . config . testFiles ) } ' copied to tmp folder ${
328
+ this . testRoot } `) ;
299
329
}
300
330
}
301
331
@@ -334,6 +364,15 @@ log_verbose(`env: ${JSON.stringify(process.env, null, 2)}`);
334
364
log_verbose ( `config: ${ JSON . stringify ( config , null , 2 ) } ` ) ;
335
365
log ( `running in ${ process . cwd ( ) } ` ) ;
336
366
367
+ // remove --preserve-symlinks-main from node wrapper as it breaks node_modules/.bin files
368
+ // run by yarn which must be resolved to their module location to work
369
+ const isWindows = process . platform === 'win32' ;
370
+ const nodePath = runfiles . resolve (
371
+ `build_bazel_rules_nodejs/internal/node/_node_bin/${ isWindows ? 'node.bat' : 'node' } ` ) ;
372
+ const nodeContents =
373
+ fs . readFileSync ( nodePath , { encoding : 'utf-8' } ) . replace ( ' --preserve-symlinks-main ' , ' ' ) ;
374
+ fs . writeFileSync ( nodePath , nodeContents , 'utf8' ) ;
375
+
337
376
const testRunner = new TestRunner ( config ) ;
338
377
const result = testRunner . run ( ) ;
339
378
log ( `${ testRunner . successful } of ${ config . commands . length } test commands successful` ) ;
0 commit comments