@@ -17,6 +17,9 @@ const VERBOSE_LOGS = !!process.env['VERBOSE_LOGS'];
1717// Set to true if you want the /tmp folder created to persist after running `bazel test`
1818const KEEP_TMP = false ;
1919
20+ // bazelisk requires a $HOME environment variable for its cache
21+ process . env [ 'HOME' ] = tmp . dirSync ( { keep : KEEP_TMP , unsafeCleanup : ! KEEP_TMP } ) . name ;
22+
2023function fail ( ...m ) {
2124 console . error ( ) ;
2225 console . error ( `[${ path . basename ( __filename ) } ]` ) ;
@@ -131,6 +134,22 @@ function copyToTmp(files) {
131134 tmp . dirSync ( { keep : KEEP_TMP , unsafeCleanup : ! KEEP_TMP } ) . name ) ;
132135}
133136
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+
134153/**
135154 * TestRunner handles setting up the integration test and executing
136155 * the test commands based on the config.
@@ -153,31 +172,37 @@ class TestRunner {
153172 // and quoted arguments that contain spaces
154173 const split = command . split ( ' ' ) ;
155174 let binary = split [ 0 ] ;
156- const args = split . slice ( 1 ) ;
175+ const args = split . slice ( 1 ) . map ( a => expandEnv ( a ) ) ;
157176 switch ( binary ) {
158177 case 'patch-package-json' : {
159178 let packageJsonFile = 'package.json' ;
160179 if ( args . length > 0 ) {
161180 packageJsonFile = args [ 0 ] ;
162181 }
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 } '`) ;
164184 this . _patchPackageJson ( packageJsonFile ) ;
165185 } break ;
166186
167187 default : {
168188 if ( binary . startsWith ( 'external/' ) ) {
169189 binary = `../${ binary . substring ( 'external/' . length ) } ` ;
170190 }
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 } '`) ;
174198 const spawnedProcess = spawnSync ( binary , args , { cwd : this . testRoot , stdio : 'inherit' } ) ;
175199 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 } `) ;
178202 }
179203 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 } `) ;
181206 return spawnedProcess . status ;
182207 }
183208 }
@@ -206,25 +231,29 @@ class TestRunner {
206231 if ( contents . dependencies && contents . dependencies [ key ] ) {
207232 replacements ++ ;
208233 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`) ;
210236 }
211237 if ( contents . devDependencies && contents . devDependencies [ key ] ) {
212238 replacements ++ ;
213239 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`) ;
215242 }
216243 if ( contents . resolutions && contents . resolutions [ key ] ) {
217244 replacements ++ ;
218245 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`) ;
220248 }
221249 // TODO: handle other formats for resolutions such as `some-package/${key}` or
222250 // `some-package/**/${key}`
223251 const altKey = `**/${ key } ` ;
224252 if ( contents . resolutions && contents . resolutions [ altKey ] ) {
225253 replacements ++ ;
226254 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`) ;
228257 }
229258 }
230259 // check packages that must be replaced
@@ -244,8 +273,8 @@ class TestRunner {
244273 const contentsEncoded = JSON . stringify ( contents , null , 2 ) ;
245274 log ( `package.json file:\n${ contentsEncoded } ` ) ;
246275 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`) ;
249278 }
250279 if ( replacements ) {
251280 fs . writeFileSync ( packageJson , contentsEncoded ) ;
@@ -295,7 +324,8 @@ class TestRunner {
295324 log ( `configuring test in-place under ${ this . testRoot } ` ) ;
296325 } else {
297326 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 } `) ;
299329 }
300330 }
301331
@@ -334,6 +364,15 @@ log_verbose(`env: ${JSON.stringify(process.env, null, 2)}`);
334364log_verbose ( `config: ${ JSON . stringify ( config , null , 2 ) } ` ) ;
335365log ( `running in ${ process . cwd ( ) } ` ) ;
336366
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+
337376const testRunner = new TestRunner ( config ) ;
338377const result = testRunner . run ( ) ;
339378log ( `${ testRunner . successful } of ${ config . commands . length } test commands successful` ) ;
0 commit comments