fix(core): Improve errors in situations where the command spawn does … (#5723)

This commit is contained in:
Richie Foreman 2025-08-06 19:31:42 -04:00 committed by GitHub
parent 626844b539
commit 4782113ceb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 43 additions and 12 deletions

View File

@ -185,6 +185,16 @@ describe('ShellExecutionService', () => {
expect(result.error).toBe(spawnError);
expect(result.exitCode).toBe(1);
});
it('handles errors that do not fire the exit event', async () => {
const error = new Error('spawn abc ENOENT');
const { result } = await simulateExecution('touch cat.jpg', (cp) => {
cp.emit('error', error); // No exit event is fired.
});
expect(result.error).toBe(error);
expect(result.exitCode).toBe(1);
});
});
describe('Aborting Commands', () => {

View File

@ -174,7 +174,19 @@ export class ShellExecutionService {
child.stdout.on('data', (data) => handleOutput(data, 'stdout'));
child.stderr.on('data', (data) => handleOutput(data, 'stderr'));
child.on('error', (err) => {
const { stdout, stderr, finalBuffer } = cleanup();
error = err;
resolve({
error,
stdout,
stderr,
rawOutput: finalBuffer,
output: stdout + (stderr ? `\n${stderr}` : ''),
exitCode: 1,
signal: null,
aborted: false,
pid: child.pid,
});
});
const abortHandler = async () => {
@ -200,18 +212,8 @@ export class ShellExecutionService {
abortSignal.addEventListener('abort', abortHandler, { once: true });
child.on('exit', (code, signal) => {
exited = true;
abortSignal.removeEventListener('abort', abortHandler);
if (stdoutDecoder) {
stdout += stripAnsi(stdoutDecoder.decode());
}
if (stderrDecoder) {
stderr += stripAnsi(stderrDecoder.decode());
}
const finalBuffer = Buffer.concat(outputChunks);
child.on('exit', (code: number, signal: NodeJS.Signals) => {
const { stdout, stderr, finalBuffer } = cleanup();
resolve({
rawOutput: finalBuffer,
@ -225,6 +227,25 @@ export class ShellExecutionService {
pid: child.pid,
});
});
/**
* Cleans up a process (and it's accompanying state) that is exiting or
* erroring and returns output formatted output buffers and strings
*/
function cleanup() {
exited = true;
abortSignal.removeEventListener('abort', abortHandler);
if (stdoutDecoder) {
stdout += stripAnsi(stdoutDecoder.decode());
}
if (stderrDecoder) {
stderr += stripAnsi(stderrDecoder.decode());
}
const finalBuffer = Buffer.concat(outputChunks);
return { stdout, stderr, finalBuffer };
}
});
return { pid: child.pid, result };