-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Fix: avoid using process.exit()
when possible
#3955
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
src/cli/index.js
Outdated
@@ -142,7 +142,7 @@ if (outputWrapper) { | |||
if (command.noArguments && commander.args.length) { | |||
reporter.error(reporter.lang('noArguments')); | |||
reporter.info(command.getDocsInfo); | |||
process.exit(1); | |||
process.exitCode = 1; |
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 need to shortcut the rest of the code to have the same behavior - process.exit
doesn't return, but setting process.exitCode
will continue executing the rest of the code.
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.
Not shortcutting the whole point with this change. We should not need the shortcut behavior and turns out we really don't since all tests are passing. It was only necessary at one point so I left it that way.
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 don't think that's a good idea.
-
it kinda violates the principle of the least surprise, since we would no longer have the guarantee that any part of this file would only be executed if no other command matched. It's only a matter of time before we make some mistake that will execute two commands in some edge case.
-
Case in point, your current code has a bug that will make
yarn -v
print the version number before runningyarn install -v
(andyarn -v -h
will print the version number then callyarn help -v
). -
Even without that, not shortcutting also means that we will be running useless tasks (such as instanciating a reporter etc) even when not needed. That's an issue because those commands must be fast (some tools such as Nuclide might need to call them often, and if their results are late it will slow down the all process).
I'm not against using exitCode
, you have a point that shutting down the program before the I/O has finished is bad, but I strongly think we then need to refactor the CLI code to make sure we never have to check that our code won't get called twice - might only be a matter of adding else
clauses 😃
src/util/signal-handler.js
Outdated
@@ -3,7 +3,7 @@ import {forwardSignalToSpawnedProcesses} from './child.js'; | |||
|
|||
function forwardSignalAndExit(signal: string) { | |||
forwardSignalToSpawnedProcesses(signal); | |||
process.exit(1); | |||
throw new Error('Received SIGTERM, terminating.'); |
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.
Will that print a stacktrace on the console? That won't be helpful for the user.
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 thought it would be. Why do you think it wouldn't?
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.
SIGTERM
is the default signal sent by kill
. It doesn't make much sense to print a stacktrace (which heavily imply that something went wrong in the program implementation) if the termination came from the outside, don't you think?
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.
Alright, makes sense. I'll find an alternative way for this one.
src/cli/index.js
Outdated
@@ -142,7 +142,7 @@ if (outputWrapper) { | |||
if (command.noArguments && commander.args.length) { | |||
reporter.error(reporter.lang('noArguments')); | |||
reporter.info(command.getDocsInfo); | |||
process.exit(1); | |||
process.exitCode = 1; |
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 don't think that's a good idea.
-
it kinda violates the principle of the least surprise, since we would no longer have the guarantee that any part of this file would only be executed if no other command matched. It's only a matter of time before we make some mistake that will execute two commands in some edge case.
-
Case in point, your current code has a bug that will make
yarn -v
print the version number before runningyarn install -v
(andyarn -v -h
will print the version number then callyarn help -v
). -
Even without that, not shortcutting also means that we will be running useless tasks (such as instanciating a reporter etc) even when not needed. That's an issue because those commands must be fast (some tools such as Nuclide might need to call them often, and if their results are late it will slow down the all process).
I'm not against using exitCode
, you have a point that shutting down the program before the I/O has finished is bad, but I strongly think we then need to refactor the CLI code to make sure we never have to check that our code won't get called twice - might only be a matter of adding else
clauses 😃
src/cli/index.js
Outdated
loudRejection(); | ||
handleSignals(); | ||
|
||
const startArgs = process.argv.slice(0, 2); |
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.
What do you think about modularizing and putting this in the main
parameters? I can see this function as an interesting entry point to run commands in our testcases - we currently need to instanciate each class and pass them the right properties, which might be tedious and error-prone, so it could be nice to just be able to use run([ 'install', '--check-files' ])
instead and use the actual CLI pipeline.
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.
Actually my comment was more about Line 31, but it's also applicable here.
@@ -196,7 +196,7 @@ export default class ConsoleReporter extends BaseReporter { | |||
(err, answer) => { | |||
if (err) { | |||
if (err.message === 'canceled') { | |||
process.exit(1); |
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.
Shouldn't we exit immediately here? Or reject?
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.
Yeah, I think you're right. I thought about the same thing and don't know why I didn't merge the reject
part. Update coming.
@@ -35,6 +35,8 @@ const compiler = webpack({ | |||
output: { | |||
filename: `yarn-${version}.js`, | |||
path: path.join(basedir, 'artifacts'), | |||
library: 'yarn-cli', | |||
libraryTarget: 'commonjs2', |
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.
Is this change required? What breaks without it?
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, this is the solution that fixes the single-file bundles. Without this, Webpack exports an empty object, causing yarn-bundled-entry-point.js
to fail since main
function can't be imported.
**Summary** Upgrade ESLint to version 4. I did this mostly to get [no-process-exit](http://eslint.org/docs/rules/no-process-exit) rule introduced in v4. Refs #3955. **Test plan** Lint should pass without errors.
**Summary** A follow up to #3955 to disable further usage of `process.exit()`. **Test plan** `yarn lint` should not produce any errors.
**Summary** A follow up to #3955 to disable further usage of `process.exit()`. **Test plan** `yarn lint` should not produce any errors.
I noticed when pulling down master and running I traced it back to this PR. You can see in CircleCI the output of the last working build here: https://circleci.com/gh/yarnpkg/yarn/4533 Notice how When this commit runs in CI, however (https://circleci.com/gh/yarnpkg/yarn/4536), there is no version number output, and I confirm that the webpack build does not work at all. |
Hmm, interesting, I wonder why the nightly end-to-end tests didn't fail. They use the nightly Debian packages, which contain the Webpack build inside them. https://build.dan.cx/job/yarn-e2e/label=docker,os=ubuntu-16.04/269/console |
I'm not sure why those don't fail (without looking into it more) -- but the reason CircleCI doesn't fail is because |
Right. Maybe the build should assert that it actually returns the correct version number. |
👍 |
I'll work on this soon. |
Filed #4057, actively working on it. |
Summary
Refs #3524. We are using
process.exit()
pretty liberally although it is strongly advised not to use it since it may cause thestdout
to get corrupted/terminated before a full flush. This patch changes all possibleprocess.exit(code)
calls withprocess.exitCode = code
statements.We'd also ideally enable no-process-exit rule in ESLint but it requires an upgrade to ESLint v4 which should be handled separately.
Test plan
Expect tests to pass and actually finish (not run indefinitely due to
yarn
not exiting at all). Also, the script referenced in #3524 to output something like the following: