Skip to content
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

Merged
merged 3 commits into from
Aug 2, 2017

Conversation

onurtemizkan
Copy link
Contributor

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)

Copy link
Member

@BYK BYK left a 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",
Copy link
Member

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.

`Output:\n${stdout.trim()}`,
].join('\n'),
);
err.EXIT_SIGNAL = signal;
Copy link
Member

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.

if (signal) {
err = new SpawnError(
[
'Command failed.',
Copy link
Member

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?

@BYK BYK self-assigned this Jul 23, 2017
@onurtemizkan onurtemizkan force-pushed the handle-segmentation-faults branch 2 times, most recently from e896b1b to e522f06 Compare July 23, 2017 20:44
Copy link
Member

@BYK BYK left a 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?

test('should not show any error messages when script ends successfully', async () => {
const stdout = await execCommand('test', 'script-success');

expect(stdout).toMatch(/Done in /);
Copy link
Member

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?

});

test('should show correct error message when the script ends with an exit code', async () => {
await execCommand('test', 'script-fail').catch(error => {
Copy link
Member

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()

});

test('should show correct error message when the script ends an exit signal', async () => {
await execCommand('test', 'script-segfault').catch(error => {
Copy link
Member

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*()

@@ -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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY this up.

@onurtemizkan
Copy link
Contributor Author

onurtemizkan commented Jul 26, 2017

@BYK while making updates on tests, I used resolves and rejects helpers, which are added at Jest v20.0.0. We are currently using Jest 20.0.4 but our flow-type definitions for Jest do not include those two properties. So the flow checks will fail on CI. Should I raise another issue for updating all the Jest flow types or is it enough to add only those two type defs with this PR?

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();
Copy link
Contributor Author

@onurtemizkan onurtemizkan Aug 1, 2017

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();
Copy link
Member

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()
);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@BYK BYK merged commit 8ead83f into yarnpkg:master Aug 2, 2017
BYK pushed a commit that referenced this pull request Sep 13, 2017
**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 :)
joaolucasl pushed a commit to joaolucasl/yarn that referenced this pull request Oct 27, 2017
**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 :)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scripts that cause a segmentation fault exit with status 0
2 participants