Skip to content

Commit

Permalink
fix(core): fix cursor being hidden and process shutdown for ctrl c
Browse files Browse the repository at this point in the history
  • Loading branch information
FrozenPandaz committed Apr 19, 2024
1 parent 82be2ae commit efa7a53
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 24 deletions.
88 changes: 64 additions & 24 deletions packages/nx/src/executors/run-commands/run-commands.impl.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { exec } from 'child_process';
import { ChildProcess, exec, Serializable } from 'child_process';
import * as path from 'path';
import * as yargsParser from 'yargs-parser';
import { env as appendLocalEnv } from 'npm-run-path';
Expand All @@ -7,22 +7,13 @@ import * as chalk from 'chalk';
import {
getPseudoTerminal,
PseudoTerminal,
PseudoTtyProcess,
} from '../../tasks-runner/pseudo-terminal';
import { signalToCode } from '../../utils/exit-codes';

export const LARGE_BUFFER = 1024 * 1000000;

const exitListeners: Set<() => void> = new Set();

function processExitListener() {
for (const listener of exitListeners) {
listener();
}
}

process.on('exit', processExitListener);
process.on('SIGTERM', processExitListener);
process.on('SIGINT', processExitListener);
process.on('SIGQUIT', processExitListener);
let pseudoTerminal: PseudoTerminal | null;
const childProcesses = new Set<ChildProcess | PseudoTtyProcess>();

async function loadEnvVars(path?: string) {
if (path) {
Expand Down Expand Up @@ -262,9 +253,7 @@ async function runSerially(
options: NormalizedRunCommandsOptions,
context: ExecutorContext
): Promise<{ success: boolean; terminalOutput: string }> {
const pseudoTerminal = PseudoTerminal.isSupported()
? getPseudoTerminal()
: null;
pseudoTerminal ??= PseudoTerminal.isSupported() ? getPseudoTerminal() : null;
let terminalOutput = '';
for (const c of options.commands) {
const result: { success: boolean; terminalOutput: string } =
Expand Down Expand Up @@ -329,6 +318,8 @@ async function createProcess(
quiet: !streamOutput,
});

childProcesses.add(cp);

return new Promise((res) => {
cp.onOutput((output) => {
terminalOutput += output;
Expand Down Expand Up @@ -372,13 +363,8 @@ function nodeProcess(
env,
cwd,
});
/**
* Ensure the child process is killed when the parent exits
*/
const childProcessKiller = (signal?: number | NodeJS.Signals) =>
childProcess.kill(signal);

exitListeners.add(childProcessKiller);
childProcesses.add(childProcess);

childProcess.stdout.on('data', (data) => {
const output = addColorAndPrefix(data, commandConfig);
Expand Down Expand Up @@ -409,7 +395,7 @@ function nodeProcess(
res({ success: false, terminalOutput });
});
childProcess.on('exit', (code) => {
exitListeners.delete(childProcessKiller);
childProcesses.delete(childProcess);
if (!readyWhen) {
res({ success: code === 0, terminalOutput });
}
Expand Down Expand Up @@ -571,3 +557,57 @@ function filterPropKeysFromUnParsedOptions(
}
return parsedOptions;
}

function registerProcessListener() {
// When the nx process gets a message, it will be sent into the task's process
process.on('message', (message: Serializable) => {
// this.publisher.publish(message.toString());
if (pseudoTerminal) {
pseudoTerminal.sendMessageToChildren(message);
}

childProcesses.forEach((p) => {
if ('connected' in p && p.connected) {
p.send(message);
}
});
});

// Terminate any task processes on exit
process.on('exit', () => {
childProcesses.forEach((p) => {
if ('connected' in p ? p.connected : p.isAlive) {
p.kill();
}
});
});
process.on('SIGINT', () => {
childProcesses.forEach((p) => {
if ('connected' in p ? p.connected : p.isAlive) {
p.kill('SIGTERM');
}
});
// we exit here because we don't need to write anything to cache.
process.exit(signalToCode('SIGINT'));
});
process.on('SIGTERM', () => {
childProcesses.forEach((p) => {
if ('connected' in p ? p.connected : p.isAlive) {
p.kill('SIGTERM');
}
});
// no exit here because we expect child processes to terminate which
// will store results to the cache and will terminate this process
});
process.on('SIGHUP', () => {
childProcesses.forEach((p) => {
if ('connected' in p ? p.connected : p.isAlive) {
p.kill('SIGTERM');
}
});
// no exit here because we expect child processes to terminate which
// will store results to the cache and will terminate this process
});
}

registerProcessListener();
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export async function createRunManyDynamicOutputRenderer({
overrides: Record<string, unknown>;
}): Promise<{ lifeCycle: LifeCycle; renderIsDone: Promise<void> }> {
cliCursor.hide();
// Show the cursor again after the process exits
process.on('exit', () => {
cliCursor.show();
});
let resolveRenderIsDonePromise: (value: void) => void;
const renderIsDone = new Promise<void>(
(resolve) => (resolveRenderIsDonePromise = resolve)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export async function createRunOneDynamicOutputRenderer({
overrides: Record<string, unknown>;
}): Promise<{ lifeCycle: LifeCycle; renderIsDone: Promise<void> }> {
cliCursor.hide();
// Show the cursor again after the process exits
process.on('exit', () => {
cliCursor.show();
});
let resolveRenderIsDonePromise: (value: void) => void;
const renderIsDone = new Promise<void>(
(resolve) => (resolveRenderIsDonePromise = resolve)
Expand Down

0 comments on commit efa7a53

Please sign in to comment.