From 6eb387d447816407bd6aa9149fb14461bb548e99 Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Fri, 16 Feb 2024 00:21:51 -0500 Subject: [PATCH] feat(core): run commands directly --- docs/generated/devkit/Task.md | 18 +++ e2e/nx-run/src/run.test.ts | 2 +- e2e/webpack/src/webpack.test.ts | 1 - packages/nx/src/config/task-graph.ts | 8 ++ .../run-commands/run-commands.impl.ts | 123 +++++++++++------- .../tasks-runner/create-task-graph.spec.ts | 42 ++++++ .../nx/src/tasks-runner/create-task-graph.ts | 6 +- .../src/tasks-runner/default-tasks-runner.ts | 4 +- .../forked-process-task-runner.ts | 4 +- .../nx/src/tasks-runner/task-orchestrator.ts | 63 +++++++-- .../nx/src/utils/command-line-utils.spec.ts | 8 ++ packages/nx/src/utils/command-line-utils.ts | 2 + 12 files changed, 213 insertions(+), 68 deletions(-) diff --git a/docs/generated/devkit/Task.md b/docs/generated/devkit/Task.md index 686bac2a5e6f20..bc638023fc5a91 100644 --- a/docs/generated/devkit/Task.md +++ b/docs/generated/devkit/Task.md @@ -8,9 +8,11 @@ A representation of the invocation of an Executor - [cache](../../devkit/documents/Task#cache): boolean - [endTime](../../devkit/documents/Task#endtime): number +- [executor](../../devkit/documents/Task#executor): string - [hash](../../devkit/documents/Task#hash): string - [hashDetails](../../devkit/documents/Task#hashdetails): Object - [id](../../devkit/documents/Task#id): string +- [options](../../devkit/documents/Task#options): Record - [outputs](../../devkit/documents/Task#outputs): string[] - [overrides](../../devkit/documents/Task#overrides): any - [projectRoot](../../devkit/documents/Task#projectroot): string @@ -35,6 +37,14 @@ Unix timestamp of when a Batch Task ends --- +### executor + +• `Optional` **executor**: `string` + +the executor to use to run the task + +--- + ### hash • `Optional` **hash**: `string` @@ -68,6 +78,14 @@ Unique ID --- +### options + +• `Optional` **options**: `Record`\<`string`, `any`\> + +The options of the executor + +--- + ### outputs • **outputs**: `string`[] diff --git a/e2e/nx-run/src/run.test.ts b/e2e/nx-run/src/run.test.ts index 02417680b415e2..40c33fbcbd94c6 100644 --- a/e2e/nx-run/src/run.test.ts +++ b/e2e/nx-run/src/run.test.ts @@ -230,7 +230,7 @@ describe('Nx Running Tests', () => { }) .split('\n') .map((r) => r.trim()) - .filter((r) => r); + .filter((r) => r && !r.startsWith('Warning:')); withBail = withBail.slice(withBail.indexOf('Failed tasks:')); expect(withBail.length).toEqual(2); if (withBail[1] === `- ${myapp1}:error`) { diff --git a/e2e/webpack/src/webpack.test.ts b/e2e/webpack/src/webpack.test.ts index 9837e29e1efa15..240bcc65bee872 100644 --- a/e2e/webpack/src/webpack.test.ts +++ b/e2e/webpack/src/webpack.test.ts @@ -1,5 +1,4 @@ import { - checkFilesExist, cleanupProject, newProject, rmDist, diff --git a/packages/nx/src/config/task-graph.ts b/packages/nx/src/config/task-graph.ts index 573f691ccc220d..49b585eb64e360 100644 --- a/packages/nx/src/config/task-graph.ts +++ b/packages/nx/src/config/task-graph.ts @@ -27,6 +27,14 @@ export interface Task { * Overrides for the configured options of the target */ overrides: any; + /** + * the executor to use to run the task + */ + executor?: string; + /** + * The options of the executor + */ + options?: Record; /** * The outputs the task may produce diff --git a/packages/nx/src/executors/run-commands/run-commands.impl.ts b/packages/nx/src/executors/run-commands/run-commands.impl.ts index e1e7b4d5c4cac7..64df5dfddf59e0 100644 --- a/packages/nx/src/executors/run-commands/run-commands.impl.ts +++ b/packages/nx/src/executors/run-commands/run-commands.impl.ts @@ -51,6 +51,7 @@ export interface RunCommandsOptions extends Json { args?: string | string[]; envFile?: string; __unparsed__: string[]; + mode?: 'run-one' | 'run-many'; } const propKeys = [ @@ -80,6 +81,7 @@ export default async function ( context: ExecutorContext ): Promise<{ success: boolean; + terminalOutput: string; }> { await loadEnvVars(options.envFile); const normalized = normalizeOptions(options); @@ -100,10 +102,10 @@ export default async function ( } try { - const success = options.parallel + const result = options.parallel ? await runInParallel(normalized, context) : await runSerially(normalized, context); - return { success }; + return result; } catch (e) { if (process.env.NX_VERBOSE_LOGGING === 'true') { console.error(e); @@ -117,7 +119,7 @@ export default async function ( async function runInParallel( options: NormalizedRunCommandsOptions, context: ExecutorContext -) { +): Promise<{ success: boolean; terminalOutput: string }> { const procs = options.commands.map((c) => createProcess( c, @@ -125,35 +127,48 @@ async function runInParallel( options.color, calculateCwd(options.cwd, context), options.env ?? {}, - true - ).then((result) => ({ + true, + options.mode + ).then((result: { success: boolean; terminalOutput: string }) => ({ result, command: c.command, })) ); if (options.readyWhen) { - const r = await Promise.race(procs); - if (!r.result) { + const r: { + result: { success: boolean; terminalOutput: string }; + command: string; + } = await Promise.race(procs); + if (!r.result.success) { process.stderr.write( `Warning: command "${r.command}" exited with non-zero status code` ); - return false; + return { success: false, terminalOutput: r.result.terminalOutput }; } else { - return true; + return { success: true, terminalOutput: r.result.terminalOutput }; } } else { - const r = await Promise.all(procs); - const failed = r.filter((v) => !v.result); + const r: { + result: { success: boolean; terminalOutput: string }; + command: string; + }[] = await Promise.all(procs); + const failed = r.filter((v) => !v.result.success); if (failed.length > 0) { failed.forEach((f) => { process.stderr.write( `Warning: command "${f.command}" exited with non-zero status code` ); }); - return false; + return { + success: false, + terminalOutput: r.map((f) => f.result.terminalOutput).join(''), + }; } else { - return true; + return { + success: true, + terminalOutput: r.map((f) => f.result.terminalOutput).join(''), + }; } } } @@ -188,25 +203,29 @@ function normalizeOptions( async function runSerially( options: NormalizedRunCommandsOptions, context: ExecutorContext -) { +): Promise<{ success: boolean; terminalOutput: string }> { + let terminalOutput = ''; for (const c of options.commands) { - const success = await createProcess( - c, - undefined, - options.color, - calculateCwd(options.cwd, context), - options.env ?? {}, - false - ); - if (!success) { + const result: { success: boolean; terminalOutput: string } = + await createProcess( + c, + undefined, + options.color, + calculateCwd(options.cwd, context), + options.env ?? {}, + false, + options.mode + ); + terminalOutput += result.terminalOutput; + if (!result.success) { process.stderr.write( `Warning: command "${c.command}" exited with non-zero status code` ); - return false; + return { success: false, terminalOutput: result.terminalOutput }; } } - return true; + return { success: true, terminalOutput: terminalOutput }; } async function createProcess( @@ -220,8 +239,9 @@ async function createProcess( color: boolean, cwd: string, env: Record, - isParallel: boolean -): Promise { + isParallel: boolean, + mode: 'run-one' | 'run-many' = 'run-one' +): Promise<{ success: boolean; terminalOutput: string }> { env = processEnv(color, cwd, env); // The rust runCommand is always a tty, so it will not look nice in parallel and if we need prefixes // currently does not work properly in windows @@ -229,32 +249,33 @@ async function createProcess( process.env.NX_NATIVE_COMMAND_RUNNER !== 'false' && process.stdout.isTTY && !commandConfig.prefix && - !isParallel + !isParallel && + mode === 'run-one' ) { const cp = new PseudoTtyProcess( - runCommand(commandConfig.command, cwd, env) + runCommand(commandConfig.command, cwd, env, false) ); return new Promise((res) => { + let terminalOutput = ''; cp.onOutput((output) => { + terminalOutput += output; if (readyWhen && output.indexOf(readyWhen) > -1) { - res(true); + res({ success: true, terminalOutput }); } }); cp.onExit((code) => { - if (code === 0) { - res(true); - } else if (code >= 128) { + if (code >= 128) { process.exit(code); } else { - res(false); + res({ success: code === 0, terminalOutput }); } }); }); } - return nodeProcess(commandConfig, color, cwd, env, readyWhen); + return nodeProcess(commandConfig, cwd, env, readyWhen); } function nodeProcess( @@ -264,11 +285,10 @@ function nodeProcess( bgColor?: string; prefix?: string; }, - color: boolean, cwd: string, env: Record, readyWhen: string -): Promise { +): Promise<{ success: boolean; terminalOutput: string }> { return new Promise((res) => { const childProcess = exec(commandConfig.command, { maxBuffer: LARGE_BUFFER, @@ -286,25 +306,32 @@ function nodeProcess( process.on('SIGINT', processExitListener); process.on('SIGQUIT', processExitListener); + let terminalOutput = ''; childProcess.stdout.on('data', (data) => { - process.stdout.write(addColorAndPrefix(data, commandConfig)); + const output = addColorAndPrefix(data, commandConfig); + terminalOutput += output; + process.stdout.write(output); if (readyWhen && data.toString().indexOf(readyWhen) > -1) { - res(true); + res({ success: true, terminalOutput }); } }); childProcess.stderr.on('data', (err) => { - process.stderr.write(addColorAndPrefix(err, commandConfig)); + const output = addColorAndPrefix(err, commandConfig); + terminalOutput += output; + process.stderr.write(output); if (readyWhen && err.toString().indexOf(readyWhen) > -1) { - res(true); + res({ success: true, terminalOutput }); } }); childProcess.on('error', (err) => { - process.stderr.write(addColorAndPrefix(err.toString(), commandConfig)); - res(false); + const ouptput = addColorAndPrefix(err.toString(), commandConfig); + terminalOutput += ouptput; + process.stderr.write(ouptput); + res({ success: false, terminalOutput }); }); childProcess.on('exit', (code) => { if (!readyWhen) { - res(code === 0); + res({ success: code === 0, terminalOutput }); } }); }); @@ -345,13 +372,15 @@ function calculateCwd( } function processEnv(color: boolean, cwd: string, env: Record) { + const localEnv = appendLocalEnv({ cwd: cwd ?? process.cwd() }); const res = { ...process.env, - ...appendLocalEnv({ cwd: cwd ?? process.cwd() }), + ...localEnv, ...env, }; + res.PATH = localEnv.PATH; // need to override PATH to make sure we are using the local node_modules - if (color) { + if (!!color) { res.FORCE_COLOR = `${color}`; } return res; @@ -364,7 +393,7 @@ export function interpolateArgsIntoCommand( 'args' | 'parsedArgs' | '__unparsed__' >, forwardAllArgs: boolean -) { +): string { if (command.indexOf('{args.') > -1) { const regex = /{args\.([^}]+)}/g; return command.replace(regex, (_, group: string) => diff --git a/packages/nx/src/tasks-runner/create-task-graph.spec.ts b/packages/nx/src/tasks-runner/create-task-graph.spec.ts index dde29f32678aa6..62a4d2f92890f5 100644 --- a/packages/nx/src/tasks-runner/create-task-graph.spec.ts +++ b/packages/nx/src/tasks-runner/create-task-graph.spec.ts @@ -104,6 +104,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'test', }, + executor: 'nx:run-commands', outputs: [], overrides: { a: 123 }, projectRoot: 'app1-root', @@ -134,6 +135,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'test', }, + executor: 'nx:run-commands', outputs: [], overrides: { a: 123 }, projectRoot: 'app1-root', @@ -144,6 +146,7 @@ describe('createTaskGraph', () => { project: 'lib1', target: 'test', }, + executor: 'nx:run-commands', outputs: [], overrides: { a: 123 }, projectRoot: 'lib1-root', @@ -283,6 +286,7 @@ describe('createTaskGraph', () => { target: 'compile', configuration: 'libDefault', }, + executor: 'my-executor', outputs: [], overrides: {}, projectRoot: 'lib1-root', @@ -293,6 +297,7 @@ describe('createTaskGraph', () => { project: 'lib2', target: 'compile', }, + executor: 'my-executor', outputs: [], overrides: { __overrides_unparsed__: [], @@ -325,6 +330,7 @@ describe('createTaskGraph', () => { target: 'compile', configuration: 'ci', }, + executor: 'my-executor', outputs: [], overrides: {}, projectRoot: 'app1-root', @@ -336,6 +342,7 @@ describe('createTaskGraph', () => { target: 'compile', configuration: 'libDefault', }, + executor: 'my-executor', outputs: [], overrides: { __overrides_unparsed__: [], @@ -349,6 +356,7 @@ describe('createTaskGraph', () => { target: 'compile', configuration: 'ci', }, + executor: 'my-executor', outputs: [], overrides: { __overrides_unparsed__: [], @@ -454,6 +462,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'compile', }, + executor: 'my-executor', }, 'lib3:compile': { id: 'lib3:compile', @@ -466,6 +475,7 @@ describe('createTaskGraph', () => { project: 'lib3', target: 'compile', }, + executor: 'my-executor', }, }, }); @@ -491,6 +501,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'test', }, + executor: 'nx:run-commands', outputs: [], overrides: { a: '--value=app1-root' }, projectRoot: 'app1-root', @@ -522,6 +533,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'test', }, + executor: 'nx:run-commands', outputs: [], overrides: { a: '--base-href=/app1-root${deploymentId}' }, projectRoot: 'app1-root', @@ -636,6 +648,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { myFlag: 'flag value' }, projectRoot: 'app1-root', @@ -646,6 +659,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'precompile', }, + executor: 'nx:run-commands', outputs: [], overrides: { myFlag: 'flag value' }, projectRoot: 'app1-root', @@ -656,6 +670,7 @@ describe('createTaskGraph', () => { project: 'lib1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { myFlag: 'flag value' }, projectRoot: 'lib1-root', @@ -666,6 +681,7 @@ describe('createTaskGraph', () => { project: 'lib2', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [] }, projectRoot: 'lib2-root', @@ -701,6 +717,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -713,6 +730,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'precompile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -725,6 +743,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'precompile2', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -737,6 +756,7 @@ describe('createTaskGraph', () => { project: 'lib1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -774,6 +794,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -786,6 +807,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'precompile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -798,6 +820,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'precompile2', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -810,6 +833,7 @@ describe('createTaskGraph', () => { project: 'lib1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -916,6 +940,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -928,6 +953,7 @@ describe('createTaskGraph', () => { project: 'lib1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -940,6 +966,7 @@ describe('createTaskGraph', () => { project: 'lib2', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -952,6 +979,7 @@ describe('createTaskGraph', () => { project: 'lib3', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -1077,6 +1105,7 @@ describe('createTaskGraph', () => { 'infra1:apply': { id: 'infra1:apply', target: { project: 'infra1', target: 'apply' }, + executor: 'nx:run-commands', projectRoot: 'infra1-root', outputs: [], overrides: { myFlag: 'flag value' }, @@ -1084,6 +1113,7 @@ describe('createTaskGraph', () => { 'app2:compile': { id: 'app2:compile', target: { project: 'app2', target: 'compile' }, + executor: 'nx:run-commands', projectRoot: 'app2-root', outputs: [], overrides: { __overrides_unparsed__: [] }, @@ -1091,6 +1121,7 @@ describe('createTaskGraph', () => { 'coreInfra:apply': { id: 'coreInfra:apply', target: { project: 'coreInfra', target: 'apply' }, + executor: 'nx:run-commands', projectRoot: 'infra3-root', outputs: [], overrides: { myFlag: 'flag value' }, @@ -1098,6 +1129,7 @@ describe('createTaskGraph', () => { 'app1:compile': { id: 'app1:compile', target: { project: 'app1', target: 'compile' }, + executor: 'nx:run-commands', projectRoot: 'app1-root', outputs: [], overrides: { __overrides_unparsed__: [] }, @@ -1105,6 +1137,7 @@ describe('createTaskGraph', () => { 'infra2:apply': { id: 'infra2:apply', target: { project: 'infra2', target: 'apply' }, + executor: 'nx:run-commands', projectRoot: 'infra2-root', outputs: [], overrides: { myFlag: 'flag value' }, @@ -1169,6 +1202,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -1181,6 +1215,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'test', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -1262,6 +1297,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -1274,6 +1310,7 @@ describe('createTaskGraph', () => { project: 'app3', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -1352,6 +1389,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -1364,6 +1402,7 @@ describe('createTaskGraph', () => { project: 'app3', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -1440,6 +1479,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -1473,6 +1513,7 @@ describe('createTaskGraph', () => { project: 'app1', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], @@ -1485,6 +1526,7 @@ describe('createTaskGraph', () => { project: 'app2', target: 'compile', }, + executor: 'nx:run-commands', outputs: [], overrides: { __overrides_unparsed__: [], diff --git a/packages/nx/src/tasks-runner/create-task-graph.ts b/packages/nx/src/tasks-runner/create-task-graph.ts index 41ac9c9b48fe44..13e33dea0abd6d 100644 --- a/packages/nx/src/tasks-runner/create-task-graph.ts +++ b/packages/nx/src/tasks-runner/create-task-graph.ts @@ -317,7 +317,9 @@ export class ProcessTasks { ); } - if (!project.data.targets[target].executor) { + const executor = project.data.targets[target].executor; + const options = project.data.targets[target].options; + if (!executor) { throw new Error( `Target "${project.name}:${target}" does not have an executor configured` ); @@ -347,6 +349,8 @@ export class ProcessTasks { ), // TODO(v19): Remove cast here after typing is moved back onto TargetConfiguration cache: (project.data.targets[target] as any).cache, + executor, + options, }; } diff --git a/packages/nx/src/tasks-runner/default-tasks-runner.ts b/packages/nx/src/tasks-runner/default-tasks-runner.ts index 75e80cdc9386b8..3fcee6e675d60f 100644 --- a/packages/nx/src/tasks-runner/default-tasks-runner.ts +++ b/packages/nx/src/tasks-runner/default-tasks-runner.ts @@ -1,6 +1,5 @@ import { TasksRunner, TaskStatus } from './tasks-runner'; import { TaskOrchestrator } from './task-orchestrator'; -import { performance } from 'perf_hooks'; import { TaskHasher } from '../hasher/task-hasher'; import { LifeCycle } from './life-cycle'; import { ProjectGraph } from '../config/project-graph'; @@ -85,7 +84,8 @@ async function runAllTasks( context.taskGraph, options, context.nxArgs?.nxBail, - context.daemon + context.daemon, + context.nxArgs?.mode ); return orchestrator.run(); diff --git a/packages/nx/src/tasks-runner/forked-process-task-runner.ts b/packages/nx/src/tasks-runner/forked-process-task-runner.ts index fe661918e6a430..15730c733b0795 100644 --- a/packages/nx/src/tasks-runner/forked-process-task-runner.ts +++ b/packages/nx/src/tasks-runner/forked-process-task-runner.ts @@ -14,8 +14,8 @@ import { } from './batch/batch-messages'; import { stripIndents } from '../utils/strip-indents'; import { Task, TaskGraph } from '../config/task-graph'; -import { Readable, Transform } from 'stream'; -import { ChildProcess as NativeChildProcess, nxFork } from '../native'; +import { Transform } from 'stream'; +import { nxFork } from '../native'; import { PsuedoIPCServer } from './psuedo-ipc'; import { FORKED_PROCESS_OS_SOCKET_PATH } from '../daemon/socket-utils'; import { PseudoTtyProcess } from '../utils/child-process'; diff --git a/packages/nx/src/tasks-runner/task-orchestrator.ts b/packages/nx/src/tasks-runner/task-orchestrator.ts index da7daa0450ebd5..2498ea03f4b8fb 100644 --- a/packages/nx/src/tasks-runner/task-orchestrator.ts +++ b/packages/nx/src/tasks-runner/task-orchestrator.ts @@ -1,6 +1,7 @@ import { defaultMaxListeners } from 'events'; import { performance } from 'perf_hooks'; import { TaskHasher } from '../hasher/task-hasher'; +import runCommandsImpl from '../executors/run-commands/run-commands.impl'; import { ForkedProcessTaskRunner } from './forked-process-task-runner'; import { Cache } from './cache'; import { DefaultTasksRunnerOptions } from './default-tasks-runner'; @@ -8,6 +9,7 @@ import { TaskStatus } from './tasks-runner'; import { calculateReverseDeps, getExecutorForTask, + getPrintableCommandArgsForTask, isCacheableTask, removeTasksFromTaskGraph, shouldStreamOutput, @@ -24,6 +26,8 @@ import { getTaskSpecificEnv, } from './task-env'; import * as os from 'os'; +import { workspaceRoot } from '../utils/workspace-root'; +import { output } from '../utils/output'; export class TaskOrchestrator { private cache = new Cache(this.options); @@ -63,7 +67,12 @@ export class TaskOrchestrator { private readonly taskGraph: TaskGraph, private readonly options: DefaultTasksRunnerOptions, private readonly bail: boolean, - private readonly daemon: DaemonClient + private readonly daemon: DaemonClient, + private readonly mode: + | 'affected' + | 'print-affected' + | 'run-one' + | 'run-many' ) {} async run() { @@ -376,20 +385,46 @@ export class TaskOrchestrator { // the task wasn't cached if (results.length === 0) { - // cache prep - const { code, terminalOutput } = await this.runTaskInForkedProcess( - task, - env, - pipeOutput, - temporaryOutputPath, - streamOutput - ); + const shouldPrefix = + streamOutput && process.env.NX_PREFIX_OUTPUT === 'true'; + if (task.executor === 'nx:run-commands' && !shouldPrefix) { + const args = getPrintableCommandArgsForTask(task); + output.logCommand(args.join(' ')); + const { success, terminalOutput } = await runCommandsImpl( + { + ...task.options, + env, + __unparsed__: task.overrides?.['__overrides_unparsed__'] ?? [], + mode: + this.mode === 'run-one' && !this.tasksSchedule.hasTasks() + ? 'run-one' + : 'run-many', + }, + { + root: workspaceRoot, // only root is needed in runCommandsImpl + } as any + ); - results.push({ - task, - status: code === 0 ? 'success' : 'failure', - terminalOutput, - }); + results.push({ + task, + status: success ? 'success' : 'failure', + terminalOutput, + }); + } else { + // cache prep + const { code, terminalOutput } = await this.runTaskInForkedProcess( + task, + env, + pipeOutput, + temporaryOutputPath, + streamOutput + ); + results.push({ + task, + status: code === 0 ? 'success' : 'failure', + terminalOutput, + }); + } } await this.postRunSteps([task], results, doNotSkipCache, { groupId }); } diff --git a/packages/nx/src/utils/command-line-utils.spec.ts b/packages/nx/src/utils/command-line-utils.spec.ts index d5b66eefbb4b64..57a3588adc5306 100644 --- a/packages/nx/src/utils/command-line-utils.spec.ts +++ b/packages/nx/src/utils/command-line-utils.spec.ts @@ -37,6 +37,7 @@ describe('splitArgs', () => { base: 'sha1', head: 'sha2', skipNxCache: false, + mode: 'affected', }); }); @@ -68,6 +69,7 @@ describe('splitArgs', () => { ).toEqual({ base: 'main', skipNxCache: false, + mode: 'affected', }); }); @@ -85,6 +87,7 @@ describe('splitArgs', () => { ).toEqual({ base: 'develop', skipNxCache: false, + mode: 'affected', }); }); @@ -102,6 +105,7 @@ describe('splitArgs', () => { ).toEqual({ base: 'main', skipNxCache: false, + mode: 'affected', }); }); @@ -193,6 +197,7 @@ describe('splitArgs', () => { ).toEqual({ projects: ['aaa', 'bbb'], skipNxCache: false, + mode: 'run-many', }); }); @@ -217,6 +222,7 @@ describe('splitArgs', () => { base: 'envVarSha1', head: 'envVarSha2', skipNxCache: false, + mode: 'affected', }); expect( @@ -234,6 +240,7 @@ describe('splitArgs', () => { base: 'envVarSha1', head: 'directlyOnCommandSha1', skipNxCache: false, + mode: 'affected', }); expect( @@ -251,6 +258,7 @@ describe('splitArgs', () => { base: 'directlyOnCommandSha2', head: 'envVarSha2', skipNxCache: false, + mode: 'affected', }); } ); diff --git a/packages/nx/src/utils/command-line-utils.ts b/packages/nx/src/utils/command-line-utils.ts index 1208c6c51b1d58..512e0315ef9e9e 100644 --- a/packages/nx/src/utils/command-line-utils.ts +++ b/packages/nx/src/utils/command-line-utils.ts @@ -36,6 +36,7 @@ export interface NxArgs { nxIgnoreCycles?: boolean; type?: string; batch?: boolean; + mode?: 'affected' | 'print-affected' | 'run-one' | 'run-many'; } export function createOverrides(__overrides_unparsed__: string[] = []) { @@ -84,6 +85,7 @@ export function splitArgsIntoNxArgsAndOverrides( } const nxArgs: RawNxArgs = args; + nxArgs.mode = mode; let overrides = createOverrides(args.__overrides_unparsed__); delete (nxArgs as any).$0;