Skip to content

Commit

Permalink
feat(core): add a command to run tasks imperatively
Browse files Browse the repository at this point in the history
  • Loading branch information
vsavkin committed Feb 21, 2023
1 parent 6796bce commit 9a18f68
Show file tree
Hide file tree
Showing 10 changed files with 539 additions and 269 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ jobs:
NX_E2E_CI_CACHE_KEY: e2e-circleci-<< parameters.os >>
SELECTED_PM: << parameters.pm >>
NX_E2E_RUN_CYPRESS: 'false'
NX_VERBOSE_LOGGING: 'true'
NX_VERBOSE_LOGGING: 'false'
NX_PERF_LOGGING: 'false'
NX_NATIVE_HASHER: 'true'
steps:
Expand All @@ -173,7 +173,7 @@ jobs:
executor: linux
environment:
NX_E2E_CI_CACHE_KEY: e2e-circleci-linux
NX_VERBOSE_LOGGING: 'true'
NX_VERBOSE_LOGGING: 'false'
NX_DAEMON: 'true'
NX_PERF_LOGGING: 'false'
NX_NATIVE_HASHER: 'true'
Expand Down
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
66 changes: 66 additions & 0 deletions e2e/nx-run/src/invoke-runner.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
checkFilesExist,
cleanupProject,
fileExists,
isWindows,
newProject,
readFile,
readJson,
readProjectConfig,
removeFile,
runCLI,
runCLIAsync,
runCommand,
tmpProjPath,
uniq,
updateFile,
updateJson,
updateProjectConfig,
} from '@nrwl/e2e/utils';
import { PackageJson } from 'nx/src/utils/package-json';
import * as path from 'path';

describe('Invoke Runner', () => {
let proj: string;
beforeAll(() => (proj = newProject()));
afterAll(() => cleanupProject());

it('should invoke runner imperatively ', async () => {
const mylib = uniq('mylib');
runCLI(`generate @nrwl/workspace:lib ${mylib}`);
updateProjectConfig(mylib, (c) => {
c.targets['prebuild'] = {
command: 'echo prebuild',
};
c.targets['build'] = {
command: 'echo build',
};
return c;
});

updateFile(
'runner.js',
`
const { initTasksRunner } = require('nx/src/index');
async function main(){
const r = await initTasksRunner({});
await r.invoke([{id: '${mylib}:prebuild', target: {project: '${mylib}', target: 'prebuild'}, overrides: {__overrides_unparsed__: ''}}]);
await r.invoke([{id: '${mylib}:build', target: {project: '${mylib}', target: 'build'}, overrides: {__overrides_unparsed__: ''}}]);
}
main().then(q => {
console.log("DONE")
process.exit(0)
})
`
);

const q = runCommand('node runner.js');
expect(q).toContain(`Task ${mylib}:prebuild`);
expect(q).toContain(`Task ${mylib}:build`);
expect(q).toContain(`Successfully ran 1 tasks`);
expect(q).toContain(`DONE`);
});
});
3 changes: 2 additions & 1 deletion graph/client/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
],
"babelUpwardRootMode": true
},
"configurations": {
"dev": {
Expand Down
24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,18 @@
"@ngrx/router-store": "~15.0.0",
"@ngrx/store": "~15.0.0",
"@nguniversal/builders": "~15.1.0",
"@nrwl/cypress": "15.7.0-beta.6",
"@nrwl/devkit": "15.7.0-beta.6",
"@nrwl/eslint-plugin-nx": "15.7.0-beta.6",
"@nrwl/jest": "15.7.0-beta.6",
"@nrwl/js": "15.7.0-beta.6",
"@nrwl/linter": "15.7.0-beta.6",
"@nrwl/next": "15.7.0-beta.6",
"@nrwl/cypress": "15.7.2",
"@nrwl/devkit": "15.7.2",
"@nrwl/eslint-plugin-nx": "15.7.2",
"@nrwl/jest": "15.7.2",
"@nrwl/js": "15.7.2",
"@nrwl/linter": "15.7.2",
"@nrwl/next": "15.7.2",
"@nrwl/nx-cloud": "15.0.3",
"@nrwl/react": "15.7.0-beta.6",
"@nrwl/storybook": "15.7.0-beta.6",
"@nrwl/web": "15.7.0-beta.6",
"@nrwl/webpack": "15.7.0-beta.6",
"@nrwl/react": "15.7.2",
"@nrwl/storybook": "15.7.2",
"@nrwl/web": "15.7.2",
"@nrwl/webpack": "15.7.2",
"@parcel/watcher": "2.0.4",
"@phenomnomnominal/tsquery": "4.1.1",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
Expand Down Expand Up @@ -197,7 +197,7 @@
"next-sitemap": "^3.1.10",
"ng-packagr": "~15.1.0",
"node-fetch": "^2.6.7",
"nx": "15.7.0-beta.6",
"nx": "15.7.2",
"open": "^8.4.0",
"parse-markdown-links": "^1.0.4",
"parse5": "4.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/nx/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { initTasksRunner } from './tasks-runner/init-tasks-runner';
54 changes: 54 additions & 0 deletions packages/nx/src/tasks-runner/init-tasks-runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { workspaceConfigurationCheck } from '../utils/workspace-configuration-check';
import { readNxJson } from '../config/configuration';
import { NxArgs } from '../utils/command-line-utils';
import { createProjectGraphAsync } from '../project-graph/project-graph';
import { Task, TaskGraph } from '../config/task-graph';
import { invokeTasksRunner } from './run-command';
import { InvokeRunnerTerminalOutputLifeCycle } from './life-cycles/invoke-runner-terminal-output-life-cycle';
import { performance } from 'perf_hooks';

export async function initTasksRunner(nxArgs: NxArgs) {
performance.mark('init-local');
workspaceConfigurationCheck();
const nxJson = readNxJson();
if (nxArgs.verbose) {
process.env.NX_VERBOSE_LOGGING = 'true';
}
const projectGraph = await createProjectGraphAsync({ exitOnError: true });
return {
invoke: async (
tasks: Task[]
): Promise<{ status: number; taskGraph: TaskGraph }> => {
performance.mark('command-execution-begins');
const lifeCycle = new InvokeRunnerTerminalOutputLifeCycle(tasks);

const taskGraph = {
roots: tasks.map((task) => task.id),
tasks: tasks.reduce((acc, task) => {
acc[task.id] = task;
return acc;
}, {} as any),
dependencies: tasks.reduce((acc, task) => {
acc[task.id] = [];
return acc;
}, {} as any),
};

const status = await invokeTasksRunner({
tasks,
projectGraph,
taskGraph,
lifeCycle,
nxJson,
nxArgs,
loadDotEnvFiles: true,
initiatingProject: null,
});

return {
status,
taskGraph,
};
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { output } from '../../utils/output';
import { TaskStatus } from '../tasks-runner';
import { getPrintableCommandArgsForTask } from '../utils';
import type { LifeCycle } from '../life-cycle';
import { Task } from '../../config/task-graph';
import { formatFlags, formatTargetsAndProjects } from './formatting-utils';

export class InvokeRunnerTerminalOutputLifeCycle implements LifeCycle {
failedTasks = [] as Task[];
cachedTasks = [] as Task[];

constructor(private readonly tasks: Task[]) {}

startCommand(): void {
output.log({
color: 'cyan',
title: `Running ${this.tasks.length} tasks:`,
bodyLines: this.tasks.map(
(task) =>
`- Task ${task.id} ${
task.overrides.__overrides_unparsed__
? `Overrides: ${task.overrides.__overrides_unparsed__}`
: ''
}`
),
});

output.addVerticalSeparatorWithoutNewLines('cyan');
}

endCommand(): void {
output.addNewline();
const taskIds = this.tasks.map((task) => {
const cached = this.cachedTasks.indexOf(task) !== -1;
const failed = this.failedTasks.indexOf(task) !== -1;
return `- Task ${task.id} ${
task.overrides.__overrides_unparsed__
? `Overrides: ${task.overrides.__overrides_unparsed__}`
: ''
} ${cached ? 'CACHED' : ''} ${failed ? 'FAILED' : ''}`;
});
if (this.failedTasks.length === 0) {
output.addVerticalSeparatorWithoutNewLines('green');
output.success({
title: `Successfully ran ${this.tasks.length} tasks:`,
bodyLines: taskIds,
});
} else {
output.addVerticalSeparatorWithoutNewLines('red');
output.error({
title: `Ran ${this.tasks.length} tasks:`,
bodyLines: taskIds,
});
}
}

endTasks(
taskResults: { task: Task; status: TaskStatus; code: number }[]
): void {
for (let t of taskResults) {
if (t.status === 'failure') {
this.failedTasks.push(t.task);
} else if (t.status === 'local-cache') {
this.cachedTasks.push(t.task);
} else if (t.status === 'local-cache-kept-existing') {
this.cachedTasks.push(t.task);
} else if (t.status === 'remote-cache') {
this.cachedTasks.push(t.task);
}
}
}

printTaskTerminalOutput(
task: Task,
cacheStatus: TaskStatus,
terminalOutput: string
) {
const args = getPrintableCommandArgsForTask(task);
output.logCommand(args.join(' '), cacheStatus);
output.addNewline();
process.stdout.write(terminalOutput);
}
}
Loading

0 comments on commit 9a18f68

Please sign in to comment.