Skip to content

Commit

Permalink
lifecycle: remove local dependencies other than spawn
Browse files Browse the repository at this point in the history
PR-URL: npm/npm#18025
Credit: @mikesherov
Reviewed-By: @zkat
  • Loading branch information
mikesherov authored and zkat committed Aug 16, 2017
1 parent f60b05d commit 4f49f68
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 71 deletions.
21 changes: 21 additions & 0 deletions lib/config/lifecycle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict'

const npm = require('../npm.js')

module.exports = lifecycleOpts

function lifecycleOpts () {
return {
config: npm.config.snapshot,
dir: npm.dir,
force: npm.config.get('force'),
group: npm.config.get('group'),
ignorePrepublish: npm.config.get('ignore-prepublish'),
ignoreScripts: npm.config.get('ignore-scripts'),
production: npm.config.get('production'),
scriptShell: npm.config.get('script-shell'),
scriptsPrependNodePath: npm.config.get('scripts-prepend-node-path'),
unsafePerm: npm.config.get('unsafe-perm'),
user: npm.config.get('user')
}
}
2 changes: 1 addition & 1 deletion lib/restart.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('./utils/lifecycle.js').cmd('restart')
module.exports = require('./utils/lifecycle-cmd.js')('restart')
2 changes: 1 addition & 1 deletion lib/start.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('./utils/lifecycle.js').cmd('start')
module.exports = require('./utils/lifecycle-cmd.js')('start')
2 changes: 1 addition & 1 deletion lib/stop.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('./utils/lifecycle.js').cmd('stop')
module.exports = require('./utils/lifecycle-cmd.js')('stop')
8 changes: 2 additions & 6 deletions lib/test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
module.exports = test

const testCmd = require('./utils/lifecycle.js').cmd('test')
const usage = require('./utils/usage')
const testCmd = require('./utils/lifecycle-cmd.js')('test')

test.usage = usage(
'test',
'npm test [-- <args>]'
)
test.usage = testCmd.usage

function test (args, cb) {
testCmd(args, function (er) {
Expand Down
18 changes: 18 additions & 0 deletions lib/utils/lifecycle-cmd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
exports = module.exports = cmd

var npm = require('../npm.js')
var usage = require('./usage.js')

function cmd (stage) {
function CMD (args, cb) {
npm.commands['run-script']([stage].concat(args), cb)
}
CMD.usage = usage(stage, 'npm ' + stage + ' [-- <args>]')
var installedShallow = require('./completion/installed-shallow.js')
CMD.completion = function (opts, cb) {
installedShallow(opts, function (d) {
return d.scripts && d.scripts[stage]
}, cb)
}
return CMD
}
103 changes: 46 additions & 57 deletions lib/utils/lifecycle.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
exports = module.exports = lifecycle
exports.cmd = cmd
exports = module.exports = runLifecycle
exports.makeEnv = makeEnv
exports._incorrectWorkingDirectory = _incorrectWorkingDirectory

var log = require('npmlog')
var spawn = require('./spawn')
var npm = require('../npm.js')
const lifecycleOpts = require('../config/lifecycle')
var path = require('path')
var fs = require('graceful-fs')
var chain = require('slide').chain
var Stream = require('stream').Stream
var PATH = 'PATH'
var uidNumber = require('uid-number')
var umask = require('./umask')
var usage = require('./usage')
var output = require('./output.js')
var umask = require('umask')
var which = require('which')

// windows calls it's path 'Path' usually, but this is not guaranteed.
Expand All @@ -31,7 +28,12 @@ function logid (pkg, stage) {
return pkg._id + '~' + stage + ':'
}

function lifecycle (pkg, stage, wd, unsafe, failOk, cb) {
function runLifecycle (pkg, stage, wd, unsafe, failOk, cb) {
const opts = lifecycleOpts()
return lifecycle(pkg, stage, wd, unsafe, failOk, cb, opts)
}

function lifecycle (pkg, stage, wd, unsafe, failOk, cb, opts) {
if (typeof cb !== 'function') {
cb = failOk
failOk = false
Expand All @@ -51,23 +53,23 @@ function lifecycle (pkg, stage, wd, unsafe, failOk, cb) {
log.info('lifecycle', logid(pkg, stage), pkg._id)
if (!pkg.scripts) pkg.scripts = {}

if (npm.config.get('ignore-scripts')) {
if (opts.ignoreScripts) {
log.info('lifecycle', logid(pkg, stage), 'ignored because ignore-scripts is set to true', pkg._id)
pkg.scripts = {}
}
if (stage === 'prepublish' && npm.config.get('ignore-prepublish')) {
if (stage === 'prepublish' && opts.ignorePrepublish) {
log.info('lifecycle', logid(pkg, stage), 'ignored because ignore-prepublish is set to true', pkg._id)
delete pkg.scripts.prepublish
}

if (!pkg.scripts[stage]) return cb()

validWd(wd || path.resolve(npm.dir, pkg.name), function (er, wd) {
validWd(wd || path.resolve(opts.dir, pkg.name), function (er, wd) {
if (er) return cb(er)

unsafe = unsafe || npm.config.get('unsafe-perm')
unsafe = unsafe || opts.unsafePerm

if ((wd.indexOf(npm.dir) !== 0 || _incorrectWorkingDirectory(wd, pkg)) &&
if ((wd.indexOf(opts.dir) !== 0 || _incorrectWorkingDirectory(wd, pkg)) &&
!unsafe && pkg.scripts[stage]) {
log.warn('lifecycle', logid(pkg, stage), 'cannot run in wd',
'%s %s (wd=%s)', pkg._id, pkg.scripts[stage], wd
Expand All @@ -76,25 +78,25 @@ function lifecycle (pkg, stage, wd, unsafe, failOk, cb) {
}

// set the env variables, then run scripts as a child process.
var env = makeEnv(pkg)
var env = makeEnv(pkg, opts)
env.npm_lifecycle_event = stage
env.npm_node_execpath = env.NODE = env.NODE || process.execPath
env.npm_execpath = require.main.filename
env.INIT_CWD = process.cwd()

// 'nobody' typically doesn't have permission to write to /tmp
// even if it's never used, sh freaks out.
if (!npm.config.get('unsafe-perm')) env.TMPDIR = wd
if (!opts.unsafePerm) env.TMPDIR = wd

lifecycle_(pkg, stage, wd, env, unsafe, failOk, cb)
lifecycle_(pkg, stage, wd, opts, env, unsafe, failOk, cb)
})
}

function _incorrectWorkingDirectory (wd, pkg) {
return wd.lastIndexOf(pkg.name) !== wd.length - pkg.name.length
}

function lifecycle_ (pkg, stage, wd, env, unsafe, failOk, cb) {
function lifecycle_ (pkg, stage, wd, opts, env, unsafe, failOk, cb) {
var pathArr = []
var p = wd.split(/[\\\/]node_modules[\\\/]/)
var acc = path.resolve(p.shift())
Expand All @@ -109,7 +111,7 @@ function lifecycle_ (pkg, stage, wd, env, unsafe, failOk, cb) {
// the bundled one will be used for installing things.
pathArr.unshift(path.join(__dirname, '..', '..', 'bin', 'node-gyp-bin'))

if (shouldPrependCurrentNodeDirToPATH()) {
if (shouldPrependCurrentNodeDirToPATH(opts.scriptsPrependNodePath)) {
// prefer current node interpreter in child scripts
pathArr.push(path.dirname(process.execPath))
}
Expand All @@ -128,7 +130,7 @@ function lifecycle_ (pkg, stage, wd, env, unsafe, failOk, cb) {

function done (er) {
if (er) {
if (npm.config.get('force')) {
if (opts.force) {
log.info('lifecycle', logid(pkg, stage), 'forced, continuing', er)
er = null
} else if (failOk) {
Expand All @@ -141,15 +143,14 @@ function lifecycle_ (pkg, stage, wd, env, unsafe, failOk, cb) {

chain(
[
packageLifecycle && [runPackageLifecycle, pkg, env, wd, unsafe],
[runHookLifecycle, pkg, env, wd, unsafe]
packageLifecycle && [runPackageLifecycle, pkg, env, wd, opts, unsafe],
[runHookLifecycle, pkg, env, wd, opts, unsafe]
],
done
)
}

function shouldPrependCurrentNodeDirToPATH () {
var cfgsetting = npm.config.get('scripts-prepend-node-path')
function shouldPrependCurrentNodeDirToPATH (cfgsetting) {
if (cfgsetting === false) return false
if (cfgsetting === true) return true

Expand Down Expand Up @@ -195,14 +196,14 @@ function validWd (d, cb) {
})
}

function runPackageLifecycle (pkg, env, wd, unsafe, cb) {
function runPackageLifecycle (pkg, env, wd, opts, unsafe, cb) {
// run package lifecycle scripts in the package root, or the nearest parent.
var stage = env.npm_lifecycle_event
var cmd = env.npm_lifecycle_script

var note = '\n> ' + pkg._id + ' ' + stage + ' ' + wd +
'\n> ' + cmd + '\n'
runCmd(note, cmd, pkg, env, stage, wd, unsafe, cb)
runCmd(note, cmd, pkg, env, stage, wd, opts, unsafe, cb)
}

var running = false
Expand All @@ -215,19 +216,21 @@ function dequeue () {
}
}

function runCmd (note, cmd, pkg, env, stage, wd, unsafe, cb) {
function runCmd (note, cmd, pkg, env, stage, wd, opts, unsafe, cb) {
if (running) {
queue.push([note, cmd, pkg, env, stage, wd, unsafe, cb])
return
}

running = true
log.pause()
var user = unsafe ? null : npm.config.get('user')
var group = unsafe ? null : npm.config.get('group')
var user = unsafe ? null : opts.user
var group = unsafe ? null : opts.group

if (log.level !== 'silent') {
output(note)
log.clearProgress()
console.log(note)
log.showProgress()
}
log.verbose('lifecycle', logid(pkg, stage), 'unsafe-perm in lifecycle', unsafe)

Expand All @@ -236,15 +239,15 @@ function runCmd (note, cmd, pkg, env, stage, wd, unsafe, cb) {
}

if (unsafe) {
runCmd_(cmd, pkg, env, wd, stage, unsafe, 0, 0, cb)
runCmd_(cmd, pkg, env, wd, opts, stage, unsafe, 0, 0, cb)
} else {
uidNumber(user, group, function (er, uid, gid) {
runCmd_(cmd, pkg, env, wd, stage, unsafe, uid, gid, cb)
runCmd_(cmd, pkg, env, wd, opts, stage, unsafe, uid, gid, cb)
})
}
}

function runCmd_ (cmd, pkg, env, wd, stage, unsafe, uid, gid, cb_) {
function runCmd_ (cmd, pkg, env, wd, opts, stage, unsafe, uid, gid, cb_) {
function cb (er) {
cb_.apply(null, arguments)
log.resume()
Expand All @@ -265,7 +268,7 @@ function runCmd_ (cmd, pkg, env, wd, stage, unsafe, uid, gid, cb_) {
var sh = 'sh'
var shFlag = '-c'

var customShell = npm.config.get('script-shell')
var customShell = opts.scriptShell

if (customShell) {
sh = customShell
Expand Down Expand Up @@ -303,8 +306,8 @@ function runCmd_ (cmd, pkg, env, wd, stage, unsafe, uid, gid, cb_) {
if (er.code !== 'EPERM') {
er.code = 'ELIFECYCLE'
}
fs.stat(npm.dir, function (statError, d) {
if (statError && statError.code === 'ENOENT' && npm.dir.split(path.sep).slice(-1)[0] === 'node_modules') {
fs.stat(opts.dir, function (statError, d) {
if (statError && statError.code === 'ENOENT' && opts.dir.split(path.sep).slice(-1)[0] === 'node_modules') {
log.warn('', 'Local package.json exists, but node_modules missing, did you mean to install?')
}
})
Expand All @@ -330,21 +333,21 @@ function runCmd_ (cmd, pkg, env, wd, stage, unsafe, uid, gid, cb_) {
}
}

function runHookLifecycle (pkg, env, wd, unsafe, cb) {
function runHookLifecycle (pkg, env, wd, opts, unsafe, cb) {
// check for a hook script, run if present.
var stage = env.npm_lifecycle_event
var hook = path.join(npm.dir, '.hooks', stage)
var hook = path.join(opts.dir, '.hooks', stage)
var cmd = hook

fs.stat(hook, function (er) {
if (er) return cb()
var note = '\n> ' + pkg._id + ' ' + stage + ' ' + wd +
'\n> ' + cmd
runCmd(note, hook, pkg, env, stage, wd, unsafe, cb)
runCmd(note, hook, pkg, env, stage, wd, opts, unsafe, cb)
})
}

function makeEnv (data, prefix, env) {
function makeEnv (data, opts, prefix, env) {
prefix = prefix || 'npm_package_'
if (!env) {
env = {}
Expand All @@ -355,7 +358,7 @@ function makeEnv (data, prefix, env) {
}

// express and others respect the NODE_ENV value.
if (npm.config.get('production')) env.NODE_ENV = 'production'
if (opts.production) env.NODE_ENV = 'production'
} else if (!data.hasOwnProperty('_lifecycleEnv')) {
Object.defineProperty(data, '_lifecycleEnv',
{
Expand All @@ -375,13 +378,14 @@ function makeEnv (data, prefix, env) {
try {
// quick and dirty detection for cyclical structures
JSON.stringify(data[i])
makeEnv(data[i], envKey + '_', env)
makeEnv(data[i], opts, envKey + '_', env)
} catch (ex) {
// usually these are package objects.
// just get the path and basic details.
var d = data[i]
makeEnv(
{ name: d.name, version: d.version, path: d.path },
opts,
envKey + '_',
env
)
Expand All @@ -399,18 +403,17 @@ function makeEnv (data, prefix, env) {

prefix = 'npm_config_'
var pkgConfig = {}
var keys = npm.config.keys
var pkgVerConfig = {}
var namePref = data.name + ':'
var verPref = data.name + '@' + data.version + ':'

keys.forEach(function (i) {
Object.keys(opts.config).forEach(function (i) {
// in some rare cases (e.g. working with nerf darts), there are segmented
// "private" (underscore-prefixed) config names -- don't export
if (i.charAt(0) === '_' && i.indexOf('_' + namePref) !== 0 || i.match(/:_/)) {
return
}
var value = npm.config.get(i)
var value = opts.config[i]
if (value instanceof Stream || Array.isArray(value)) return
if (i.match(/umask/)) value = umask.toString(value)
if (!value) value = ''
Expand Down Expand Up @@ -443,17 +446,3 @@ function makeEnv (data, prefix, env) {

return env
}

function cmd (stage) {
function CMD (args, cb) {
npm.commands['run-script']([stage].concat(args), cb)
}
CMD.usage = usage(stage, 'npm ' + stage + ' [-- <args>]')
var installedShallow = require('./completion/installed-shallow.js')
CMD.completion = function (opts, cb) {
installedShallow(opts, function (d) {
return d.scripts && d.scripts[stage]
}, cb)
}
return CMD
}
Loading

0 comments on commit 4f49f68

Please sign in to comment.