forked from npm/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This command will diagnose user's environment and let the user know some recommended solutions if they potentially have any problems related to npm. Credit: @watilde Reviewed-By: @othiym23 Reviewed-By: @iarna PR-URL: npm/npm#14582 Fixes: npm#6756
- Loading branch information
Showing
11 changed files
with
406 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
npm-doctor(1) -- Check your environments | ||
======================================================== | ||
|
||
## SYNOPSIS | ||
|
||
npm doctor | ||
|
||
## DESCRIPTION | ||
|
||
npm command is just a single command, but depends on several things outside | ||
the code base. Broadly speaking, it is the following three types: | ||
|
||
+ Technology stack: Node.js, git | ||
+ Registry: `registry.npmjs.com` | ||
+ Files: `node_modules` in local/global, cached files in `npm config get cache` | ||
|
||
Without all of these working properly, the npm command will not work properly. | ||
Many issue reports that arrive under us are often attributable to things that | ||
are outside the code base as described above, and it is necessary to confirm | ||
that this is correctly set with one command would help you solve your issue. | ||
Also, in addition to this, there are also very many issue reports due to using | ||
old versions of npm. Since npm is constantly improving, in every aspect the | ||
`latest` npm is better than the old version(it should be, and we are trying). | ||
|
||
From the above reasons, `npm doctor` will investigate the following items in | ||
your environment and if there are any other recommended settings, it will | ||
display the recommended example. | ||
|
||
|What to check|What we recommend| | ||
|---|---| | ||
|`npm ping`|It must be able to communicate with the registry| | ||
|`npm -v`|It is preferable that the latest version of LTS is used| | ||
|`node -v`|It is preferable that the latest version of LTS is used| | ||
|`npm config get registry`|In order not to consider exceptions, it is better to set default values `registry.npmjs.org`| | ||
|`which git`|Git has to be installed| | ||
|`Perms check on cached files`|All cached module files must be readable.| | ||
|`Perms check on global node_modules`|All global module files must be executable.| | ||
|`Perms check on local node_modules`|All local module files must be executable.| | ||
|`Checksum cached files`|All cached files must not be broken| | ||
|
||
## SEE ALSO | ||
|
||
* npm-bugs(1) | ||
* npm-help(1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
var path = require('path') | ||
var chain = require('slide').chain | ||
var table = require('text-table') | ||
var color = require('ansicolors') | ||
var styles = require('ansistyles') | ||
var semver = require('semver') | ||
var npm = require('./npm.js') | ||
var log = require('npmlog') | ||
var ansiTrim = require('./utils/ansi-trim.js') | ||
var output = require('./utils/output.js') | ||
var defaultRegistry = require('./config/defaults.js').defaults.registry | ||
var checkPing = require('./doctor/check-ping.js') | ||
var getGitPath = require('./doctor/get-git-path.js') | ||
var checksumCachedFiles = require('./doctor/checksum-cached-files.js') | ||
var checkFilesPermission = require('./doctor/check-files-permission.js') | ||
var getLatestNodejsVersion = require('./doctor/get-latest-nodejs-version.js') | ||
var getLatestNpmVersion = require('./doctor/get-latest-npm-version') | ||
var globalNodeModules = path.join(npm.config.globalPrefix, 'lib', 'node_modules') | ||
var localNodeModules = path.join(npm.config.localPrefix, 'node_modules') | ||
|
||
module.exports = doctor | ||
|
||
doctor.usage = 'npm doctor' | ||
|
||
function doctor (args, silent, cb) { | ||
args = args || {} | ||
if (typeof cb !== 'function') { | ||
cb = silent | ||
silent = false | ||
} | ||
|
||
var actionsToRun = [ | ||
[checkPing], | ||
[getLatestNpmVersion], | ||
[getLatestNodejsVersion, args['node-url']], | ||
[getGitPath], | ||
[checkFilesPermission, npm.cache, 6], | ||
[checkFilesPermission, globalNodeModules, 4], | ||
[checkFilesPermission, localNodeModules, 6], | ||
[checksumCachedFiles] | ||
] | ||
|
||
log.info('doctor', 'Running checkup') | ||
chain(actionsToRun, function (stderr, stdout) { | ||
if (stderr && stderr.message !== 'not found: git') return cb(stderr) | ||
var outHead = ['Check', 'Value', 'Recommendation'] | ||
var list = makePretty(stdout) | ||
var outBody = list | ||
|
||
if (npm.color) { | ||
outHead = outHead.map(function (item) { | ||
return styles.underline(item) | ||
}) | ||
outBody = outBody.map(function (item) { | ||
if (item[2]) { | ||
item[0] = color.red(item[0]) | ||
item[2] = color.magenta(item[2]) | ||
} | ||
return item | ||
}) | ||
} | ||
|
||
var outTable = [outHead].concat(outBody) | ||
var tableOpts = { | ||
stringLength: function (s) { return ansiTrim(s).length } | ||
} | ||
|
||
if (!silent) output(table(outTable, tableOpts)) | ||
|
||
cb(null, list) | ||
}) | ||
} | ||
|
||
function makePretty (p) { | ||
var ping = p[0] ? 'ok' : 'notOk' | ||
var npmLTS = p[1] | ||
var nodeLTS = p[2].replace('v', '') | ||
var whichGit = p[3] || 'not installed' | ||
var readbleCaches = p[4] ? 'ok' : 'notOk' | ||
var executableGlobalModules = p[5] ? 'ok' : 'notOk' | ||
var executableLocalModules = p[6] ? 'ok' : 'notOk' | ||
var checksumCachedFiles = p[7] ? 'ok' : 'notOk' | ||
var npmV = npm.version | ||
var nodeV = process.version.replace('v', '') | ||
var registry = npm.config.get('registry') | ||
var list = [ | ||
['npm ping', ping], | ||
['npm -v', 'v' + npmV], | ||
['node -v', 'v' + nodeV], | ||
['npm config get registry', registry], | ||
['which git', whichGit], | ||
['Perms check on cached files', readbleCaches], | ||
['Perms check on global node_modules', executableGlobalModules], | ||
['Perms check on local node_modules', executableLocalModules], | ||
['Checksum cached files', checksumCachedFiles] | ||
] | ||
|
||
if (ping !== 'ok') list[0][2] = 'Check your internet connection' | ||
if (!semver.satisfies(npmV, '>=' + npmLTS)) list[1][2] = 'Use npm v' + npmLTS | ||
if (!semver.satisfies(nodeV, '>=' + nodeLTS)) list[2][2] = 'Use node v' + nodeLTS | ||
if (registry !== defaultRegistry) list[3][2] = 'Try `npm config set ' + defaultRegistry | ||
if (whichGit === 'not installed') list[4][2] = 'Install git' | ||
if (readbleCaches !== 'ok') list[5][2] = 'Check the permission of your files in ' + npm.config.get('cache') | ||
if (executableGlobalModules !== 'ok') list[6][2] = 'Check the permission of your files in ' + globalNodeModules | ||
if (executableLocalModules !== 'ok') list[7][2] = 'Check the permission of your files in ' + localNodeModules | ||
if (checksumCachedFiles !== 'ok') list[8][2] = 'You have some broken packages in your cache' | ||
|
||
return list | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
var fs = require('fs') | ||
var path = require('path') | ||
var getUid = require('uid-number') | ||
var chain = require('slide').chain | ||
var log = require('npmlog') | ||
var npm = require('../npm.js') | ||
var fileCompletion = require('../utils/completion/file-completion.js') | ||
|
||
function checkFilesPermission (root, mask, cb) { | ||
if (process.platform === 'win32') return cb(null, true) | ||
getUid(npm.config.get('user'), npm.config.get('group'), function (e, uid, gid) { | ||
if (e) { | ||
tracker.finish() | ||
tracker.warn('checkFilePermissions', 'Error looking up user and group:', e) | ||
return cb(e) | ||
} | ||
var tracker = log.newItem('checkFilePermissions', 1) | ||
tracker.info('checkFilePermissions', 'Building file list of ' + root) | ||
fileCompletion(root, '.', Infinity, function (e, files) { | ||
if (e) { | ||
tracker.warn('checkFilePermissions', 'Error building file list:', e) | ||
tracker.finish() | ||
return cb(e) | ||
} | ||
tracker.addWork(files.length) | ||
tracker.completeWork(1) | ||
chain(files.map(andCheckFile), function (er) { | ||
tracker.finish() | ||
cb(null, !er) | ||
}) | ||
function andCheckFile (f) { | ||
return [checkFile, f] | ||
} | ||
function checkFile (f, next) { | ||
var file = path.join(root, f) | ||
tracker.silly('checkFilePermissions', f) | ||
fs.stat(file, function (e, stat) { | ||
tracker.completeWork(1) | ||
if (e) return next(e) | ||
if (!stat.isFile()) return next() | ||
var mode = stat.mode | ||
var isGroup = stat.gid ? stat.gid === gid : true | ||
var isUser = stat.uid ? stat.uid === uid : true | ||
if ((mode & parseInt('000' + mask, 8))) return next() | ||
if ((isGroup && mode & parseInt('00' + mask + '0', 8))) return next() | ||
if ((isUser && mode & parseInt('0' + mask + '00', 8))) return next() | ||
tracker.error('checkFilePermissions', 'Missing permissions on (' + isGroup + ', ' + isUser + ', ' + mode + ')', file) | ||
return next(new Error('Missing permissions for ' + file)) | ||
}) | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
module.exports = checkFilesPermission |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
var log = require('npmlog') | ||
var ping = require('../ping.js') | ||
|
||
function checkPing (cb) { | ||
var tracker = log.newItem('checkPing', 1) | ||
tracker.info('checkPing', 'Pinging registry') | ||
ping({}, true, function (err, pong) { | ||
tracker.finish() | ||
cb(err, pong) | ||
}) | ||
} | ||
|
||
module.exports = checkPing |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
var crypto = require('crypto') | ||
var fs = require('fs') | ||
var path = require('path') | ||
var chain = require('slide').chain | ||
var log = require('npmlog') | ||
var npm = require('../npm') | ||
var fileCompletion = require('../utils/completion/file-completion.js') | ||
|
||
function checksum (str) { | ||
return crypto | ||
.createHash('sha1') | ||
.update(str, 'utf8') | ||
.digest('hex') | ||
} | ||
|
||
function checksumCachedFiles (cb) { | ||
var tracker = log.newItem('checksumCachedFiles', 1) | ||
tracker.info('checksumCachedFiles', 'Building file list of ' + npm.cache) | ||
fileCompletion(npm.cache, '.', Infinity, function (e, files) { | ||
if (e) { | ||
tracker.finish() | ||
return cb(e) | ||
} | ||
tracker.addWork(files.length) | ||
tracker.completeWork(1) | ||
chain(files.map(andChecksumFile), function (er) { | ||
tracker.finish() | ||
cb(null, !er) | ||
}) | ||
function andChecksumFile (f) { | ||
return [function (next) { process.nextTick(function () { checksumFile(f, next) }) }] | ||
} | ||
function checksumFile (f, next) { | ||
var file = path.join(npm.cache, f) | ||
tracker.silly('checksumFile', f) | ||
if (!/.tgz$/.test(file)) { | ||
tracker.completeWork(1) | ||
return next() | ||
} | ||
fs.readFile(file, function (err, tgz) { | ||
tracker.completeWork(1) | ||
if (err) return next(err) | ||
try { | ||
var pkgJSON = fs.readFileSync(path.join(path.dirname(file), 'package/package.json')) | ||
} catch (e) { | ||
return next() // no package.json in cche is ok | ||
} | ||
try { | ||
var pkg = JSON.parse(pkgJSON) | ||
var shasum = (pkg.dist && pkg.dist.shasum) || pkg._shasum | ||
var actual = checksum(tgz) | ||
if (actual !== shasum) return next(new Error('Checksum mismatch on ' + file + ', expected: ' + shasum + ', got: ' + shasum)) | ||
return next() | ||
} catch (e) { | ||
return next(new Error('Error parsing JSON in ' + file + ': ' + e)) | ||
} | ||
}) | ||
} | ||
}) | ||
} | ||
|
||
module.exports = checksumCachedFiles |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
var log = require('npmlog') | ||
var which = require('which') | ||
|
||
function getGitPath (cb) { | ||
var tracker = log.newItem('getGitPath', 1) | ||
tracker.info('getGitPath', 'Finding git in your PATH') | ||
which('git', function (err, path) { | ||
tracker.finish() | ||
cb(err, path) | ||
}) | ||
} | ||
|
||
module.exports = getGitPath |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
var log = require('npmlog') | ||
var request = require('request') | ||
|
||
function getLatestNodejsVersion (url, cb) { | ||
var tracker = log.newItem('getLatestNodejsVersion', 1) | ||
tracker.info('getLatestNodejsVersion', 'Getting Node.js release information') | ||
var version = '' | ||
url = url || 'https://nodejs.org/dist/index.json' | ||
request(url, function (e, res, index) { | ||
tracker.finish() | ||
if (e) return cb(e) | ||
if (res.statusCode !== 200) { | ||
return cb(new Error('Status not 200, ' + res.statusCode)) | ||
} | ||
try { | ||
JSON.parse(index).forEach(function (item) { | ||
if (item.lts && item.version > version) version = item.version | ||
}) | ||
cb(null, version) | ||
} catch (e) { | ||
cb(e) | ||
} | ||
}) | ||
} | ||
|
||
module.exports = getLatestNodejsVersion |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
var log = require('npmlog') | ||
var fetchPackageMetadata = require('../fetch-package-metadata') | ||
|
||
function getLatestNpmVersion (cb) { | ||
var tracker = log.newItem('getLatestNpmVersion', 1) | ||
tracker.info('getLatestNpmVersion', 'Getting npm package information') | ||
fetchPackageMetadata('npm@latest', '.', function (e, d) { | ||
tracker.finish() | ||
cb(e, d.version) | ||
}) | ||
} | ||
|
||
module.exports = getLatestNpmVersion |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.