diff options
author | Kat Marchán <kzm@sykosomatic.org> | 2017-05-12 12:33:31 +0300 |
---|---|---|
committer | Rebecca Turner <me@re-becca.org> | 2017-05-26 04:55:19 +0300 |
commit | d27c18195478462d7e5dcdbdba220e790e07af12 (patch) | |
tree | b1346c0cd092cc96e5a3876f51037876f241d97e | |
parent | daa77708adeb4fed5067472732deeee05b7930cd (diff) |
actions: converted actions.js to use mainly promises
-rw-r--r-- | lib/install/actions.js | 213 | ||||
-rw-r--r-- | test/tap/install-actions.js | 12 |
2 files changed, 122 insertions, 103 deletions
diff --git a/lib/install/actions.js b/lib/install/actions.js index 23f0c93cb..912985e2c 100644 --- a/lib/install/actions.js +++ b/lib/install/actions.js @@ -1,18 +1,16 @@ 'use strict' -var validate = require('aproba') -var chain = require('slide').chain -var asyncMap = require('slide').asyncMap -var limit = require('call-limit') -var iferr = require('iferr') -var npm = require('../npm.js') -var andFinishTracker = require('./and-finish-tracker.js') -var andAddParentToErrors = require('./and-add-parent-to-errors.js') -var failedDependency = require('./deps.js').failedDependency -var moduleName = require('../utils/module-name.js') -var reportOptionalFailure = require('./report-optional-failure.js') -var isInstallable = require('./validate-args.js').isInstallable - -var actions = {} + +const BB = require('bluebird') + +const andAddParentToErrors = require('./and-add-parent-to-errors.js') +const failedDependency = require('./deps.js').failedDependency +const isInstallable = BB.promisify(require('./validate-args.js').isInstallable) +const moduleName = require('../utils/module-name.js') +const npm = require('../npm.js') +const reportOptionalFailure = require('./report-optional-failure.js') +const validate = require('aproba') + +const actions = {} actions.fetch = require('./action/fetch.js') actions.extract = require('./action/extract.js') @@ -33,11 +31,10 @@ actions['refresh-package-json'] = require('./action/refresh-package-json.js') Object.keys(actions).forEach(function (actionName) { var action = actions[actionName] - actions[actionName] = limit(function (staging, pkg, log, next) { - // top, buildpath, pkg, log - validate('SOOF', arguments) + actions[actionName] = (staging, pkg, log) => { + validate('SOO', [staging, pkg, log]) // refuse to run actions for failed packages - if (pkg.failed) return next() + if (pkg.failed) return BB.resolve() if (action.rollback) { if (!pkg.rollback) pkg.rollback = [] pkg.rollback.unshift(action.rollback) @@ -46,119 +43,137 @@ Object.keys(actions).forEach(function (actionName) { if (!pkg.commit) pkg.commit = [] pkg.commit.push(action.commit) } + + let actionP if (pkg.knownInstallable) { - return thenRunAction() + actionP = runAction(action, staging, pkg, log) } else { - return isInstallable(pkg.package, iferr(andDone(next), andMarkInstallable(thenRunAction))) - } - function andMarkInstallable (cb) { - return function () { + actionP = isInstallable(pkg.package).then(() => { pkg.knownInstallable = true - cb() - } - } - function thenRunAction () { - var done = andDone(next) - var result = action(staging, pkg, log, done) - if (result && result.then) result.then(() => done(), done) - } - function andDone (cb) { - return andFinishTracker(log, andAddParentToErrors(pkg.parent, andHandleOptionalDepErrors(pkg, cb))) + return runAction(action, staging, pkg, log) + }) } - }, npm.limit.action) + + return actionP.then(() => { + log.finish() + }, (err) => { + return BB.fromNode((cb) => { + andAddParentToErrors(pkg.parent, cb)(err) + }).catch((err) => { + return handleOptionalDepErrors(pkg, err) + }) + }) + } }) +exports.actions = actions + +function runAction (action, staging, pkg, log) { + return BB.fromNode((cb) => { + const result = action(staging, pkg, log, cb) + if (result && result.then) { + result.then(() => cb(), cb) + } + }) +} function markAsFailed (pkg) { pkg.failed = true - pkg.requires.forEach(function (req) { - req.requiredBy = req.requiredBy.filter(function (reqReqBy) { return reqReqBy !== pkg }) + pkg.requires.forEach((req) => { + req.requiredBy = req.requiredBy.filter((reqReqBy) => { + return reqReqBy !== pkg + }) if (req.requiredBy.length === 0 && !req.userRequired) { markAsFailed(req) } }) } -function andHandleOptionalDepErrors (pkg, next) { - return function (er) { - if (!er) return next.apply(null, arguments) - markAsFailed(pkg) - var anyFatal = pkg.userRequired || pkg.isTop - for (var ii = 0; ii < pkg.requiredBy.length; ++ii) { - var parent = pkg.requiredBy[ii] - var isFatal = failedDependency(parent, pkg) - if (isFatal) anyFatal = true - } - if (anyFatal) return next.apply(null, arguments) - reportOptionalFailure(pkg, null, er) - next() +function handleOptionalDepErrors (pkg, err) { + markAsFailed(pkg) + var anyFatal = pkg.userRequired || pkg.isTop + for (var ii = 0; ii < pkg.requiredBy.length; ++ii) { + var parent = pkg.requiredBy[ii] + var isFatal = failedDependency(parent, pkg) + if (isFatal) anyFatal = true } -} - -function prepareAction (staging, log) { - validate('SO', arguments) - return function (action) { - validate('SO', action) - var cmd = action[0] - var pkg = action[1] - if (!actions[cmd]) throw new Error('Unknown decomposed command "' + cmd + '" (is it new?)') - return [actions[cmd], staging, pkg, log.newGroup(cmd + ':' + moduleName(pkg))] + if (anyFatal) { + throw err + } else { + reportOptionalFailure(pkg, null, err) } } -exports.actions = actions - -function execAction (todo, done) { - validate('AF', arguments) - var cmd = todo.shift() - todo.push(done) - cmd.apply(null, todo) -} - -exports.doOne = function (cmd, staging, pkg, log, next) { +exports.doOne = doOne +function doOne (cmd, staging, pkg, log, next) { validate('SSOOF', arguments) - execAction(prepareAction(staging, log)([cmd, pkg]), next) + execAction(prepareAction([cmd, pkg], staging, log)).then(() => next(), next) } -function time (log) { - process.emit('time', 'action:' + log.name) -} -function timeEnd (log) { - process.emit('timeEnd', 'action:' + log.name) +exports.doParallel = doParallel +function doParallel (type, staging, actionsToRun, log, next) { + validate('SSAOF', arguments) + const acts = actionsToRun.reduce((acc, todo) => { + if (todo[0] === type) { + acc.push(prepareAction(todo, staging, log)) + } + return acc + }, []) + log.silly('doParallel', type + ' ' + actionsToRun.length) + time(log) + BB.map(acts, execAction, { + concurrency: npm.limit.action + }).nodeify((err) => { + log.finish() + timeEnd(log) + next(err) + }) } -exports.doSerial = function (type, staging, actionsToRun, log, next) { +exports.doSerial = doSerial +function doSerial (type, staging, actionsToRun, log, next) { validate('SSAOF', arguments) - actionsToRun = actionsToRun - .filter(function (value) { return value[0] === type }) log.silly('doSerial', '%s %d', type, actionsToRun.length) - time(log) - chain(actionsToRun.map(prepareAction(staging, log)), andFinishTracker(log, function () { - timeEnd(log) - next.apply(null, arguments) - })) + runSerial(type, staging, actionsToRun, log, next) } -exports.doReverseSerial = function (type, staging, actionsToRun, log, next) { +exports.doReverseSerial = doReverseSerial +function doReverseSerial (type, staging, actionsToRun, log, next) { validate('SSAOF', arguments) - actionsToRun = actionsToRun - .filter(function (value) { return value[0] === type }) - .reverse() log.silly('doReverseSerial', '%s %d', type, actionsToRun.length) + runSerial(type, staging, actionsToRun.reverse(), log, next) +} + +function runSerial (type, staging, actionsToRun, log, next) { + const acts = actionsToRun.reduce((acc, todo) => { + if (todo[0] === type) { + acc.push(prepareAction(todo, staging, log)) + } + return acc + }, []) time(log) - chain(actionsToRun.map(prepareAction(staging, log)), andFinishTracker(log, function () { + BB.each(acts, execAction).nodeify((err) => { + log.finish() timeEnd(log) - next.apply(null, arguments) - })) + next(err) + }) } -exports.doParallel = function (type, staging, actionsToRun, log, next) { - validate('SSAOF', arguments) - actionsToRun = actionsToRun.filter(function (value) { return value[0] === type }) - log.silly('doParallel', type + ' ' + actionsToRun.length) - time(log) +function time (log) { + process.emit('time', 'action:' + log.name) +} +function timeEnd (log) { + process.emit('timeEnd', 'action:' + log.name) +} - asyncMap(actionsToRun.map(prepareAction(staging, log)), execAction, andFinishTracker(log, function () { - timeEnd(log) - next.apply(null, arguments) - })) +function prepareAction (action, staging, log) { + validate('ASO', arguments) + validate('SO', action) + var cmd = action[0] + var pkg = action[1] + if (!actions[cmd]) throw new Error('Unknown decomposed command "' + cmd + '" (is it new?)') + return [actions[cmd], staging, pkg, log.newGroup(cmd + ':' + moduleName(pkg))] +} + +function execAction (todo) { + return todo[0].apply(null, todo.slice(1)) } diff --git a/test/tap/install-actions.js b/test/tap/install-actions.js index 824a31c1b..6ca6e3353 100644 --- a/test/tap/install-actions.js +++ b/test/tap/install-actions.js @@ -56,8 +56,10 @@ test('->optdep:a->dep:b', function (t) { moduleB.parent = tree t.plan(3) - actions.postinstall('/', moduleA, mockLog, function (er) { - t.is(er && er.code, 'ELIFECYCLE', 'Lifecycle failed') + return actions.postinstall('/', moduleA, mockLog).then(() => { + throw new Error('was not supposed to succeed') + }, (err) => { + t.is(err && err.code, 'ELIFECYCLE', 'Lifecycle failed') t.ok(moduleA.failed, 'moduleA (optional dep) is marked failed') t.ok(moduleB.failed, 'moduleB (direct dep of moduleA) is marked as failed') t.end() @@ -108,8 +110,10 @@ test('->dep:b,->optdep:a->dep:b', function (t) { moduleB.parent = tree t.plan(3) - actions.postinstall('/', moduleA, mockLog, function (er) { - t.ok(er && er.code === 'ELIFECYCLE', 'Lifecycle failed') + return actions.postinstall('/', moduleA, mockLog).then(() => { + throw new Error('was not supposed to succeed') + }, (err) => { + t.ok(err && err.code === 'ELIFECYCLE', 'Lifecycle failed') t.ok(moduleA.failed, 'moduleA (optional dep) is marked failed') t.ok(!moduleB.failed, 'moduleB (direct dep of moduleA) is marked as failed') t.end() |