Skip to content

Commit 9a18f68

Browse files
committed
feat(core): add a command to run tasks imperatively
1 parent 6796bce commit 9a18f68

File tree

10 files changed

+539
-269
lines changed

10 files changed

+539
-269
lines changed

.circleci/config.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ jobs:
146146
NX_E2E_CI_CACHE_KEY: e2e-circleci-<< parameters.os >>
147147
SELECTED_PM: << parameters.pm >>
148148
NX_E2E_RUN_CYPRESS: 'false'
149-
NX_VERBOSE_LOGGING: 'true'
149+
NX_VERBOSE_LOGGING: 'false'
150150
NX_PERF_LOGGING: 'false'
151151
NX_NATIVE_HASHER: 'true'
152152
steps:
@@ -173,7 +173,7 @@ jobs:
173173
executor: linux
174174
environment:
175175
NX_E2E_CI_CACHE_KEY: e2e-circleci-linux
176-
NX_VERBOSE_LOGGING: 'true'
176+
NX_VERBOSE_LOGGING: 'false'
177177
NX_DAEMON: 'true'
178178
NX_PERF_LOGGING: 'false'
179179
NX_NATIVE_HASHER: 'true'

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

e2e/nx-run/src/invoke-runner.test.ts

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {
2+
checkFilesExist,
3+
cleanupProject,
4+
fileExists,
5+
isWindows,
6+
newProject,
7+
readFile,
8+
readJson,
9+
readProjectConfig,
10+
removeFile,
11+
runCLI,
12+
runCLIAsync,
13+
runCommand,
14+
tmpProjPath,
15+
uniq,
16+
updateFile,
17+
updateJson,
18+
updateProjectConfig,
19+
} from '@nrwl/e2e/utils';
20+
import { PackageJson } from 'nx/src/utils/package-json';
21+
import * as path from 'path';
22+
23+
describe('Invoke Runner', () => {
24+
let proj: string;
25+
beforeAll(() => (proj = newProject()));
26+
afterAll(() => cleanupProject());
27+
28+
it('should invoke runner imperatively ', async () => {
29+
const mylib = uniq('mylib');
30+
runCLI(`generate @nrwl/workspace:lib ${mylib}`);
31+
updateProjectConfig(mylib, (c) => {
32+
c.targets['prebuild'] = {
33+
command: 'echo prebuild',
34+
};
35+
c.targets['build'] = {
36+
command: 'echo build',
37+
};
38+
return c;
39+
});
40+
41+
updateFile(
42+
'runner.js',
43+
`
44+
const { initTasksRunner } = require('nx/src/index');
45+
46+
async function main(){
47+
const r = await initTasksRunner({});
48+
49+
await r.invoke([{id: '${mylib}:prebuild', target: {project: '${mylib}', target: 'prebuild'}, overrides: {__overrides_unparsed__: ''}}]);
50+
await r.invoke([{id: '${mylib}:build', target: {project: '${mylib}', target: 'build'}, overrides: {__overrides_unparsed__: ''}}]);
51+
}
52+
53+
main().then(q => {
54+
console.log("DONE")
55+
process.exit(0)
56+
})
57+
`
58+
);
59+
60+
const q = runCommand('node runner.js');
61+
expect(q).toContain(`Task ${mylib}:prebuild`);
62+
expect(q).toContain(`Task ${mylib}:build`);
63+
expect(q).toContain(`Successfully ran 1 tasks`);
64+
expect(q).toContain(`DONE`);
65+
});
66+
});

graph/client/project.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@
5656
"maximumWarning": "2mb",
5757
"maximumError": "5mb"
5858
}
59-
]
59+
],
60+
"babelUpwardRootMode": true
6061
},
6162
"configurations": {
6263
"dev": {

package.json

+12-12
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,18 @@
5959
"@ngrx/router-store": "~15.0.0",
6060
"@ngrx/store": "~15.0.0",
6161
"@nguniversal/builders": "~15.1.0",
62-
"@nrwl/cypress": "15.7.0-beta.6",
63-
"@nrwl/devkit": "15.7.0-beta.6",
64-
"@nrwl/eslint-plugin-nx": "15.7.0-beta.6",
65-
"@nrwl/jest": "15.7.0-beta.6",
66-
"@nrwl/js": "15.7.0-beta.6",
67-
"@nrwl/linter": "15.7.0-beta.6",
68-
"@nrwl/next": "15.7.0-beta.6",
62+
"@nrwl/cypress": "15.7.2",
63+
"@nrwl/devkit": "15.7.2",
64+
"@nrwl/eslint-plugin-nx": "15.7.2",
65+
"@nrwl/jest": "15.7.2",
66+
"@nrwl/js": "15.7.2",
67+
"@nrwl/linter": "15.7.2",
68+
"@nrwl/next": "15.7.2",
6969
"@nrwl/nx-cloud": "15.0.3",
70-
"@nrwl/react": "15.7.0-beta.6",
71-
"@nrwl/storybook": "15.7.0-beta.6",
72-
"@nrwl/web": "15.7.0-beta.6",
73-
"@nrwl/webpack": "15.7.0-beta.6",
70+
"@nrwl/react": "15.7.2",
71+
"@nrwl/storybook": "15.7.2",
72+
"@nrwl/web": "15.7.2",
73+
"@nrwl/webpack": "15.7.2",
7474
"@parcel/watcher": "2.0.4",
7575
"@phenomnomnominal/tsquery": "4.1.1",
7676
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
@@ -197,7 +197,7 @@
197197
"next-sitemap": "^3.1.10",
198198
"ng-packagr": "~15.1.0",
199199
"node-fetch": "^2.6.7",
200-
"nx": "15.7.0-beta.6",
200+
"nx": "15.7.2",
201201
"open": "^8.4.0",
202202
"parse-markdown-links": "^1.0.4",
203203
"parse5": "4.0.0",

packages/nx/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { initTasksRunner } from './tasks-runner/init-tasks-runner';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { workspaceConfigurationCheck } from '../utils/workspace-configuration-check';
2+
import { readNxJson } from '../config/configuration';
3+
import { NxArgs } from '../utils/command-line-utils';
4+
import { createProjectGraphAsync } from '../project-graph/project-graph';
5+
import { Task, TaskGraph } from '../config/task-graph';
6+
import { invokeTasksRunner } from './run-command';
7+
import { InvokeRunnerTerminalOutputLifeCycle } from './life-cycles/invoke-runner-terminal-output-life-cycle';
8+
import { performance } from 'perf_hooks';
9+
10+
export async function initTasksRunner(nxArgs: NxArgs) {
11+
performance.mark('init-local');
12+
workspaceConfigurationCheck();
13+
const nxJson = readNxJson();
14+
if (nxArgs.verbose) {
15+
process.env.NX_VERBOSE_LOGGING = 'true';
16+
}
17+
const projectGraph = await createProjectGraphAsync({ exitOnError: true });
18+
return {
19+
invoke: async (
20+
tasks: Task[]
21+
): Promise<{ status: number; taskGraph: TaskGraph }> => {
22+
performance.mark('command-execution-begins');
23+
const lifeCycle = new InvokeRunnerTerminalOutputLifeCycle(tasks);
24+
25+
const taskGraph = {
26+
roots: tasks.map((task) => task.id),
27+
tasks: tasks.reduce((acc, task) => {
28+
acc[task.id] = task;
29+
return acc;
30+
}, {} as any),
31+
dependencies: tasks.reduce((acc, task) => {
32+
acc[task.id] = [];
33+
return acc;
34+
}, {} as any),
35+
};
36+
37+
const status = await invokeTasksRunner({
38+
tasks,
39+
projectGraph,
40+
taskGraph,
41+
lifeCycle,
42+
nxJson,
43+
nxArgs,
44+
loadDotEnvFiles: true,
45+
initiatingProject: null,
46+
});
47+
48+
return {
49+
status,
50+
taskGraph,
51+
};
52+
},
53+
};
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { output } from '../../utils/output';
2+
import { TaskStatus } from '../tasks-runner';
3+
import { getPrintableCommandArgsForTask } from '../utils';
4+
import type { LifeCycle } from '../life-cycle';
5+
import { Task } from '../../config/task-graph';
6+
import { formatFlags, formatTargetsAndProjects } from './formatting-utils';
7+
8+
export class InvokeRunnerTerminalOutputLifeCycle implements LifeCycle {
9+
failedTasks = [] as Task[];
10+
cachedTasks = [] as Task[];
11+
12+
constructor(private readonly tasks: Task[]) {}
13+
14+
startCommand(): void {
15+
output.log({
16+
color: 'cyan',
17+
title: `Running ${this.tasks.length} tasks:`,
18+
bodyLines: this.tasks.map(
19+
(task) =>
20+
`- Task ${task.id} ${
21+
task.overrides.__overrides_unparsed__
22+
? `Overrides: ${task.overrides.__overrides_unparsed__}`
23+
: ''
24+
}`
25+
),
26+
});
27+
28+
output.addVerticalSeparatorWithoutNewLines('cyan');
29+
}
30+
31+
endCommand(): void {
32+
output.addNewline();
33+
const taskIds = this.tasks.map((task) => {
34+
const cached = this.cachedTasks.indexOf(task) !== -1;
35+
const failed = this.failedTasks.indexOf(task) !== -1;
36+
return `- Task ${task.id} ${
37+
task.overrides.__overrides_unparsed__
38+
? `Overrides: ${task.overrides.__overrides_unparsed__}`
39+
: ''
40+
} ${cached ? 'CACHED' : ''} ${failed ? 'FAILED' : ''}`;
41+
});
42+
if (this.failedTasks.length === 0) {
43+
output.addVerticalSeparatorWithoutNewLines('green');
44+
output.success({
45+
title: `Successfully ran ${this.tasks.length} tasks:`,
46+
bodyLines: taskIds,
47+
});
48+
} else {
49+
output.addVerticalSeparatorWithoutNewLines('red');
50+
output.error({
51+
title: `Ran ${this.tasks.length} tasks:`,
52+
bodyLines: taskIds,
53+
});
54+
}
55+
}
56+
57+
endTasks(
58+
taskResults: { task: Task; status: TaskStatus; code: number }[]
59+
): void {
60+
for (let t of taskResults) {
61+
if (t.status === 'failure') {
62+
this.failedTasks.push(t.task);
63+
} else if (t.status === 'local-cache') {
64+
this.cachedTasks.push(t.task);
65+
} else if (t.status === 'local-cache-kept-existing') {
66+
this.cachedTasks.push(t.task);
67+
} else if (t.status === 'remote-cache') {
68+
this.cachedTasks.push(t.task);
69+
}
70+
}
71+
}
72+
73+
printTaskTerminalOutput(
74+
task: Task,
75+
cacheStatus: TaskStatus,
76+
terminalOutput: string
77+
) {
78+
const args = getPrintableCommandArgsForTask(task);
79+
output.logCommand(args.join(' '), cacheStatus);
80+
output.addNewline();
81+
process.stdout.write(terminalOutput);
82+
}
83+
}

0 commit comments

Comments
 (0)