@@ -57,6 +57,8 @@ function exec (command, options, callback) {
5757 }
5858 procDone ( err )
5959 } )
60+
61+ return proc
6062}
6163
6264// If we call `process.exit` immediately after logging to the console, then
@@ -118,10 +120,11 @@ function postinstallBuild () {
118120 var npm = 'npm'
119121 var execPath = process . env . npm_execpath
120122 var userAgent = process . env . npm_config_user_agent || ''
123+ var npmVersion = ( userAgent . match ( / ^ n p m \/ ( [ ^ \s ] + ) / ) || [ ] ) [ 1 ]
121124 // If the user agent doesn't start with `npm/`, just fall back to running
122125 // `npm` since alternative agents (e.g. Yarn) may not support the same
123126 // commands (like `prune`).
124- if ( execPath && userAgent . indexOf ( 'npm/' ) === 0 ) {
127+ if ( execPath && npmVersion ) {
125128 npm = '"' + process . argv [ 0 ] + '" "' + execPath + '"'
126129 }
127130
@@ -235,6 +238,18 @@ function postinstallBuild () {
235238 return installArgs
236239 }
237240
241+ var warnUserAgent = function ( ) {
242+ if ( npmVersion && ! / ^ ( 3 \. | 4 \. 0 \. ) / . test ( npmVersion ) ) {
243+ log . warn (
244+ 'postinstall-build:\n This version of npm (' + npmVersion + ') may ' +
245+ 'not be compatible with postinstall-build! There\n are outstanding ' +
246+ 'bugs in certain versions of npm that prevent it from working with\n ' +
247+ 'postinstall-build. See https://github.com/exogen/postinstall-build#bugs-in-npm\n ' +
248+ 'for more information.\n'
249+ )
250+ }
251+ }
252+
238253 var checkBuildArtifact = function ( callback ) {
239254 fs . stat ( buildArtifact , function ( err , stats ) {
240255 if ( err || ! ( stats . isFile ( ) || stats . isDirectory ( ) ) ) {
@@ -252,6 +267,30 @@ function postinstallBuild () {
252267 } )
253268 }
254269
270+ var checkUserAgent = function ( callback ) {
271+ // If we know an incompatible version of npm triggered `postinstall-build`,
272+ // print a warning. Note that if the original user agent is NOT npm, then we
273+ // don't know what version `postinstall-build` will actually run, since the
274+ // `$npm_config_user_agent` string didn't tell us, so run `npm --version` to
275+ // find out.
276+ if ( npmVersion ) {
277+ warnUserAgent ( )
278+ return callback ( null , npmVersion )
279+ }
280+ var output = ''
281+ var command = npm + ' --version'
282+ log . info ( 'postinstall-build:\n ' + command + '\n' )
283+ var proc = exec ( command , { stdio : 'pipe' } , function ( err ) {
284+ npmVersion = ( output . match ( / ^ ( \d [ ^ \s ] * ) / ) || [ ] ) [ 1 ]
285+ warnUserAgent ( )
286+ callback ( err , npmVersion )
287+ } )
288+ proc . stdout . setEncoding ( 'utf8' )
289+ proc . stdout . on ( 'data' , function ( chunk ) {
290+ output += chunk
291+ } )
292+ }
293+
255294 var installBuildDependencies = function ( execOpts , callback ) {
256295 // We only need to install dependencies if `shouldPrune` is true. Why?
257296 // Because this flag detects whether `devDependencies` were already
@@ -301,38 +340,47 @@ function postinstallBuild () {
301340 return handleError ( err )
302341 }
303342 if ( shouldBuild ) {
304- // If `npm install` ends up being run by `installBuildDependencies`, this
305- // script will be run again. Set an environment variable to tell it to
306- // skip the check. Really we just want the spawned child's `env` to be
307- // modified, but it's easier just modify and pass along our entire
308- // `process.env`.
309- process . env . POSTINSTALL_BUILD_CWD = CWD
310-
311- var execOpts = {
312- env : process . env ,
313- quote : true
314- }
315- if ( flags . verbosity === 0 ) {
316- execOpts . stdio = 'ignore'
317- }
318-
319- installBuildDependencies ( execOpts , function ( err ) {
343+ checkUserAgent ( function ( err ) {
344+ // If an error occurred, the only consequence is that we don't know
345+ // which version of npm is going to be run, and can't issue a warning.
346+ // If there's an actual problem running npm we'll find out soon enough.
320347 if ( err ) {
321- return handleError ( err )
348+ log . warn ( 'postinstall-build:\n ' + err + '\n' )
322349 }
323- // Don't need this flag anymore as `postinstall` was already run.
324- // Change it back so the environment is minimally changed for the
325- // remaining commands.
326- process . env . POSTINSTALL_BUILD_CWD = POSTINSTALL_BUILD_CWD
327350
328- runBuildCommand ( execOpts , function ( err ) {
351+ // If `npm install` ends up being run by `installBuildDependencies`, this
352+ // script will be run again. Set an environment variable to tell it to
353+ // skip the check. Really we just want the spawned child's `env` to be
354+ // modified, but it's easier just modify and pass along our entire
355+ // `process.env`.
356+ process . env . POSTINSTALL_BUILD_CWD = CWD
357+
358+ var execOpts = {
359+ env : process . env ,
360+ quote : true
361+ }
362+ if ( flags . verbosity === 0 ) {
363+ execOpts . stdio = 'ignore'
364+ }
365+
366+ installBuildDependencies ( execOpts , function ( err ) {
329367 if ( err ) {
330368 return handleError ( err )
331369 }
332- cleanUp ( execOpts , function ( err ) {
370+ // Don't need this flag anymore as `postinstall` was already run.
371+ // Change it back so the environment is minimally changed for the
372+ // remaining commands.
373+ process . env . POSTINSTALL_BUILD_CWD = POSTINSTALL_BUILD_CWD
374+
375+ runBuildCommand ( execOpts , function ( err ) {
333376 if ( err ) {
334377 return handleError ( err )
335378 }
379+ cleanUp ( execOpts , function ( err ) {
380+ if ( err ) {
381+ return handleError ( err )
382+ }
383+ } )
336384 } )
337385 } )
338386 } )
0 commit comments