Skip to content

Commit

Permalink
Add --kill-signal option (#402)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulsmithkc authored Mar 28, 2023
1 parent 91c2448 commit a469388
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 6 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ Killing other processes
-k, --kill-others Kill other processes if one exits or dies.[boolean]
--kill-others-on-fail Kill other processes if one exits with non zero
status code. [boolean]
--kill-signal Signal to send to other processes if one exits or dies.
(SIGTERM/SIGKILL, defaults to SIGTERM) [string]
Restarting
--restart-tries How many times a process that died should restart.
Expand Down
10 changes: 9 additions & 1 deletion bin/concurrently.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ const args = yargs(argsBeforeSep)
describe: 'Kill other processes if one exits with non zero status code.',
type: 'boolean',
},
'kill-signal': {
alias: 'ks',
describe:
'Signal to send to other processes if one exits or dies. (SIGTERM/SIGKILL, defaults to SIGTERM)',
type: 'string',
default: defaults.killSignal,
},

// Prefix
prefix: {
Expand Down Expand Up @@ -185,7 +192,7 @@ const args = yargs(argsBeforeSep)
)
.group(['p', 'c', 'l', 't'], 'Prefix styling')
.group(['i', 'default-input-target'], 'Input handling')
.group(['k', 'kill-others-on-fail'], 'Killing other processes')
.group(['k', 'kill-others-on-fail', 'kill-signal'], 'Killing other processes')
.group(['restart-tries', 'restart-after'], 'Restarting')
.epilogue(epilogue)
.parseSync();
Expand All @@ -208,6 +215,7 @@ concurrently(
: args.killOthersOnFail
? ['failure']
: [],
killSignal: args.killSignal,
maxProcesses: args.maxProcesses,
raw: args.raw,
hide: args.hide.split(','),
Expand Down
5 changes: 5 additions & 0 deletions src/concurrently.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ export type ConcurrentlyOptions = {
*/
kill: KillProcess;

/**
* Signal to send to killed processes.
*/
killSignal?: string;

/**
* List of additional arguments passed that will get replaced in each command.
* If not defined, no argument replacing will happen.
Expand Down
7 changes: 7 additions & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,10 @@ export const timings = false;
* Passthrough additional arguments to commands (accessible via placeholders) instead of treating them as commands.
*/
export const passthroughArguments = false;

/**
* Signal to send to other processes if one exits or dies.
*
* Defaults to OS specific signal. (SIGTERM on Linux/MacOS)
*/
export const killSignal: string | undefined = undefined;
29 changes: 26 additions & 3 deletions src/flow-control/kill-others.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ beforeEach(() => {
logger = createMockInstance(Logger);
});

const createWithConditions = (conditions: ProcessCloseCondition[]) =>
const createWithConditions = (conditions: ProcessCloseCondition[], killSignal?: string) =>
new KillOthers({
logger,
conditions,
killSignal,
});

it('returns same commands', () => {
Expand All @@ -48,7 +49,18 @@ it('kills other killable processes on success', () => {
expect(logger.logGlobalEvent).toHaveBeenCalledTimes(1);
expect(logger.logGlobalEvent).toHaveBeenCalledWith('Sending SIGTERM to other processes..');
expect(commands[0].kill).not.toHaveBeenCalled();
expect(commands[1].kill).toHaveBeenCalled();
expect(commands[1].kill).toHaveBeenCalledWith(undefined);
});

it('kills other killable processes on success, with specified signal', () => {
createWithConditions(['success'], 'SIGKILL').handle(commands);
commands[1].isKillable = true;
commands[0].close.next(createFakeCloseEvent({ exitCode: 0 }));

expect(logger.logGlobalEvent).toHaveBeenCalledTimes(1);
expect(logger.logGlobalEvent).toHaveBeenCalledWith('Sending SIGKILL to other processes..');
expect(commands[0].kill).not.toHaveBeenCalled();
expect(commands[1].kill).toHaveBeenCalledWith('SIGKILL');
});

it('does nothing if called without conditions', () => {
Expand All @@ -69,7 +81,18 @@ it('kills other killable processes on failure', () => {
expect(logger.logGlobalEvent).toHaveBeenCalledTimes(1);
expect(logger.logGlobalEvent).toHaveBeenCalledWith('Sending SIGTERM to other processes..');
expect(commands[0].kill).not.toHaveBeenCalled();
expect(commands[1].kill).toHaveBeenCalled();
expect(commands[1].kill).toHaveBeenCalledWith(undefined);
});

it('kills other killable processes on failure, with specified signal', () => {
createWithConditions(['failure'], 'SIGKILL').handle(commands);
commands[1].isKillable = true;
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));

expect(logger.logGlobalEvent).toHaveBeenCalledTimes(1);
expect(logger.logGlobalEvent).toHaveBeenCalledWith('Sending SIGKILL to other processes..');
expect(commands[0].kill).not.toHaveBeenCalled();
expect(commands[1].kill).toHaveBeenCalledWith('SIGKILL');
});

it('does not try to kill processes already dead', () => {
Expand Down
10 changes: 8 additions & 2 deletions src/flow-control/kill-others.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@ export type ProcessCloseCondition = 'failure' | 'success';
export class KillOthers implements FlowController {
private readonly logger: Logger;
private readonly conditions: ProcessCloseCondition[];
private readonly killSignal: string | undefined;

constructor({
logger,
conditions,
killSignal,
}: {
logger: Logger;
conditions: ProcessCloseCondition | ProcessCloseCondition[];
killSignal: string | undefined;
}) {
this.logger = logger;
this.conditions = _.castArray(conditions);
this.killSignal = killSignal;
}

handle(commands: Command[]) {
Expand All @@ -47,8 +51,10 @@ export class KillOthers implements FlowController {
closeState.subscribe(() => {
const killableCommands = commands.filter((command) => Command.canKill(command));
if (killableCommands.length) {
this.logger.logGlobalEvent('Sending SIGTERM to other processes..');
killableCommands.forEach((command) => command.kill());
this.logger.logGlobalEvent(
`Sending ${this.killSignal || 'SIGTERM'} to other processes..`
);
killableCommands.forEach((command) => command.kill(this.killSignal));
}
})
);
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export default (
new KillOthers({
logger,
conditions: options.killOthers || [],
killSignal: options.killSignal,
}),
new LogTimings({
logger: options.timings ? logger : undefined,
Expand Down

0 comments on commit a469388

Please sign in to comment.