Remove links pointing to nonexistent folders on cache-clean, #182.

This commit is contained in:
Marcelo Conceicao
2013-02-07 16:17:00 +00:00
committed by Marcelo Conceição
parent 4f64412c19
commit 2ec352bc70
2 changed files with 155 additions and 63 deletions

View File

@@ -12,6 +12,7 @@ var nopt = require('nopt');
var rimraf = require('rimraf');
var path = require('path');
var glob = require('glob');
var fs = require('fs');
var _ = require('lodash');
var help = require('./help');
@@ -22,75 +23,119 @@ var fileExists = require('../util/file-exists');
var optionTypes = { help: Boolean };
var shorthand = { 'h': ['--help'] };
var removePkg = function (pkg, emitter, next) {
var processCachedPackage = function (emitter, pkg, next) {
removeCachedPackage(pkg, function (err, exists) {
if (err) {
emitter.emit('error', err);
return next();
}
if (exists) {
template('action', { name: 'cache cleared', shizzle: pkg })
.on('data', emitter.emit.bind(emitter, 'data'));
}
next();
});
};
var removeCachedPackage = function (pkg, next) {
var folder = path.join(config.cache, pkg);
fileExists(folder, function (exists) {
var err;
if (!exists) {
err = new Error('Package ' + pkg + ' is not installed');
emitter.emit('error', err);
return next(err);
}
if (!exists) return next(null, false);
rimraf(folder, function (err) {
if (err) {
emitter.emit('error', err);
return next(err);
}
emitter.emit('data', template('action', { name: 'cleared', shizzle: pkg }, true));
next();
if (err) return next(err);
next(null, true);
});
});
};
var createFuncs = function (pkgs, emitter) {
return pkgs.map(function (pkg) {
pkg = pkg.replace(/^\.\//, '');
return removePkg.bind(removePkg, pkg, emitter);
var processLinkedPackage = function (emitter, pkg, next) {
checkAndRemoveLinkToPackage(pkg, function (err, removed) {
if (err) {
emitter.emit('error', err);
return next();
}
if (removed) {
template('action', { name: 'link cleared', shizzle: pkg })
.on('data', emitter.emit.bind(emitter, 'data'));
}
next();
});
};
var cleanCompletion = function (emitter, next) {
fileExists(config.completion, function (exists) {
if (!exists) return next();
var checkAndRemoveLinkToPackage = function (pkg, next) {
var folder = path.join(config.links, pkg);
rimraf(config.completion, function (err) {
if (err) {
emitter.emit('error', err);
return next(err);
fs.readlink(folder, function (err, linkString) {
if (err && err.code === 'ENOENT') return next();
fileExists(linkString, function (exists) {
if (!exists) {
return rimraf(folder, function (err) {
if (err) return next(err);
next(null, true);
});
}
emitter.emit('data', template('action', { name: 'cleared', shizzle: 'completion cache' }, true));
next();
next(null, false);
});
});
};
module.exports = function (pkgs) {
var emitter = new Emitter;
var funcs;
// If no pkgs are passed we delete all
// Otherwise we delete the passed ones
if (!pkgs || !pkgs.length) {
glob('./*', { cwd: config.cache }, function (err, dirs) {
if (err) return emitter.emit('error', err);
pkgs = dirs;
funcs = createFuncs(pkgs, emitter);
async.parallel({
cache: function (next) {
if (!pkgs || !pkgs.length) {
glob('./*', { cwd: config.cache }, function (err, dirs) {
if (err) {
emitter.emit('error', err);
return next();
}
// If all the cache is to be cleared,
// also clear the completion cache
funcs.push(cleanCompletion.bind(cleanCompletion, emitter));
var pkgs = dirs.map(function (dir) { return dir.replace(/^\.\//, ''); });
async.forEach(pkgs, processCachedPackage.bind(this, emitter), next);
});
} else {
pkgs = _.uniq(pkgs);
async.forEach(pkgs, processCachedPackage.bind(this, emitter), next);
}
},
links: function (next) {
if (!pkgs || !pkgs.length) {
glob('./*', { cwd: config.links }, function (err, dirs) {
if (err) {
emitter.emit('error', err);
return next();
}
async.parallel(funcs, emitter.emit.bind(emitter, 'end'));
});
} else {
funcs = createFuncs(_.uniq(pkgs), emitter);
async.parallel(funcs, emitter.emit.bind(emitter, 'end'));
}
var pkgs = dirs.map(function (dir) { return dir.replace(/^\.\//, ''); });
async.forEach(pkgs, processLinkedPackage.bind(this, emitter), next);
});
} else {
pkgs = _.uniq(pkgs);
async.forEach(pkgs, processLinkedPackage.bind(this, emitter), next);
}
},
completion: function (next) {
rimraf(config.completion, function (err) {
if (err) {
emitter.emit('error', err);
return next();
}
template('action', { name: 'completion cleared', shizzle: 'completion cache' })
.on('data', emitter.emit.bind(emitter, 'data'));
next();
});
}
}, emitter.emit.bind(emitter, 'end'));
return emitter;
};

View File

@@ -13,24 +13,39 @@ describe('cache-clean', function () {
function clean(done) {
var del = 0;
rimraf(config.directory, function () {
// Ignore the error if the local directory was not actually deleted
if (++del >= 2) createCache(done);
rimraf(config.cache, function (err) {
if (err) return done(new Error('Unable to remove cache directory'));
if (++del >= 3) createDirs(done);
});
rimraf(config.cache, function () {
// Ignore the error if the cache directory was not actually deleted
if (++del >= 2) createCache(done);
rimraf(config.links, function (err) {
if (err) return done(new Error('Unable to remove links directory'));
if (++del >= 3) createDirs(done);
});
rimraf(__dirname + '/temp', function (err) {
if (err) return done(new Error('Unable to remove temp directory'));
if (++del >= 3) createDirs(done);
});
}
beforeEach(clean);
beforeEach(function (done) {
clean(function (err) {
if (err) return done(err);
fs.mkdirSync(__dirname + '/temp');
done();
});
});
after(clean);
function createCache(done) {
function createDirs(done) {
mkdirp(config.cache, function (err) {
if (err) throw new Error('Unable to create cache');
done();
if (err) return done(new Error('Unable to create cache directory'));
mkdirp(config.links, function (err) {
if (err) return done(new Error('Unable to create links directory'));
done();
});
});
}
@@ -45,9 +60,18 @@ describe('cache-clean', function () {
fs.writeFileSync(path.join(someDir, 'some-other-file'), 'bower is fantastic');
}
function simulateLink(name, linkedPath) {
var dir = path.join(config.links, name);
fs.mkdirSync(linkedPath);
fs.symlinkSync(linkedPath, dir);
}
it('Should clean the entire cache', function (next) {
simulatePackage('some-package');
simulatePackage('other-package');
simulateLink('linked-package', __dirname + '/temp/linked-package');
fs.rmdirSync(__dirname + '/temp/linked-package'); // simulate invalid symlinks
simulateLink('linked-package2', __dirname + '/temp/linked-package2');
var cleaner = cacheClean();
@@ -59,7 +83,19 @@ describe('cache-clean', function () {
glob(config.cache + '/*', function (err, dirs) {
if (err) throw err;
assert(dirs.length === 0);
next();
glob(config.links + '/*', function (err, dirs) {
if (err) throw err;
dirs = dirs.map(function (dir) {
return path.basename(dir);
});
assert(dirs.length === 1);
assert.deepEqual(dirs, ['linked-package2']);
next();
});
});
});
});
@@ -88,17 +124,28 @@ describe('cache-clean', function () {
});
});
it('Should throw error on unknown package', function (next) {
var cleaner = cacheClean(['not-cached-package']),
cleanedPkg = false;
it('Should clean only the selected links', function (next) {
simulateLink('linked-package', __dirname + '/temp/linked-package');
fs.rmdirSync(__dirname + '/temp/linked-package'); // simulate invalid symlinks
simulateLink('linked-package2', __dirname + '/temp/linked-package2');
fs.rmdirSync(__dirname + '/temp/linked-package2'); // simulate invalid symlinks
var cleaner = cacheClean(['linked-package']);
cleaner.on('error', function (err) {
if (/not\-cached\-package/.test(err)) cleanedPkg = true;
throw err;
});
cleaner.on('end', function () {
if (!cleanedPkg) throw new Error('Should have thrown an error.');
next();
glob(config.links + '/*', function (err, dirs) {
if (err) throw err;
dirs = dirs.map(function (dir) {
return path.basename(dir);
});
assert.deepEqual(dirs, ['linked-package2']);
next();
});
});
});