Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1782 from sass/feat/cache-path-candidates
Browse files Browse the repository at this point in the history
Handle permission errors when attempting to cache binaries
  • Loading branch information
xzyfer authored Nov 13, 2016
2 parents e54581d + d1bfd54 commit a14fbe5
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 17 deletions.
77 changes: 77 additions & 0 deletions lib/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
var eol = require('os').EOL,
fs = require('fs'),
pkg = require('../package.json'),
mkdir = require('mkdirp'),
path = require('path'),
defaultBinaryPath = path.join(__dirname, '..', 'vendor');

Expand Down Expand Up @@ -261,6 +262,79 @@ function getBinaryPath() {
return binaryPath;
}

/**
* 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
*
Expand Down Expand Up @@ -289,6 +363,9 @@ module.exports.hasBinary = hasBinary;
module.exports.getBinaryUrl = getBinaryUrl;
module.exports.getBinaryName = getBinaryName;
module.exports.getBinaryPath = getBinaryPath;
module.exports.getBinaryCachePath = getBinaryCachePath;
module.exports.getCachedBinary = getCachedBinary;
module.exports.getCachePathCandidates = getCachePathCandidates;
module.exports.getVersionInfo = getVersionInfo;
module.exports.getHumanEnvironment = getHumanEnvironment;
module.exports.getInstalledBinaries = getInstalledBinaries;
Expand Down
54 changes: 37 additions & 17 deletions scripts/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,34 +93,54 @@ function download(url, dest, cb) {
*/

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;
}

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) {
log.error('node-sass install', err);
return;
}

download(sass.getBinaryUrl(), sass.getBinaryPath(), function(err) {
if (err) {
log.error('node-sass install', err);
return;
}
log.info('node-sass install', 'Binary saved at %s', binaryPath);

log.info('node-sass install', 'Binary saved at %s', sass.getBinaryPath());
});
});
}
cachedBinary = path.join(cachePath, sass.getBinaryName());

/**
* Skip if CI
*/
if (cachePath) {
log.info('node-sass install', 'Caching binary to %s', cachedBinary);

if (process.env.SKIP_SASS_BINARY_DOWNLOAD_FOR_CI) {
log.info('node-sass install', 'Skipping downloading binaries on CI builds');
return;
try {
mkdir.sync(path.dirname(cachedBinary));
fs.createReadStream(binaryPath).pipe(fs.createWriteStream(cachedBinary));
} catch (err) {
log.error('node-sass install', 'Failed to cache binary: %s', err);
}
}
});
}

/**
Expand Down
21 changes: 21 additions & 0 deletions test/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down

0 comments on commit a14fbe5

Please sign in to comment.