From 9ee16cdb55ae18368e36a476f32d7a64af6178b5 Mon Sep 17 00:00:00 2001 From: "Rico Sta. Cruz" Date: Sat, 30 Jan 2016 03:59:02 +0800 Subject: [PATCH 1/2] Improve atomicness --- lib/install.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/install.js b/lib/install.js index fc89c17442..ee34a8a3a5 100644 --- a/lib/install.js +++ b/lib/install.js @@ -12,6 +12,7 @@ var relSymlink = require('./rel_symlink') var linkBins = require('./install/link_bins') var linkBundledDeps = require('./install/link_bundled_deps') var fs = require('mz/fs') +var rimraf = require('thenify')(require('rimraf')) /* * Installs a package. @@ -78,12 +79,11 @@ module.exports = function install (ctx, pkgSpec, modules, options) { var log = ctx.log(pkg.spec) // function - return make(join(modules, pkg.spec.name), _ => + return make(join(modules, pkg.spec.name), false, _ => resolve(pkg.spec) .then(saveResolution) .then(_ => log('resolved', pkg.data)) - .then(_ => make(paths.target, _ => - buildToStoreCached(ctx, paths, pkg, log))) + .then(_ => buildToStoreCached(ctx, paths, pkg, log)) .then(_ => mkdirp(paths.modules)) .then(_ => symlinkToModules(paths.target, pkg.spec, paths.modules))) .then(_ => log('done')) @@ -111,8 +111,9 @@ function buildToStoreCached (ctx, paths, pkg, log) { if (isCircular(pkg)) { return Promise.resolve() } else { - return memoize(ctx.builds, pkg.fullname, _ => - buildToStore(ctx, paths, pkg, log)) + return make(paths.target, ctx.builds[pkg.fullname], _ => + memoize(ctx.builds, pkg.fullname, _ => + buildToStore(ctx, paths, pkg, log))) } } @@ -135,6 +136,7 @@ function buildToStore (ctx, paths, pkg, log) { .then(_ => log('downloading')) .then(_ => mkdirp(paths.store)) .then(_ => mkdirp(paths.tmp)) + .then(_ => fs.writeFile(join(paths.tmp, '.pnpm_inprogress'), '', 'utf-8')) .then(_ => fetch(paths.tmp, pkg.dist.tarball, pkg.dist.shasum, log)) // update pkg.fulldata; to be used later @@ -156,6 +158,7 @@ function buildToStore (ctx, paths, pkg, log) { .then(_ => symlinkSelf(paths.tmp, pkg.data, pkg.keypath.length)) // move to .store/lodash@4.0.0; remove the stub done earlier + .then(_ => fs.unlink(join(paths.tmp, '.pnpm_inprogress'))) .then(_ => fs.unlink(paths.target)) .then(_ => fs.rename(paths.tmp, paths.target)) } @@ -208,12 +211,19 @@ function isCircular (pkg) { } /* - * If `path` exists, don't do anything; otherwise invoke `fn()`. - * Kinda like how makefiles work. + * If `path` doesn't exist, run `fn()`. + * If it exists and is not in progress, don't do anything. + * If it's in progress, check if we're working on it. If we're not, + * obliterate it and run `fn()`. */ -function make (path, fn) { +function make (path, isWorking, fn) { return fs.stat(path) + .then(_ => { + return fs.stat(join(path, '.pnpm_inprogress')) + .then(_ => { if (!isWorking) return rimraf(path).then(fn) }) + .catch(err => { if (err.code !== 'ENOENT') throw err }) + }) .catch(err => { if (err.code !== 'ENOENT') throw err return fn() From 9536d5f234943345834301d427ed3f476a17bcde Mon Sep 17 00:00:00 2001 From: "Rico Sta. Cruz" Date: Sat, 30 Jan 2016 04:04:53 +0800 Subject: [PATCH 2/2] Clean .tmp in incomplete runs --- lib/fs/obliterate.js | 23 +++++++++++++++++++++++ lib/install.js | 4 ++-- 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 lib/fs/obliterate.js diff --git a/lib/fs/obliterate.js b/lib/fs/obliterate.js new file mode 100644 index 0000000000..93478cd2f5 --- /dev/null +++ b/lib/fs/obliterate.js @@ -0,0 +1,23 @@ +require('../promise') +var rimraf = require('thenify')(require('rimraf')) +var fs = require('mz/fs') + +/* + * Removes `path`. + * If it's a symlink, remove its destination as well. + */ + +module.exports = function obliterate (path) { + return fs.lstat(path) + .then(stat => { + if (stat.isSymbolicLink()) { + return fs.readlink(path) + .then(realpath => rimraf(realpath)) + } else { + return rimraf(path) + } + }) + .catch(err => { + if (err !== 'ENOENT') throw err + }) +} diff --git a/lib/install.js b/lib/install.js index ee34a8a3a5..144e078a31 100644 --- a/lib/install.js +++ b/lib/install.js @@ -12,7 +12,7 @@ var relSymlink = require('./rel_symlink') var linkBins = require('./install/link_bins') var linkBundledDeps = require('./install/link_bundled_deps') var fs = require('mz/fs') -var rimraf = require('thenify')(require('rimraf')) +var obliterate = require('./fs/obliterate') /* * Installs a package. @@ -221,7 +221,7 @@ function make (path, isWorking, fn) { return fs.stat(path) .then(_ => { return fs.stat(join(path, '.pnpm_inprogress')) - .then(_ => { if (!isWorking) return rimraf(path).then(fn) }) + .then(_ => { if (!isWorking) return obliterate(path).then(fn) }) .catch(err => { if (err.code !== 'ENOENT') throw err }) }) .catch(err => {