Skip to content

Commit

Permalink
feat(core): run commands directly
Browse files Browse the repository at this point in the history
  • Loading branch information
xiongemi committed Feb 27, 2024
1 parent 452d845 commit 208f165
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 61 deletions.
18 changes: 18 additions & 0 deletions docs/generated/devkit/Task.md
Expand Up @@ -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<string, any>
- [outputs](../../devkit/documents/Task#outputs): string[]
- [overrides](../../devkit/documents/Task#overrides): any
- [projectRoot](../../devkit/documents/Task#projectroot): string
Expand All @@ -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`
Expand Down Expand Up @@ -68,6 +78,14 @@ Unique ID

---

### options

`Optional` **options**: `Record`\<`string`, `any`\>

The options of the executor

---

### outputs

**outputs**: `string`[]
Expand Down
6 changes: 6 additions & 0 deletions docs/generated/packages/nx/executors/run-commands.json
Expand Up @@ -121,6 +121,12 @@
"items": { "type": "string" },
"$default": { "$source": "unparsed" },
"x-priority": "internal"
},
"mode": {
"type": "string",
"description": "Mode to trigger the command.",
"enum": ["run-one", "run-many"],
"x-priority": "internal"
}
},
"additionalProperties": true,
Expand Down
1 change: 0 additions & 1 deletion e2e/webpack/src/webpack.test.ts
@@ -1,5 +1,4 @@
import {
checkFilesExist,
cleanupProject,
newProject,
rmDist,
Expand Down
8 changes: 8 additions & 0 deletions packages/nx/src/config/task-graph.ts
Expand Up @@ -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<string, any>;

/**
* The outputs the task may produce
Expand Down
136 changes: 94 additions & 42 deletions packages/nx/src/executors/run-commands/run-commands.impl.ts
@@ -1,4 +1,4 @@
import { exec } from 'child_process';
import { exec, spawn } from 'child_process';
import * as path from 'path';
import * as yargsParser from 'yargs-parser';
import { env as appendLocalEnv } from 'npm-run-path';
Expand Down Expand Up @@ -51,6 +51,7 @@ export interface RunCommandsOptions extends Json {
args?: string | string[];
envFile?: string;
__unparsed__: string[];
mode?: 'run-one' | 'run-many';
}

const propKeys = [
Expand Down Expand Up @@ -80,6 +81,7 @@ export default async function (
context: ExecutorContext
): Promise<{
success: boolean;
terminalOutput: string;
}> {
await loadEnvVars(options.envFile);
const normalized = normalizeOptions(options);
Expand All @@ -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);
Expand All @@ -117,43 +119,60 @@ export default async function (
async function runInParallel(
options: NormalizedRunCommandsOptions,
context: ExecutorContext
) {
): Promise<{ success: boolean; terminalOutput: string }> {
console.log(
'runInParallel calculateCwd(options.cwd, context),',
calculateCwd(options.cwd, context)
);
const procs = options.commands.map((c) =>
createProcess(
c,
options.readyWhen,
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(''),
};
}
}
}
Expand Down Expand Up @@ -182,31 +201,40 @@ function normalizeOptions(
c.forwardAllArgs ?? true
);
});
console.log(options);
return options as NormalizedRunCommandsOptions;
}

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
console.log(
'runSerially calculateCwd(options.cwd, context),',
calculateCwd(options.cwd, context)
);
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(
Expand All @@ -220,41 +248,57 @@ async function createProcess(
color: boolean,
cwd: string,
env: Record<string, string>,
isParallel: boolean
): Promise<boolean> {
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
console.log(
` process.env.NX_NATIVE_COMMAND_RUNNER !== 'false' &&
process.stdout.isTTY &&
!commandConfig.prefix &&
!isParallel &&
mode !== 'run-many'`,
process.env.NX_NATIVE_COMMAND_RUNNER !== 'false' &&
process.stdout.isTTY &&
!commandConfig.prefix &&
!isParallel &&
mode !== 'run-many'
);
if (
process.env.NX_NATIVE_COMMAND_RUNNER !== 'false' &&
process.stdout.isTTY &&
!commandConfig.prefix &&
!isParallel
!isParallel &&
mode !== 'run-many'
) {
const cp = new PseudoTtyProcess(
runCommand(commandConfig.command, cwd, env)
);

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);
res({ success: true, terminalOutput });
} else if (code >= 128) {
process.exit(code);
} else {
res(false);
res({ success: false, terminalOutput });
}
});
});
}

return nodeProcess(commandConfig, color, cwd, env, readyWhen);
return nodeProcess(commandConfig, cwd, env, readyWhen);
}

function nodeProcess(
Expand All @@ -264,12 +308,12 @@ function nodeProcess(
bgColor?: string;
prefix?: string;
},
color: boolean,
cwd: string,
env: Record<string, string>,
readyWhen: string
): Promise<boolean> {
): Promise<{ success: boolean; terminalOutput: string }> {
return new Promise((res) => {
console.log('nodeProcess', { cwd, env, readyWhen });
const childProcess = exec(commandConfig.command, {
maxBuffer: LARGE_BUFFER,
env,
Expand All @@ -286,25 +330,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 });
}
});
});
Expand Down Expand Up @@ -339,6 +390,7 @@ function calculateCwd(
cwd: string | undefined,
context: ExecutorContext
): string {
console.log('calculateCwd', cwd, context.root);
if (!cwd) return context.root;
if (path.isAbsolute(cwd)) return cwd;
return path.join(context.root, cwd);
Expand All @@ -364,7 +416,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) =>
Expand Down
6 changes: 6 additions & 0 deletions packages/nx/src/executors/run-commands/schema.json
Expand Up @@ -135,6 +135,12 @@
"$source": "unparsed"
},
"x-priority": "internal"
},
"mode": {
"type": "string",
"description": "Mode to trigger the command.",
"enum": ["run-one", "run-many"],
"x-priority": "internal"
}
},
"additionalProperties": true,
Expand Down

0 comments on commit 208f165

Please sign in to comment.