-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Handle scripts failing with exit signals (fixes: #3954) #3995
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.
Very nice fix, thank you!
Looks like you have a failing test to fix and my comments to address, mostly about Windows :)
@@ -0,0 +1,9 @@ | |||
{ | |||
"scripts": { | |||
"compile": "gcc segfault.c -g -o segfault", |
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 this work on AppVeyor (Windows)
I'm not sure if Windows has the concept of "exiting with a signal" so it may be better to disable this test on Windows.
src/util/child.js
Outdated
`Output:\n${stdout.trim()}`, | ||
].join('\n'), | ||
); | ||
err.EXIT_SIGNAL = signal; |
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'd still add EXIT_CODE
property.
src/util/child.js
Outdated
if (signal) { | ||
err = new SpawnError( | ||
[ | ||
'Command failed.', |
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.
Looks like this message generation part is repeating itself quite a bit. May be you can DRY this up a bit?
e896b1b
to
e522f06
Compare
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.
Let's also put a reference to the node.js
issue number about this only happening on macOS so we can remove the workaround in the future?
__tests__/lifecycle-scripts.js
Outdated
test('should not show any error messages when script ends successfully', async () => { | ||
const stdout = await execCommand('test', 'script-success'); | ||
|
||
expect(stdout).toMatch(/Done in /); |
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 text may disappear in the future. May be just do expect().not.toThrow
?
__tests__/lifecycle-scripts.js
Outdated
}); | ||
|
||
test('should show correct error message when the script ends with an exit code', async () => { | ||
await execCommand('test', 'script-fail').catch(error => { |
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 test would pass if no error is thrown (if the script doesn't fail). I'd use expect().toThrow()
or even better, toThrowErrorMatchingSnapshot()
__tests__/lifecycle-scripts.js
Outdated
}); | ||
|
||
test('should show correct error message when the script ends an exit signal', async () => { | ||
await execCommand('test', 'script-segfault').catch(error => { |
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.
Same as above, use toThrow*()
src/util/execute-lifecycle-script.js
Outdated
@@ -268,7 +268,11 @@ export async function execCommand(stage: string, config: Config, cmd: string, cw | |||
return Promise.resolve(); | |||
} catch (err) { | |||
if (err instanceof SpawnError) { | |||
throw new MessageError(reporter.lang('commandFailed', err.EXIT_CODE)); | |||
if (err.EXIT_SIGNAL) { |
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.
DRY this up.
@BYK while making updates on tests, I used |
e26019c
to
9643175
Compare
__tests__/lifecycle-scripts.js
Outdated
expect(error.code).not.toBe(0); | ||
}); | ||
test('should throw error when the script ends with an exit code', async () => { | ||
await expect(execCommand('test', 'script-fail')).rejects.toBeDefined(); |
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.
@BYK this test (also the test below) should fail when the script unexpectedly succeeds. This looked a good way to message-independently test the error to me. What do you think?
@@ -142,3 +142,17 @@ test('should inherit existing environment variables when setting via yarnrc', as | |||
expect(stdout).toMatch(/^RAB$/m); | |||
expect(stdout).toMatch(/^FOO$/m); | |||
}); | |||
|
|||
test('should not show any error messages when script ends successfully', async () => { | |||
await expect(execCommand('test', 'script-success')).resolves.toBeDefined(); |
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 can simply return these, making it a one-liner:
test('should not show any error messages when script ends successfully', async () =>
expect(execCommand('test', 'script-success')).resolves.toBeDefined()
);
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.
👍
**Summary** A combination of changes have caused `yarn upgrade-interactive` to exit with a promise rejection. In short, I believe it has always been a problem, but #3995 exposed it to the prompt. The child rejection inside of [upgrade-interactive](https://github.com/yarnpkg/yarn/blob/master/src/cli/commands/upgrade-interactive.js#L152) is fine, as it was handled by the `Promise.race` condition; however, rejecting at the parent level inside of [console-reporter](https://github.com/yarnpkg/yarn/blob/master/src/reporters/console/console-reporter.js#L458) causes[ loud-rejection](https://github.com/sindresorhus/loud-rejection) to handle this. I believe @arcanis 's PR #4283 is what would allow us not to hook into `SIGINT` inside of the console reporter and allow the reporter to cleanly close itself. **Test Plan** Will work on some scenarios! This PR needs some more verification on my end ... @BYK @torifat @arcanis please jump in and provide any feedback you think could be helpful! Opened early for visibility :)
**Summary** A combination of changes have caused `yarn upgrade-interactive` to exit with a promise rejection. In short, I believe it has always been a problem, but yarnpkg#3995 exposed it to the prompt. The child rejection inside of [upgrade-interactive](https://github.com/yarnpkg/yarn/blob/master/src/cli/commands/upgrade-interactive.js#L152) is fine, as it was handled by the `Promise.race` condition; however, rejecting at the parent level inside of [console-reporter](https://github.com/yarnpkg/yarn/blob/master/src/reporters/console/console-reporter.js#L458) causes[ loud-rejection](https://github.com/sindresorhus/loud-rejection) to handle this. I believe @arcanis 's PR yarnpkg#4283 is what would allow us not to hook into `SIGINT` inside of the console reporter and allow the reporter to cleanly close itself. **Test Plan** Will work on some scenarios! This PR needs some more verification on my end ... @BYK @torifat @arcanis please jump in and provide any feedback you think could be helpful! Opened early for visibility :)
Summary
Fixes: #3954, Adds error handling for scripts that are failing with a signal such as
SIGBUS
,SIGSEGV
, etc.In current behavior, yarn checks the exit code of a script to show errors and fail the process. But certain errors make node child-processes to exit with a signal instead. (e.g. the case in #3954)
This fix adds support for handling exit signals from child processes and shows more informative error messages.
Note: This implementation handles exit signals priorly, assuming exit signals are pointing to more critical low-level issues.
Test plan
Added 3 test cases (one for zero exit code, one for non-zero exit code, one for exit signal)