fix(core): handle run-commands targets with no commands (#31364)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

Nx hangs when here is a `run-commands` target with no commands.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Nx does not hang when there is a `run-commands` target with no commands.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #31345
This commit is contained in:
Jason Jean 2025-05-29 09:48:03 -04:00 committed by GitHub
parent 2f37cb25a0
commit d7106f5ede
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 24 additions and 3 deletions

View File

@ -22,6 +22,18 @@ describe('Run Commands', () => {
jest.clearAllMocks(); jest.clearAllMocks();
}); });
it('should handle empty commands array', async () => {
const result = await runCommands(
{
commands: [],
__unparsed__: [],
},
context
);
expect(result.success).toEqual(true);
expect(result.terminalOutput).toEqual('');
});
it('should interpolate provided --args', async () => { it('should interpolate provided --args', async () => {
const f = fileSync().name; const f = fileSync().name;
const result = await runCommands( const result = await runCommands(

View File

@ -2,6 +2,7 @@ import * as yargsParser from 'yargs-parser';
import { ExecutorContext } from '../../config/misc-interfaces'; import { ExecutorContext } from '../../config/misc-interfaces';
import { isTuiEnabled } from '../../tasks-runner/is-tui-enabled'; import { isTuiEnabled } from '../../tasks-runner/is-tui-enabled';
import { PseudoTerminal } from '../../tasks-runner/pseudo-terminal'; import { PseudoTerminal } from '../../tasks-runner/pseudo-terminal';
import { NoopChildProcess } from '../../tasks-runner/running-tasks/noop-child-process';
import { import {
ParallelRunningTasks, ParallelRunningTasks,
runSingleCommandWithPseudoTerminal, runSingleCommandWithPseudoTerminal,
@ -117,6 +118,11 @@ export async function runCommands(
); );
} }
// Handle empty commands array - return immediately with success
if (normalized.commands.length === 0) {
return new NoopChildProcess({ code: 0, terminalOutput: '' });
}
const isSingleCommand = normalized.commands.length === 1; const isSingleCommand = normalized.commands.length === 1;
const usePseudoTerminal = const usePseudoTerminal =

View File

@ -1,4 +1,3 @@
import { Serializable } from 'child_process';
import { RunningTask } from './running-task'; import { RunningTask } from './running-task';
export class NoopChildProcess implements RunningTask { export class NoopChildProcess implements RunningTask {
@ -14,7 +13,11 @@ export class NoopChildProcess implements RunningTask {
return; return;
} }
onExit(cb: (code: number) => void): void { onExit(cb: (code: number, terminalOutput: string) => void): void {
cb(this.results.code); cb(this.results.code, this.results.terminalOutput);
}
onOutput(cb: (terminalOutput: string) => void): void {
cb(this.results.terminalOutput);
} }
} }