-
Notifications
You must be signed in to change notification settings - Fork 739
Add boolean fatal
option to exec()
function
#961
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Once I receive feedback on the implementation itself, I can add documentation and tests! |
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.
Left some comments, but mostly good (and this seems like a good feature to add to the project)
src/exec.js
Outdated
@@ -28,6 +28,7 @@ function execSync(cmd, opts, pipe) { | |||
|
|||
opts = common.extend({ | |||
silent: common.config.silent, | |||
fatal: common.config.fatal, |
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 also redundant (see comment below)
src/common.js
Outdated
@@ -118,7 +118,7 @@ function error(msg, _code, options) { | |||
state.error += logEntry; | |||
|
|||
// Throw an error, or log the entry | |||
if (config.fatal) throw new Error(logEntry); | |||
if (config.fatal && options.fatal !== false) throw new Error(logEntry); |
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.
Instead of this, how about we assign fatal: config.fatal
up above in DEFAULT_OPTIONS
?
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 it; not sure how I missed that pattern originally.
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.
You should update this to check options.fatal
instead of config.fatal
(otherwise, this should be OK)
Here's a similar test you might be able to base this on: Lines 42 to 48 in 57df38c
|
Codecov Report
@@ Coverage Diff @@
## master #961 +/- ##
==========================================
- Coverage 97.28% 97.14% -0.14%
==========================================
Files 34 34
Lines 1289 1298 +9
==========================================
+ Hits 1254 1261 +7
- Misses 35 37 +2
Continue to review full report at Codecov.
|
There is a very strange error where one of the new tests I added causes I drilled it down to that line by adding a I have no idea why that is. |
Not sure what the failure on 119 is (I don't see it on the bots), but https://travis-ci.org/shelljs/shelljs/jobs/572075940 failed here:
This makes sense, because the implementation has a bug (per my previous comment, you need to check |
The code does check |
When running How do you propose this should be addressed? There does not appear to be an easy way to wire the new |
I've implemented what I think is a decent solution, working within this project's existing design. I've introduced a new Let me know your thoughts, @nfischer. |
@@ -88,7 +88,7 @@ CommandError.prototype = Object.create(Error.prototype); | |||
CommandError.prototype.constructor = CommandError; | |||
exports.CommandError = CommandError; // visible for testing | |||
|
|||
// Shows error message. Throws if config.fatal is true | |||
// Shows error message. Throws if fatal is true (defaults to config.fatal, overridable with options.fatal) |
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.
nit: "Throws if options.fatal is..."
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 verbiage here is intentionally just "fatal" because it's looking at two separate values. First it looks at the global config, but it also can be overridable with the function's options. The options.fatal
value isn't always provided, in which case, it's really the global config that determines the behavior.
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 suppose you could rewrite it to read: // Throws if options.fatal is true, or if options.fatal is undefined and config.fatal is true
. Let me know if you think that wording is more clear.
src/exec.js
Outdated
}); | ||
|
||
// We use this function to run `exec` synchronously while also providing realtime | ||
// output. | ||
function execSync(cmd, opts, pipe) { | ||
if (!common.config.execPath) { | ||
common.error('Unable to find a path to the node binary. Please manually set config.execPath'); | ||
common.error('Unable to find a path to the node binary. Please manually set config.execPath', { continue: true, fatal: opts.fatal }); |
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.
Why continue: true
?
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.
Without it, even if fatal: false
is provided to the common error()
function, it will still throw a CommandError
if continue
is not true
. See: https://github.com/WesCossick/shelljs/blob/master/src/common.js#L125
Normally, the wrap()
function would have caught that error, seen that fatal was disabled globally, and not re-thrown it. But now, the wrap()
function will always throw errors since handlesFatalDynamically: true
for the exec
command. That means it's up to the exec
command to decide whether to throw it or not. Even if it doesn't throw an error, at a minimum it should not execute any logic past that point.
To make this more clear, I've re-written this bit of code (here and the other place where continue: true
was followed by a return
.
src/exec.js
Outdated
async: false, | ||
}, options); | ||
|
||
if (!command) { | ||
common.error('must specify command', { continue: true, fatal: options.fatal }); | ||
return; |
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 doesn't look right for continue: true
to be followed by return
...
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.
See above comment.
Hey @nfischer, let me know what you think of the recent changes. |
@nfischer Any update on this? |
This PR introduces a new boolean
fatal
option for theexec()
function. Like the existingsilent
option, this new option allows you to override the globalfatal
configuration parameter on a per-command basis, like:This eliminates the need for the following pattern:
The old pattern can introduce race conditions in
async
functions, an issue that was surfaced in our code with the new ESLint rule, require-atomic-updates.