Skip to content

Commit 6f1286f

Browse files
committed
Fix infinite install loop.
Retry the download+install dance only once after encountering an EACCES. That only happens when both the devdir (usually: `$HOME/.node-gyp`) and the current working directory aren't writable. Users won't often hit that except through `sudo npm install` because npm drops privileges before executing node-gyp. Fixes: #1383 PR-URL: #1384 Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com> Reviewed-By: Richard Lau <riclau@uk.ibm.com>
1 parent 2580b91 commit 6f1286f

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

lib/install.js

+15-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
module.exports = exports = function (gyp, argv, callback) {
2+
return install(fs, gyp, argv, callback)
3+
}
14

2-
module.exports = exports = install
3-
4-
module.exports.test = { download: download, readCAFile: readCAFile }
5+
module.exports.test = {
6+
download: download,
7+
install: install,
8+
readCAFile: readCAFile,
9+
}
510

611
exports.usage = 'Install node development files for the specified node version.'
712

@@ -25,7 +30,7 @@ var fs = require('graceful-fs')
2530
, processRelease = require('./process-release')
2631
, win = process.platform == 'win32'
2732

28-
function install (gyp, argv, callback) {
33+
function install (fs, gyp, argv, callback) {
2934

3035
var release = processRelease(argv, gyp, process.version, process.release)
3136

@@ -84,7 +89,7 @@ function install (gyp, argv, callback) {
8489
log.verbose('install', 'version not already installed, continuing with install', release.version)
8590
go()
8691
} else if (err.code == 'EACCES') {
87-
eaccesFallback()
92+
eaccesFallback(err)
8893
} else {
8994
cb(err)
9095
}
@@ -129,7 +134,7 @@ function install (gyp, argv, callback) {
129134
mkdir(devDir, function (err, created) {
130135
if (err) {
131136
if (err.code == 'EACCES') {
132-
eaccesFallback()
137+
eaccesFallback(err)
133138
} else {
134139
cb(err)
135140
}
@@ -409,7 +414,9 @@ function install (gyp, argv, callback) {
409414
* the compilation will succeed...
410415
*/
411416

412-
function eaccesFallback () {
417+
function eaccesFallback (err) {
418+
var noretry = '--node_gyp_internal_noretry'
419+
if (-1 !== argv.indexOf(noretry)) return cb(err)
413420
var tmpdir = osenv.tmpdir()
414421
gyp.devDir = path.resolve(tmpdir, '.node-gyp')
415422
log.warn('EACCES', 'user "%s" does not have permission to access the dev dir "%s"', osenv.user(), devDir)
@@ -418,7 +425,7 @@ function install (gyp, argv, callback) {
418425
log.verbose('tmpdir == cwd', 'automatically will remove dev files after to save disk space')
419426
gyp.todo.push({ name: 'remove', args: argv })
420427
}
421-
gyp.commands.install(argv, cb)
428+
gyp.commands.install([noretry].concat(argv), cb)
422429
}
423430

424431
}

test/test-install.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict'
2+
3+
var test = require('tape')
4+
var install = require('../lib/install').test.install
5+
6+
test('EACCES retry once', function (t) {
7+
t.plan(3)
8+
9+
var fs = {}
10+
fs.stat = function (path, cb) {
11+
var err = new Error()
12+
err.code = 'EACCES'
13+
cb(err)
14+
t.ok(true);
15+
}
16+
17+
18+
var gyp = {}
19+
gyp.devDir = __dirname
20+
gyp.opts = {}
21+
gyp.opts.ensure = true
22+
gyp.commands = {}
23+
gyp.commands.install = function (argv, cb) {
24+
install(fs, gyp, argv, cb)
25+
}
26+
gyp.commands.remove = function (argv, cb) {
27+
cb()
28+
}
29+
30+
gyp.commands.install([], function (err) {
31+
t.ok(true)
32+
if (/"pre" versions of node cannot be installed/.test(err.message)) {
33+
t.ok(true)
34+
t.ok(true)
35+
}
36+
})
37+
})

0 commit comments

Comments
 (0)