-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: improved error handling on the CLI #1335
Changes from 6 commits
43c0c13
6924e35
40111af
6dbb834
c649a98
2bdfeee
43bcafa
34bab95
7209da3
7f6fccc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,8 @@ | |
| test/repo-tests* | ||
| **/bundle.js | ||
| docs | ||
| .vscode | ||
| .eslintrc | ||
| # Logs | ||
| logs | ||
| *.log | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,104 +2,161 @@ | |
|
|
||
| 'use strict' | ||
|
|
||
| const yargs = require('yargs') | ||
| const updateNotifier = require('update-notifier') | ||
| const readPkgUp = require('read-pkg-up') | ||
| const fs = require('fs') | ||
| const path = require('path') | ||
| const utils = require('./utils') | ||
| const print = utils.print | ||
| const yargs = require('yargs/yargs') | ||
| const updateNotifier = require('update-notifier') | ||
| const readPkgUp = require('read-pkg-up') | ||
| const { disablePrinting, print, getNodeOrAPI } = require('./utils') | ||
| const addCmd = require('./commands/files/add') | ||
| const catCmd = require('./commands/files/cat') | ||
| const getCmd = require('./commands/files/get') | ||
|
|
||
| const pkg = readPkgUp.sync({cwd: __dirname}).pkg | ||
|
|
||
| updateNotifier({ | ||
| pkg, | ||
| updateCheckInterval: 1000 * 60 * 60 * 24 * 7 // 1 week | ||
| }).notify() | ||
|
|
||
| const args = process.argv.slice(2) | ||
| const MSG_USAGE = `Usage: | ||
| ipfs - Global p2p merkle-dag filesystem. | ||
| ipfs [options] <command> ...` | ||
| const MSG_EPILOGUE = `Use 'ipfs <command> --help' to learn more about each command. | ||
|
||
| ipfs uses a repository in the local file system. By default, the repo is | ||
| located at ~/.ipfs. To change the repo location, set the $IPFS_PATH | ||
| environment variable: | ||
| export IPFS_PATH=/path/to/ipfsrepo | ||
| // Determine if the first argument is a sub-system command | ||
| EXIT STATUS | ||
| The CLI will exit with one of the following values: | ||
| 0 Successful execution. | ||
| 1 Failed executions. | ||
| ` | ||
| const MSG_NO_CMD = 'You need at least one command before moving on' | ||
|
|
||
| const argv = process.argv.slice(2) | ||
| const commandNames = fs.readdirSync(path.join(__dirname, 'commands')) | ||
| const isCommand = commandNames.includes(`${args[0]}.js`) | ||
| const isCommand = commandNames.includes(`${argv[0]}.js`) | ||
|
|
||
| const cli = yargs | ||
| let args = {} | ||
| let cli = yargs(argv) | ||
| .usage(MSG_USAGE) | ||
| .option('silent', { | ||
| desc: 'Write no output', | ||
| type: 'boolean', | ||
| default: false, | ||
| coerce: ('silent', silent => silent ? utils.disablePrinting() : silent) | ||
| coerce: disablePrinting | ||
| }) | ||
| .option('debug', { | ||
| desc: 'Show debug output', | ||
| type: 'boolean', | ||
| default: false, | ||
| alias: 'D' | ||
| }) | ||
| .option('pass', { | ||
| desc: 'Pass phrase for the keys', | ||
| type: 'string', | ||
| default: '' | ||
| }) | ||
| .option('api', { | ||
| desc: 'Use a specific API instance.', | ||
| type: 'string' | ||
| }) | ||
| .commandDir('commands', { | ||
| // Only include the commands for the sub-system we're using, or include all | ||
| // if no sub-system command has been passed. | ||
| include (path, filename) { | ||
| if (!isCommand) return true | ||
| return `${args[0]}.js` === filename | ||
| return `${argv[0]}.js` === filename | ||
| } | ||
| }) | ||
| .epilog(utils.ipfsPathHelp) | ||
| .demandCommand(1) | ||
| .fail((msg, err, yargs) => { | ||
| if (err) { | ||
| throw err // preserve stack | ||
|
|
||
| if(!isCommand){ | ||
| cli | ||
| // NOTE: This creates an alias of | ||
| // `jsipfs files {add, get, cat}` to `jsipfs {add, get, cat}`. | ||
| // This will stay until https://github.com/ipfs/specs/issues/98 is resolved. | ||
| .command(addCmd) | ||
| .command(catCmd) | ||
| .command(getCmd) | ||
| } | ||
| cli | ||
| .demandCommand(1, MSG_NO_CMD) | ||
| .alias('help', 'h') | ||
| .epilogue(MSG_EPILOGUE) | ||
| .strict() | ||
| // .recommendCommands() | ||
| .completion() | ||
|
|
||
| if (['daemon', 'init', 'id', 'version'].includes(argv[0])) { | ||
| args = cli.fail((msg, err, yargs) => { | ||
| if (err instanceof Error && err.message && !msg) { | ||
| msg = err.message | ||
| } | ||
|
|
||
| if (args.length > 0) { | ||
| print(msg) | ||
| // Cli specific error messages | ||
| if (err && err.code === 'ERR_REPO_NOT_INITIALIZED') { | ||
| msg = `No IPFS repo found in ${err.path}. | ||
| please run: 'ipfs init'` | ||
| } | ||
|
|
||
| yargs.showHelp() | ||
| }) | ||
| // Show help and error message | ||
| if (!args.silent) { | ||
| yargs.showHelp() | ||
| console.error('Error: ' + msg) | ||
| } | ||
|
|
||
| // If not a sub-system command then load the top level aliases | ||
| if (!isCommand) { | ||
| // NOTE: This creates an alias of | ||
| // `jsipfs files {add, get, cat}` to `jsipfs {add, get, cat}`. | ||
| // This will stay until https://github.com/ipfs/specs/issues/98 is resolved. | ||
| const addCmd = require('./commands/files/add') | ||
| const catCmd = require('./commands/files/cat') | ||
| const getCmd = require('./commands/files/get') | ||
| const aliases = [addCmd, catCmd, getCmd] | ||
| aliases.forEach((alias) => { | ||
| cli.command(alias.command, alias.describe, alias.builder, alias.handler) | ||
| }) | ||
| } | ||
| // Write to stderr when debug is on | ||
| if (err && args.debug) { | ||
| console.error(err) | ||
| } | ||
|
|
||
| // Need to skip to avoid locking as these commands | ||
| // don't require a daemon | ||
| if (args[0] === 'daemon' || args[0] === 'init') { | ||
| cli | ||
| .help() | ||
| .strict() | ||
| .completion() | ||
| .parse(args) | ||
| process.exit(1) | ||
| }).argv | ||
| } else { | ||
| // here we have to make a separate yargs instance with | ||
| // only the `api` option because we need this before doing | ||
| // the final yargs parse where the command handler is invoked.. | ||
| yargs().option('api').parse(process.argv, (err, argv, output) => { | ||
| if (err) { | ||
| throw err | ||
| } | ||
| utils.getIPFS(argv, (err, ipfs, cleanup) => { | ||
| if (err) { throw err } | ||
|
|
||
| cli | ||
| .help() | ||
| .strict() | ||
| .completion() | ||
| .parse(args, { ipfs: ipfs }, (err, argv, output) => { | ||
| if (output) { print(output) } | ||
|
|
||
| cleanup(() => { | ||
| if (err) { throw err } | ||
| yargs() | ||
| .option('pass', { | ||
| desc: 'Pass phrase for the keys', | ||
| type: 'string', | ||
| default: '' | ||
| }) | ||
| .option('api', { | ||
| desc: 'Use a specific API instance.', | ||
| type: 'string' | ||
| }) | ||
| .parse(argv, (err, parsedArgv, output) => { | ||
| if (err) { | ||
| console.error(err) | ||
| } else { | ||
| getNodeOrAPI(parsedArgv) | ||
| .then(node => { | ||
| args = cli | ||
| .parse(argv, { ipfs: node }, (err, parsedArgv, output) => { | ||
| if (output) { | ||
| print(output) | ||
| } | ||
| if (node && node._repo && !node._repo.closed) { | ||
| node._repo.close(err => { | ||
| if (err) { | ||
| console.error(err) | ||
| } | ||
| }) | ||
| } | ||
| if (err && parsedArgv.debug) { | ||
| console.error(err) | ||
| } | ||
| }) | ||
| }) | ||
| .catch(err => { | ||
| console.error(err) | ||
| process.exit(1) | ||
| }) | ||
| }) | ||
| } | ||
| }) | ||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| 'use strict' | ||
| const print = require('../utils').print | ||
|
|
||
| const {print, getNodeOrAPI} = require('../utils') | ||
|
|
||
| module.exports = { | ||
| command: 'id', | ||
|
|
@@ -15,12 +16,11 @@ module.exports = { | |
|
|
||
| handler (argv) { | ||
| // TODO: handle argv.format | ||
| argv.ipfs.id((err, id) => { | ||
| if (err) { | ||
| throw err | ||
| } | ||
|
|
||
| print(JSON.stringify(id, '', 2)) | ||
| }) | ||
| return getNodeOrAPI(argv) | ||
| .then(node => Promise.all([Promise.resolve(node), node.id()])) | ||
| .then(([node, id]) => { | ||
| print(JSON.stringify(id, '', 2)) | ||
| node.stop() | ||
|
||
| }) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,8 +2,7 @@ | |
|
|
||
| const Repo = require('ipfs-repo') | ||
| const IPFS = require('../../core') | ||
| const utils = require('../utils') | ||
| const print = utils.print | ||
| const { ipfsPathHelp, getRepoPath, print } = require('../utils') | ||
|
|
||
| module.exports = { | ||
| command: 'init', | ||
|
|
@@ -12,7 +11,7 @@ module.exports = { | |
|
|
||
| builder (yargs) { | ||
| return yargs | ||
| .epilog(utils.ipfsPathHelp) | ||
| .epilog(ipfsPathHelp) | ||
| .option('bits', { | ||
| type: 'number', | ||
| alias: 'b', | ||
|
|
@@ -22,33 +21,26 @@ module.exports = { | |
| .option('emptyRepo', { | ||
| alias: 'e', | ||
| type: 'boolean', | ||
| describe: "Don't add and pin help files to the local storage" | ||
| describe: 'Don\'t add and pin help files to the local storage' | ||
| }) | ||
| }, | ||
|
|
||
| handler (argv) { | ||
| const path = utils.getRepoPath() | ||
| const path = getRepoPath() | ||
|
|
||
| print(`initializing ipfs node at ${path}`) | ||
|
|
||
| const node = new IPFS({ | ||
| return IPFS.createNodePromise({ | ||
| repo: new Repo(path), | ||
| init: false, | ||
| start: false | ||
| }) | ||
|
||
|
|
||
| node.init({ | ||
| bits: argv.bits, | ||
| emptyRepo: argv.emptyRepo, | ||
| pass: argv.pass, | ||
| log: print | ||
| }, (err) => { | ||
| if (err) { | ||
| if (err.code === 'EACCES') { | ||
| err.message = `EACCES: permission denied, stat $IPFS_PATH/version` | ||
| } | ||
| throw err | ||
| } | ||
| }).then(node => { | ||
| return node.init({ | ||
| bits: argv.bits, | ||
| emptyRepo: argv.emptyRepo, | ||
| pass: argv.pass, | ||
| log: print | ||
| }) | ||
| }) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's faster if these 3 are only required when needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed with last commit