Commit a14fbe5b by Michael Mifsud Committed by GitHub

Merge pull request #1782 from sass/feat/cache-path-candidates

Handle permission errors when attempting to cache binaries
parents e54581d7 d1bfd548
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
var eol = require('os').EOL, var eol = require('os').EOL,
fs = require('fs'), fs = require('fs'),
pkg = require('../package.json'), pkg = require('../package.json'),
mkdir = require('mkdirp'),
path = require('path'), path = require('path'),
defaultBinaryPath = path.join(__dirname, '..', 'vendor'); defaultBinaryPath = path.join(__dirname, '..', 'vendor');
...@@ -262,6 +263,79 @@ function getBinaryPath() { ...@@ -262,6 +263,79 @@ function getBinaryPath() {
} }
/** /**
* An array of paths suitable for use as a local disk cache of the binding.
*
* @return {[]String} an array of paths
* @api public
*/
function getCachePathCandidates() {
return [
process.env.npm_config_sass_binary_cache,
process.env.npm_config_cache,
].filter(function(_) { return _; });
}
/**
* The most suitable location for caching the binding on disk.
*
* Given the candidates directories provided by `getCachePathCandidates()` this
* returns the first writable directory. By treating the candidate directories
* as a prioritised list this method is deterministic, assuming no change to the
* local environment.
*
* @return {String} directory to cache binding
* @api public
*/
function getBinaryCachePath() {
var i,
cachePath,
cachePathCandidates = getCachePathCandidates();
for (i = 0; i < cachePathCandidates.length; i++) {
cachePath = path.join(cachePathCandidates[i], pkg.name, pkg.version);
try {
mkdir.sync(cachePath);
return cachePath;
} catch (e) {
// Directory is not writable, try another
}
}
return '';
}
/**
* The cached binding
*
* Check the candidates directories provided by `getCachePathCandidates()` for
* the binding file, if it exists. By treating the candidate directories
* as a prioritised list this method is deterministic, assuming no change to the
* local environment.
*
* @return {String} path to cached binary
* @api public
*/
function getCachedBinary() {
var i,
cachePath,
cacheBinary,
cachePathCandidates = getCachePathCandidates(),
binaryName = getBinaryName();
for (i = 0; i < cachePathCandidates.length; i++) {
cachePath = path.join(cachePathCandidates[i], pkg.name, pkg.version);
cacheBinary = path.join(cachePath, binaryName);
if (fs.existsSync(cacheBinary)) {
return cacheBinary;
}
}
return '';
}
/**
* Does the supplied binary path exist * Does the supplied binary path exist
* *
* @param {String} binaryPath * @param {String} binaryPath
...@@ -289,6 +363,9 @@ module.exports.hasBinary = hasBinary; ...@@ -289,6 +363,9 @@ module.exports.hasBinary = hasBinary;
module.exports.getBinaryUrl = getBinaryUrl; module.exports.getBinaryUrl = getBinaryUrl;
module.exports.getBinaryName = getBinaryName; module.exports.getBinaryName = getBinaryName;
module.exports.getBinaryPath = getBinaryPath; module.exports.getBinaryPath = getBinaryPath;
module.exports.getBinaryCachePath = getBinaryCachePath;
module.exports.getCachedBinary = getCachedBinary;
module.exports.getCachePathCandidates = getCachePathCandidates;
module.exports.getVersionInfo = getVersionInfo; module.exports.getVersionInfo = getVersionInfo;
module.exports.getHumanEnvironment = getHumanEnvironment; module.exports.getHumanEnvironment = getHumanEnvironment;
module.exports.getInstalledBinaries = getInstalledBinaries; module.exports.getInstalledBinaries = getInstalledBinaries;
......
...@@ -93,34 +93,54 @@ function download(url, dest, cb) { ...@@ -93,34 +93,54 @@ function download(url, dest, cb) {
*/ */
function checkAndDownloadBinary() { function checkAndDownloadBinary() {
if (sass.hasBinary(sass.getBinaryPath())) { if (process.env.SKIP_SASS_BINARY_DOWNLOAD_FOR_CI) {
log.info('node-sass install', 'Skipping downloading binaries on CI builds');
return; return;
} }
mkdir(path.dirname(sass.getBinaryPath()), function(err) { var cachedBinary = sass.getCachedBinary(),
cachePath = sass.getBinaryCachePath(),
binaryPath = sass.getBinaryPath();
if (sass.hasBinary(binaryPath)) {
log.info('node-sass build', 'Binary found at %s', binaryPath);
return;
}
try {
mkdir.sync(path.dirname(binaryPath));
} catch (err) {
log.error('node-sass install', 'Unable to save binary to %s: %s', path.dirname(binaryPath), err);
return;
}
if (cachedBinary) {
log.info('node-sass install', 'Cached binary found at %s', cachedBinary);
fs.createReadStream(cachedBinary).pipe(fs.createWriteStream(binaryPath));
return;
}
download(sass.getBinaryUrl(), binaryPath, function(err) {
if (err) { if (err) {
log.error('node-sass install', err); log.error('node-sass install', err);
return; return;
} }
download(sass.getBinaryUrl(), sass.getBinaryPath(), function(err) { log.info('node-sass install', 'Binary saved at %s', binaryPath);
if (err) {
log.error('node-sass install', err);
return;
}
log.info('node-sass install', 'Binary saved at %s', sass.getBinaryPath()); cachedBinary = path.join(cachePath, sass.getBinaryName());
});
});
}
/** if (cachePath) {
* Skip if CI log.info('node-sass install', 'Caching binary to %s', cachedBinary);
*/
if (process.env.SKIP_SASS_BINARY_DOWNLOAD_FOR_CI) { try {
log.info('node-sass install', 'Skipping downloading binaries on CI builds'); mkdir.sync(path.dirname(cachedBinary));
return; fs.createReadStream(binaryPath).pipe(fs.createWriteStream(cachedBinary));
} catch (err) {
log.error('node-sass install', 'Failed to cache binary: %s', err);
}
}
});
} }
/** /**
......
...@@ -115,6 +115,27 @@ describe('runtime parameters', function() { ...@@ -115,6 +115,27 @@ describe('runtime parameters', function() {
}); });
}); });
describe.skip('Sass Binary Cache', function() {
var npmCacheDir;
before(function() {
npmCacheDir = process.env.npm_config_cache;
});
beforeEach(function() {
delete process.env.npm_config_sass_binary_cache;
});
it('npm config variable', function() {
var overridenCachePath = '/foo/bar/';
process.env.npm_config_sass_binary_cache = overridenCachePath;
assert.equal(sass.getCachePath(), overridenCachePath);
});
it('With no value, falls back to NPM cache', function() {
assert.equal(sass.getCachePath(), npmCacheDir);
});
});
}); });
// describe('library detection', function() { // describe('library detection', function() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment