Merge pull request #17 from rstacruz/improve-atomicness

Improve atomicness
This commit is contained in:
Rico Sta. Cruz
2016-01-30 04:11:52 +08:00
2 changed files with 41 additions and 8 deletions

23
lib/fs/obliterate.js Normal file
View File

@@ -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
})
}

View File

@@ -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 obliterate = require('./fs/obliterate')
/*
* 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 obliterate(path).then(fn) })
.catch(err => { if (err.code !== 'ENOENT') throw err })
})
.catch(err => {
if (err.code !== 'ENOENT') throw err
return fn()