Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ process.domain = null;
exitCode = code;
},
enumerable: true,
configurable: false,
configurable: true,
});
}
process._exiting = false;
Expand Down
14 changes: 9 additions & 5 deletions test/parallel/test-process-exit-code-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,14 @@ if (process.argv[2] === undefined) {

// Check process.exitCode
for (const arg of invalids) {
debug(`invaild code: ${inspect(arg.code)}`);
debug(`invalid code: ${inspect(arg.code)}`);
throws(() => (process.exitCode = arg.code), new RegExp(arg.pattern));
}
for (const arg of valids) {
debug(`vaild code: ${inspect(arg.code)}`);
debug(`valid code: ${inspect(arg.code)}`);
process.exitCode = arg.code;
}

throws(() => {
delete process.exitCode;
}, /Cannot delete property 'exitCode' of #<process>/);
process.exitCode = 0;

// Check process.exit([code])
Expand All @@ -141,3 +138,10 @@ if (process.argv[2] === undefined) {
process.exit(args[index].code);
}
}

const origExitCode = Object.getOwnPropertyDescriptor(process, 'exitCode');
try {
delete process.exitCode;
Copy link
Member

Choose a reason for hiding this comment

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

I think it needs to be handled so that even when users delete process.exitCode, the exit code set by the core code will still be applied.

process.exitCode = kGenericUserError; 

process.exitCode is also used by the core. This is concerning because if users delete it, the intended behavior in the core, like the code mentioned above (#49579 (comment)), won't be applied.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure how that would be possible. If it's deleted, there's nothing there.

Copy link
Member Author

@ljharb ljharb Jul 16, 2024

Choose a reason for hiding this comment

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

Do note that process.exitCode = kGenericUserError; will still work after it's been deleted, it just won't have the magic getter/setter anymore.

Copy link
Member

Choose a reason for hiding this comment

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

If process.exitCode = kGenericUserError; worked, the error code after terminating the node should be kGenericUserError, not 0. Last time I checked, it was 0... right?

Copy link
Member

Choose a reason for hiding this comment

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

I mean it should not be 0 when calling the exit status. echo $?

Copy link
Member Author

Choose a reason for hiding this comment

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

it can be restricted to any domain you like :-) just integer numbers then?

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I can give you the answer you want, as I can't guess how you'd implement it based on your current description. Currently, the types allowed are:

* {integer|string|null|undefined} The exit code. For string type, only
  integer strings (e.g.,'1') are allowed. **Default:** `undefined`.

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, so I’ll move forward with that schema, thanks.

Copy link
Member Author

Choose a reason for hiding this comment

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

hmm - seems like the exitCode is read in C++, so i'm not sure how it can read the JS-set value once it's overridden.

@daeyeon are you comfortable with this proceeding with the understanding that if a user mocks process.exitCode, it's up to them to restore it? (just like other globals)

If not, what about #49579 (comment) ?

Copy link
Member

Choose a reason for hiding this comment

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

@daeyeon are you comfortable with this proceeding with the understanding that if a user mocks process.exitCode, it's up to them to restore it? (just like other globals)

The core's behavior related to exitCode will be breaking during the time gap between mocking and restoring. It's probably not enough to rely on the restoring after finishing the mocking.

If not, what about #49579 (comment) ?

Looks like a good idea. In addition to moving the getters/setters to the process prototype, I think that the core code should use the exitCode of the process prototype, rather than the process itself. This would address concerns about breaking and can provide guidance on how users can monitor it.

Copy link
Member

Choose a reason for hiding this comment

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

I think we should just test that redefining process.exitCode with accessors that call out to the original at the end still works as expected. That means users can still mock it if they call out to the original accessors. If they don't, or if they delete it, then it is just not going to get picked up as the actual exit code. And we can just put a note in the doc warning that delete process.exitCode is not going to work, like what @isaacs suggested in #49579 (comment)

} finally {
Object.defineProperty(process, 'exitCode', origExitCode);
}