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

Add result.signalName #3

Merged
merged 1 commit into from
Aug 22, 2024
Merged

Add result.signalName #3

merged 1 commit into from
Aug 22, 2024

Conversation

ehmicky
Copy link
Collaborator

@ehmicky ehmicky commented Aug 20, 2024

This adds result.signalName.

index.js Outdated

// TODO: Pass in `stdin` and `stdout` strings here.
const waitForExit = async subprocess => {
const [exitCode, signalName] = await once(subprocess, 'close');
Copy link
Collaborator Author

@ehmicky ehmicky Aug 20, 2024

Choose a reason for hiding this comment

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

Note: once() throws on error event (which is what we want).

index.js Outdated
// TODO: Pass in `stdin` and `stdout` strings here.
const waitForExit = async subprocess => {
const [exitCode, signalName] = await once(subprocess, 'close');
return {exitCode, signalName};
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I double checked the core Node.js code, and just listening for the close and error events catches every possible exit situation. 👍

Also, unlike the exit event, the close event waits for IPC and file descriptors to close.

test.js Outdated
@@ -6,6 +6,7 @@ test('can be awaited', async t => {
// TODO
// t.is(result.stdout, '🦄');
t.is(result.exitCode, 0);
t.is(result.signalName, null);
Copy link
Owner

Choose a reason for hiding this comment

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

Can we make this undefined instead? exitCode too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed. 👍

@ehmicky ehmicky force-pushed the signal-name branch 4 times, most recently from 18d031e to b77520f Compare August 21, 2024 22:19
@ehmicky
Copy link
Collaborator Author

ehmicky commented Aug 21, 2024

Done.

@ehmicky ehmicky requested a review from sindresorhus August 21, 2024 22:19
const [exitCode] = await once(subprocess, 'close');
return {...getOutput(result), exitCode};
await once(subprocess, 'close');
return getOutput(subprocess, result);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We should throw on non-0 exit code or on signal termination. I will do this in a follow-up PR. (see #8)

test('result.exitCode|signalName on "error" event after spawn', async t => {
const {exitCode, signalName} = await t.throwsAsync(nanoSpawn('node', {signal: AbortSignal.abort()}));
t.is(exitCode, undefined);
t.is(signalName, 'SIGTERM');
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Those above are the 6 main ways a Node.js subprocess exits.

const getOutput = ({exitCode, signalCode}, {stdout, stderr}) => ({
// `exitCode` can be a negative number (`errno`) when the `error` event is emitted on the subprocess
...(exitCode === null || exitCode < 0 ? {} : {exitCode}),
...(signalCode === null ? {} : {signalName: signalCode}),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We use subprocess.exitCode and subprocess.signalCode instead of the values passed to the close event emitter. That's because the close event is not emitted when an error event was emitted before spawn.

But it turns out those are the exact same values.
See source: https://github.com/nodejs/node/blob/8b0c699f2aafdf81bc4834b9bd2a4923741d3a6f/lib/internal/child_process.js#L1104

// Then, `subprocess.pid` is `undefined` and `close` is never emitted.
// - After `spawn`, e.g. for the `signal` option.
// Then, `subprocess.pid` is set and `close` is always emitted.
if (subprocess.pid !== undefined) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

// - After `spawn`, e.g. for the `signal` option.
// Then, `subprocess.pid` is set and `close` is always emitted.
if (subprocess.pid !== undefined) {
await Promise.allSettled([once(subprocess, 'close')]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Promise.allSettled() is here because the error event can technically be emitted twice on the subprocess.

// - After `spawn`, e.g. for the `signal` option.
// Then, `subprocess.pid` is set and `close` is always emitted.
if (subprocess.pid !== undefined) {
await Promise.allSettled([once(subprocess, 'close')]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We wait for close so we can get the termination signalName, if any.

@sindresorhus sindresorhus merged commit f720393 into main Aug 22, 2024
6 checks passed
@sindresorhus sindresorhus deleted the signal-name branch August 22, 2024 04:37
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.

2 participants