-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: improved error handling on the CLI #1335
Conversation
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.
dismiss my review. wrong button
Needed PRs have been reviewed and merged. Not blocked anymore |
48b07e4
to
49571a2
Compare
49571a2
to
c649a98
Compare
@diasdavid this PR is complete and with ipfs/js-ipfsd-ctl#241 linked tests pass locally. |
@alanshaw mind doing the last review? |
I'm puzzled on why the "better CLI errors" is the fix for changing the error on ipfs-repo introduced in -- ipfs/js-ipfs-repo@755b5c6 --. |
src/cli/bin.js
Outdated
const { disablePrinting, print, getNodeOrAPI } = require('./utils') | ||
const addCmd = require('./commands/files/add') | ||
const catCmd = require('./commands/files/cat') | ||
const getCmd = require('./commands/files/get') |
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
src/cli/bin.js
Outdated
ipfs - Global p2p merkle-dag filesystem. | ||
|
||
ipfs [options] <command> ...` | ||
const MSG_EPILOGUE = `Use 'ipfs <command> --help' to learn more about each command. |
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.
Minor - too much indentation!
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.
Sorry where? 26 has no identation. It's exactly the same as the go impl.
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.
i see now, its const
eslint auto fixes that wrong.
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
* @param {object} stateOptions - Node state options to reject with error | ||
* @returns {IPFS} | ||
*/ | ||
IPFS.createNodePromise = (options = {}, stateOptions = {}) => { |
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.
The name suggests that the only difference between it and createNode
is that it returns a promise, but actually it creates a node and waits for it to be ready. What about IPFS.startNode()
?
Secondly, since you're adding a new public API function here, is it better to offer a dual promise/callback function like the rest of the API? I'd promisify
this.
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.
Oh, if this is going to add a new public API, it also needs to add documentation for it in the README: https://github.com/ipfs/js-ipfs/blob/master/README.md#api
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.
Yes also!
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.
This is a static factory method, with the objective of creating a node if it is started or initialised depends on the options.
A startNode factory would imply options = {start:true}
.
I can just move this to utils.js
and the scope would be cli only.
src/cli/commands/id.js
Outdated
.then(node => Promise.all([Promise.resolve(node), node.id()])) | ||
.then(([node, id]) => { | ||
print(JSON.stringify(id, '', 2)) | ||
node.stop() |
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.
How come stop()
is called here but not in version.js
?
Do all our commands have to remember to call stop()
after they're done, and also catch any error and call stop()
to clean up correctly? Would it be better to have IPFS created and destroyed once in bin.js
like it is at the moment? Is there another benefit to moving the IPFS node getter into each command?
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.
I'm not sure why this command is requiring a online node? Shouldn't this just work and return the ID without having to start a node? Feels wildly inefficient.
Looking at what go-ipfs does: when you call ID with a online daemon, it returns the ID object but with swarm address. When offline, it returns the same but with addresses empty.
In contrast, what this command seems to be doing is to either grab the API if daemon is running but if none is running, start a node.
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.
That too, but I think that should be fixed outside of this PR.
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.
The more i look, more code paths i see, events, cb(err), fsm, etc. This is tricky. I'm gonna push couple commits to improve further on this.
But still to answer your questions:
How come stop() is called here but not in version.js?
you r right every cmd needs to do cleanup (easier cleanup coming in the next commits)
Do all our commands have to remember to call stop() after they're done, and also catch any error and call stop() to clean up correctly? Would it be better to have IPFS created and destroyed once in bin.js like it is at the moment? Is there another benefit to moving the IPFS node getter into each command?
I think being explicit and each cmd control their own lifecycle is better. Also we could remove all this code https://github.com/ipfs/js-ipfs/blob/master/src/cli/bin.js#L73-L104, which got even bigger in this PR after adding more safe guards and error handling. In the end we would only have one code path ending in fail()
and proper stack traces, as we do now for daemon, init, id and version.
I'm not sure why this command is requiring a online node? Shouldn't this just work and return the ID without having to start a node? Feels wildly inefficient.
Looking at what go-ipfs does: when you call ID with a online daemon, it returns the ID object but with swarm address. When offline, it returns the same but with addresses empty.
In contrast, what this command seems to be doing is to either grab the API if daemon is running but if none is running, start a node.
It works exactly like that now
{
"id": "QmYHif2UK4aFaMYBiNZYrA7xLvYJajYSQt14H5tmHyagvV",
"publicKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBJRc5+YxkfPX1LfwUXGY18ezEUpsUmWLAQxReOdeedbEHly8O7WYfYW1iDV1obVttDAbsYLMLh2gmoPqyMjvcAtA/JFnkCjTJ1GCAJFPvrHly5ZFO67knMTHXfVTaKJRBs98JnwrVgFRAbZAwoeVBRbPeP53FOmiw3pXoraOXU7YvGV3vc29PUTn4+1g4seoznyHAx5Ezp52MspiBvb0QmkEGvqZEKFRBUSz8pqMPCDYtItXMzOoBJG29TxxUztGTHvfogdxY9EOpPxVW6Nm0YRWbs9Vc8m/Vjtb88VQGrXXiplgag8OzyT8qM7Axx44nqOkAnQM3L50lOJ/ezGr9AgMBAAE=",
"addresses": [],
"agentVersion": "js-ipfs/0.28.2",
"protocolVersion": "9000"
}
repo: new Repo(path), | ||
init: false, | ||
start: false | ||
}) |
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.
Does createNodePromise
resolve when start: false
?
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.
yes, ready is always emitted if an error doesn't happen
https://github.com/ipfs/js-ipfs/blob/feat/better-errors-cli/src/core/boot.js#L53
} | ||
|
||
// Required inline to reduce startup time |
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.
Please leave this comment
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
const IPFS = require('../core') | ||
const node = new IPFS({ | ||
return IPFS.createNodePromise({ | ||
repo: exports.getRepoPath(), | ||
init: false, | ||
start: false, |
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.
Also here, does createNodePromise
resolve when start
is false
?
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.
yes, ready is always emitted if an error doesn't happen
https://github.com/ipfs/js-ipfs/blob/feat/better-errors-cli/src/core/boot.js#L53
fix indentation in bin.js add back inline require comment in utils.js
…eRepoInitialized forceRepoInitialized represents better to objective of the option forceInitialized could confuse we node initiliazed instead of repo
boot always runs first and puts state in stopped state, this check doesnt make sense self.state.init() does nothing fsm even prints an error, check with DEBUG=* js-ipfs init
This is marked as "blocked" but it's unclear why it's blocked. |
What's the progress here or why is it blocked? Being able to tell when a CLI command have finished running enables us to implement |
@hugomrdias I think #1834 ended capturing a lot (if not all) of what this PR was trying to address. Shall we close this PR (it's super out of date now) or do you think there are parts of this PR that would still be useful and that aren't already address or about to be addressed by near future PRs (I'm thinking async/await endeavour)? |
i still need this PR, ill close it when i finish the error codes PR |
Hi! js-ipfs master just got a whole new set of automated tests with #2528, #2440 and also running some of the test suites from our early testers (hi5 to @achingbrain for setting it all up!). Would you mind rebasing the master branch on this PR to ensure it runs all the latest tests? Thank you! |
This PR should improve error handling and help messages in the cli. All messages printed to the console follow the go implementation.
In order to do this i used the yargs
fail()
method to catch and handle errors from the commands (https://github.com/ipfs/js-ipfs/blob/feat/better-errors-cli/src/cli/bin.js#L81-L104).Previously we were using the
parse()
method which isn't made to handle errors, its for running yargs in headless mode to build cli tests.The
fail()
method handles promises returned from the commands handler, so its the perfect top-level spot to catch errors and output help specific to the cli. Like this:https://github.com/ipfs/js-ipfs/blob/feat/better-errors-cli/src/cli/bin.js#L87-L90
Another nice thing is that we can hide the stack trace behind a flag
--debug or -D
.In the PR i keep an hybrid solution only
daemon
,init
andid
commands are ported to return promises and usefail()
. Everything else works almost the same, only two things changed:The ultimate goal if everyone agrees would be to port all commands to something like the 'id' command included in the PR and start creating errors with codes like this
Basically just adding error codes just like node does, so we can do this
instead of this
In the end we could delete all this and do all the cli specific error handling inside
fail()
.Needs:
Fixes #1180