From 63e3e710ab65f173dcd9ddacd59f180cb2e147f9 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Mon, 17 Apr 2023 14:15:14 -0400 Subject: [PATCH 1/3] feat(core): ability to save task graph to a file when running --graph --- docs/generated/cli/affected.md | 4 ++-- docs/generated/cli/exec.md | 4 ++-- docs/generated/cli/run-many.md | 4 ++-- docs/generated/packages/nx/documents/affected.md | 4 ++-- docs/generated/packages/nx/documents/exec.md | 4 ++-- docs/generated/packages/nx/documents/run-many.md | 4 ++-- packages/nx/src/command-line/affected/affected.ts | 7 ++++++- packages/nx/src/command-line/run-many/run-many.ts | 6 ++++++ packages/nx/src/command-line/run/run-one.ts | 6 ++++++ .../nx/src/command-line/yargs-utils/shared-options.ts | 5 +++-- packages/nx/src/utils/command-line-utils.ts | 11 ++++++++++- 11 files changed, 43 insertions(+), 16 deletions(-) diff --git a/docs/generated/cli/affected.md b/docs/generated/cli/affected.md index 3f79efbf0865e..6bc744acdf199 100644 --- a/docs/generated/cli/affected.md +++ b/docs/generated/cli/affected.md @@ -105,11 +105,11 @@ Change the way Nx is calculating the affected command by providing directly chan ### graph -Type: `boolean` +Type: `string` Default: `false` -Show the task graph of the command +Show the task graph of the command. Pass a file path to save the graph data instead of viewing it in the browser. ### head diff --git a/docs/generated/cli/exec.md b/docs/generated/cli/exec.md index f30730dd77684..2a5b82e2d2116 100644 --- a/docs/generated/cli/exec.md +++ b/docs/generated/cli/exec.md @@ -31,11 +31,11 @@ Exclude certain projects from being processed ### graph -Type: `boolean` +Type: `string` Default: `false` -Show the task graph of the command +Show the task graph of the command. Pass a file path to save the graph data instead of viewing it in the browser. ### nx-bail diff --git a/docs/generated/cli/run-many.md b/docs/generated/cli/run-many.md index bcacf2666e21c..21366e40cf290 100644 --- a/docs/generated/cli/run-many.md +++ b/docs/generated/cli/run-many.md @@ -83,11 +83,11 @@ Exclude certain projects from being processed ### graph -Type: `boolean` +Type: `string` Default: `false` -Show the task graph of the command +Show the task graph of the command. Pass a file path to save the graph data instead of viewing it in the browser. ### help diff --git a/docs/generated/packages/nx/documents/affected.md b/docs/generated/packages/nx/documents/affected.md index 3f79efbf0865e..6bc744acdf199 100644 --- a/docs/generated/packages/nx/documents/affected.md +++ b/docs/generated/packages/nx/documents/affected.md @@ -105,11 +105,11 @@ Change the way Nx is calculating the affected command by providing directly chan ### graph -Type: `boolean` +Type: `string` Default: `false` -Show the task graph of the command +Show the task graph of the command. Pass a file path to save the graph data instead of viewing it in the browser. ### head diff --git a/docs/generated/packages/nx/documents/exec.md b/docs/generated/packages/nx/documents/exec.md index f30730dd77684..2a5b82e2d2116 100644 --- a/docs/generated/packages/nx/documents/exec.md +++ b/docs/generated/packages/nx/documents/exec.md @@ -31,11 +31,11 @@ Exclude certain projects from being processed ### graph -Type: `boolean` +Type: `string` Default: `false` -Show the task graph of the command +Show the task graph of the command. Pass a file path to save the graph data instead of viewing it in the browser. ### nx-bail diff --git a/docs/generated/packages/nx/documents/run-many.md b/docs/generated/packages/nx/documents/run-many.md index bcacf2666e21c..21366e40cf290 100644 --- a/docs/generated/packages/nx/documents/run-many.md +++ b/docs/generated/packages/nx/documents/run-many.md @@ -83,11 +83,11 @@ Exclude certain projects from being processed ### graph -Type: `boolean` +Type: `string` Default: `false` -Show the task graph of the command +Show the task graph of the command. Pass a file path to save the graph data instead of viewing it in the browser. ### help diff --git a/packages/nx/src/command-line/affected/affected.ts b/packages/nx/src/command-line/affected/affected.ts index 402f4a04264ee..4d949b5793d51 100644 --- a/packages/nx/src/command-line/affected/affected.ts +++ b/packages/nx/src/command-line/affected/affected.ts @@ -83,7 +83,11 @@ export async function affected( const projectsWithTarget = allProjectsWithTarget(projects, nxArgs); if (nxArgs.graph) { const projectNames = projectsWithTarget.map((t) => t.name); - + const file = + typeof nxArgs.graph === 'string' && + (nxArgs.graph.endsWith('.json') || nxArgs.graph.endsWith('html')) + ? nxArgs.graph + : undefined; return await generateGraph( { watch: false, @@ -91,6 +95,7 @@ export async function affected( view: 'tasks', targets: nxArgs.targets, projects: projectNames, + file, }, projectNames ); diff --git a/packages/nx/src/command-line/run-many/run-many.ts b/packages/nx/src/command-line/run-many/run-many.ts index 16cc92e903713..55e06f1c8aa40 100644 --- a/packages/nx/src/command-line/run-many/run-many.ts +++ b/packages/nx/src/command-line/run-many/run-many.ts @@ -46,6 +46,11 @@ export async function runMany( const projects = projectsToRun(nxArgs, projectGraph); if (nxArgs.graph) { + const file = + typeof nxArgs.graph === 'string' && + (nxArgs.graph.endsWith('.json') || nxArgs.graph.endsWith('html')) + ? nxArgs.graph + : undefined; const projectNames = projects.map((t) => t.name); return await generateGraph( { @@ -55,6 +60,7 @@ export async function runMany( all: nxArgs.all, targets: nxArgs.targets, projects: projectNames, + file, }, projectNames ); diff --git a/packages/nx/src/command-line/run/run-one.ts b/packages/nx/src/command-line/run/run-one.ts index 3ef3099fe78dd..cf13b8b233569 100644 --- a/packages/nx/src/command-line/run/run-one.ts +++ b/packages/nx/src/command-line/run/run-one.ts @@ -66,6 +66,11 @@ export async function runOne( if (nxArgs.graph) { const projectNames = projects.map((t) => t.name); + const file = + typeof nxArgs.graph === 'string' && + (nxArgs.graph.endsWith('.json') || nxArgs.graph.endsWith('html')) + ? nxArgs.graph + : undefined; return await generateGraph( { @@ -74,6 +79,7 @@ export async function runOne( view: 'tasks', targets: nxArgs.targets, projects: projectNames, + file, }, projectNames ); diff --git a/packages/nx/src/command-line/yargs-utils/shared-options.ts b/packages/nx/src/command-line/yargs-utils/shared-options.ts index 8aaab5e901668..45a4046ed387a 100644 --- a/packages/nx/src/command-line/yargs-utils/shared-options.ts +++ b/packages/nx/src/command-line/yargs-utils/shared-options.ts @@ -29,8 +29,9 @@ export function withRunOptions(yargs: Argv): Argv { hidden: true, }) .option('graph', { - type: 'boolean', - describe: 'Show the task graph of the command', + type: 'string', + describe: + 'Show the task graph of the command. Pass a file path to save the graph data instead of viewing it in the browser.', default: false, }) .option('verbose', { diff --git a/packages/nx/src/utils/command-line-utils.ts b/packages/nx/src/utils/command-line-utils.ts index 19b6d9ec4beb2..878ceb62539de 100644 --- a/packages/nx/src/utils/command-line-utils.ts +++ b/packages/nx/src/utils/command-line-utils.ts @@ -29,7 +29,7 @@ export interface NxArgs { plain?: boolean; projects?: string[]; select?: string; - graph?: boolean; + graph?: string | boolean; skipNxCache?: boolean; outputStyle?: string; nxBail?: boolean; @@ -81,6 +81,15 @@ export function splitArgsIntoNxArgsAndOverrides( delete (nxArgs as any).$0; delete (nxArgs as any).__overrides_unparsed__; + if (!(nxArgs.graph === null || nxArgs.graph === undefined)) { + nxArgs.graph = + nxArgs.graph === 'true' || nxArgs.graph === true + ? true + : nxArgs.graph === 'false' || nxArgs.graph === false + ? false + : nxArgs.graph; + } + if (mode === 'run-many') { const args = nxArgs as any; if (!args.projects) { From e5680e0f7730bad9ec788e095911d4fb25f7967d Mon Sep 17 00:00:00 2001 From: AgentEnder Date: Wed, 10 May 2023 17:01:26 -0400 Subject: [PATCH 2/3] feat(core): add support for --graph stdout and graph --file stdout --- e2e/nx-run/src/affected-graph.test.ts | 7 +++++++ .../nx/src/command-line/affected/affected.ts | 12 ++++++------ packages/nx/src/command-line/graph/graph.ts | 6 ++++++ .../nx/src/command-line/run-many/run-many.ts | 11 +++++------ packages/nx/src/command-line/run/run-one.ts | 11 +++++------ .../command-line/yargs-utils/shared-options.ts | 6 ++++++ packages/nx/src/utils/command-line-utils.ts | 17 ++++++----------- 7 files changed, 41 insertions(+), 29 deletions(-) diff --git a/e2e/nx-run/src/affected-graph.test.ts b/e2e/nx-run/src/affected-graph.test.ts index e8db511405135..260af42c12239 100644 --- a/e2e/nx-run/src/affected-graph.test.ts +++ b/e2e/nx-run/src/affected-graph.test.ts @@ -503,6 +503,13 @@ describe('Nx Affected and Graph Tests', () => { expect(environmentJs).toContain('"affected":[]'); }); + it('graph should output valid json when stdout is specified', () => { + const result = runCLI(`graph --out stdout`); + let model; + expect(() => (model = JSON.parse(result))).not.toThrow(); + expect(model).toHaveProperty('nodes'); + }); + it('affected:graph should include affected projects in environment file', () => { runCLI(`affected:graph --file=project-graph.html`); diff --git a/packages/nx/src/command-line/affected/affected.ts b/packages/nx/src/command-line/affected/affected.ts index 4d949b5793d51..b4b0fbb2035d0 100644 --- a/packages/nx/src/command-line/affected/affected.ts +++ b/packages/nx/src/command-line/affected/affected.ts @@ -4,7 +4,10 @@ import { output } from '../../utils/output'; import { generateGraph } from '../graph/graph'; import { printAffected } from './print-affected'; import { connectToNxCloudIfExplicitlyAsked } from '../connect/connect-to-nx-cloud'; -import type { NxArgs } from '../../utils/command-line-utils'; +import { + NxArgs, + readGraphFileFromGraphArg, +} from '../../utils/command-line-utils'; import { parseFiles, splitArgsIntoNxArgsAndOverrides, @@ -83,11 +86,8 @@ export async function affected( const projectsWithTarget = allProjectsWithTarget(projects, nxArgs); if (nxArgs.graph) { const projectNames = projectsWithTarget.map((t) => t.name); - const file = - typeof nxArgs.graph === 'string' && - (nxArgs.graph.endsWith('.json') || nxArgs.graph.endsWith('html')) - ? nxArgs.graph - : undefined; + const file = readGraphFileFromGraphArg(nxArgs); + return await generateGraph( { watch: false, diff --git a/packages/nx/src/command-line/graph/graph.ts b/packages/nx/src/command-line/graph/graph.ts index 4942f53f06f3d..d8744626dae50 100644 --- a/packages/nx/src/command-line/graph/graph.ts +++ b/packages/nx/src/command-line/graph/graph.ts @@ -246,6 +246,12 @@ export async function generateGraph( graph = filterGraph(graph, args.focus || null, args.exclude || []); if (args.file) { + // stdout is a magical constant that doesn't actually write a file + if (args.file === 'stdout') { + console.log(JSON.stringify(graph, null, 2)); + process.exit(0); + } + const workspaceFolder = workspaceRoot; const ext = extname(args.file); const fullFilePath = isAbsolute(args.file) diff --git a/packages/nx/src/command-line/run-many/run-many.ts b/packages/nx/src/command-line/run-many/run-many.ts index 55e06f1c8aa40..968c243043475 100644 --- a/packages/nx/src/command-line/run-many/run-many.ts +++ b/packages/nx/src/command-line/run-many/run-many.ts @@ -1,5 +1,8 @@ import { runCommand } from '../../tasks-runner/run-command'; -import type { NxArgs } from '../../utils/command-line-utils'; +import { + NxArgs, + readGraphFileFromGraphArg, +} from '../../utils/command-line-utils'; import { splitArgsIntoNxArgsAndOverrides } from '../../utils/command-line-utils'; import { projectHasTarget } from '../../utils/project-graph-utils'; import { connectToNxCloudIfExplicitlyAsked } from '../connect/connect-to-nx-cloud'; @@ -46,11 +49,7 @@ export async function runMany( const projects = projectsToRun(nxArgs, projectGraph); if (nxArgs.graph) { - const file = - typeof nxArgs.graph === 'string' && - (nxArgs.graph.endsWith('.json') || nxArgs.graph.endsWith('html')) - ? nxArgs.graph - : undefined; + const file = readGraphFileFromGraphArg(nxArgs); const projectNames = projects.map((t) => t.name); return await generateGraph( { diff --git a/packages/nx/src/command-line/run/run-one.ts b/packages/nx/src/command-line/run/run-one.ts index cf13b8b233569..6b4a6c6eb3fbb 100644 --- a/packages/nx/src/command-line/run/run-one.ts +++ b/packages/nx/src/command-line/run/run-one.ts @@ -1,5 +1,8 @@ import { runCommand } from '../../tasks-runner/run-command'; -import { splitArgsIntoNxArgsAndOverrides } from '../../utils/command-line-utils'; +import { + readGraphFileFromGraphArg, + splitArgsIntoNxArgsAndOverrides, +} from '../../utils/command-line-utils'; import { connectToNxCloudIfExplicitlyAsked } from '../connect/connect-to-nx-cloud'; import { performance } from 'perf_hooks'; import { @@ -66,11 +69,7 @@ export async function runOne( if (nxArgs.graph) { const projectNames = projects.map((t) => t.name); - const file = - typeof nxArgs.graph === 'string' && - (nxArgs.graph.endsWith('.json') || nxArgs.graph.endsWith('html')) - ? nxArgs.graph - : undefined; + const file = readGraphFileFromGraphArg(nxArgs); return await generateGraph( { diff --git a/packages/nx/src/command-line/yargs-utils/shared-options.ts b/packages/nx/src/command-line/yargs-utils/shared-options.ts index 45a4046ed387a..8256b658e992c 100644 --- a/packages/nx/src/command-line/yargs-utils/shared-options.ts +++ b/packages/nx/src/command-line/yargs-utils/shared-options.ts @@ -33,6 +33,12 @@ export function withRunOptions(yargs: Argv): Argv { describe: 'Show the task graph of the command. Pass a file path to save the graph data instead of viewing it in the browser.', default: false, + coerce: (value) => + value === 'true' || value === true + ? true + : value === 'false' || value === false + ? false + : value, }) .option('verbose', { type: 'boolean', diff --git a/packages/nx/src/utils/command-line-utils.ts b/packages/nx/src/utils/command-line-utils.ts index 878ceb62539de..f45c9f72648c9 100644 --- a/packages/nx/src/utils/command-line-utils.ts +++ b/packages/nx/src/utils/command-line-utils.ts @@ -81,15 +81,6 @@ export function splitArgsIntoNxArgsAndOverrides( delete (nxArgs as any).$0; delete (nxArgs as any).__overrides_unparsed__; - if (!(nxArgs.graph === null || nxArgs.graph === undefined)) { - nxArgs.graph = - nxArgs.graph === 'true' || nxArgs.graph === true - ? true - : nxArgs.graph === 'false' || nxArgs.graph === false - ? false - : nxArgs.graph; - } - if (mode === 'run-many') { const args = nxArgs as any; if (!args.projects) { @@ -297,8 +288,6 @@ function getUncommittedFiles(): string[] { return parseGitOutput(`git diff --name-only --no-renames --relative HEAD .`); } -``; - function getUntrackedFiles(): string[] { return parseGitOutput(`git ls-files --others --exclude-standard`); } @@ -347,3 +336,9 @@ export function getProjectRoots( ): string[] { return projectNames.map((name) => nodes[name].data.root); } + +export function readGraphFileFromGraphArg({ graph }: NxArgs) { + return typeof graph === 'string' && graph !== 'true' && graph !== '' + ? graph + : undefined; +} From 8c46a3799c144fbbaa3291e8f38b654fad045e76 Mon Sep 17 00:00:00 2001 From: AgentEnder Date: Fri, 12 May 2023 11:33:28 -0400 Subject: [PATCH 3/3] feat(core): add tasks to json output --- e2e/nx-run/src/affected-graph.test.ts | 5 +- .../nx/src/command-line/affected/affected.ts | 3 +- packages/nx/src/command-line/graph/graph.ts | 77 +++++++++++++++++-- .../nx/src/command-line/run-many/run-many.ts | 2 +- packages/nx/src/command-line/run/run-one.ts | 2 +- 5 files changed, 78 insertions(+), 11 deletions(-) diff --git a/e2e/nx-run/src/affected-graph.test.ts b/e2e/nx-run/src/affected-graph.test.ts index 260af42c12239..798a7e29a7f76 100644 --- a/e2e/nx-run/src/affected-graph.test.ts +++ b/e2e/nx-run/src/affected-graph.test.ts @@ -504,10 +504,11 @@ describe('Nx Affected and Graph Tests', () => { }); it('graph should output valid json when stdout is specified', () => { - const result = runCLI(`graph --out stdout`); + const result = runCLI(`affected -t build --graph stdout`); let model; expect(() => (model = JSON.parse(result))).not.toThrow(); - expect(model).toHaveProperty('nodes'); + expect(model).toHaveProperty('graph'); + expect(model).toHaveProperty('tasks'); }); it('affected:graph should include affected projects in environment file', () => { diff --git a/packages/nx/src/command-line/affected/affected.ts b/packages/nx/src/command-line/affected/affected.ts index b4b0fbb2035d0..15d305ab61111 100644 --- a/packages/nx/src/command-line/affected/affected.ts +++ b/packages/nx/src/command-line/affected/affected.ts @@ -41,7 +41,8 @@ export async function affected( args, 'affected', { - printWarnings: command !== 'print-affected' && !args.plain, + printWarnings: + command !== 'print-affected' && !args.plain && args.graph !== 'stdout', }, nxJson ); diff --git a/packages/nx/src/command-line/graph/graph.ts b/packages/nx/src/command-line/graph/graph.ts index d8744626dae50..352fdbbbc1564 100644 --- a/packages/nx/src/command-line/graph/graph.ts +++ b/packages/nx/src/command-line/graph/graph.ts @@ -187,7 +187,12 @@ export async function generateGraph( }, affectedProjects: string[] ): Promise { - if (Array.isArray(args.targets) && args.targets.length > 1) { + if ( + Array.isArray(args.targets) && + args.targets.length > 1 && + args.file && + !(args.file === 'stdout' || args.file.endsWith('.json')) + ) { output.warn({ title: 'Showing Multiple Targets is not supported yet', bodyLines: [ @@ -248,7 +253,13 @@ export async function generateGraph( if (args.file) { // stdout is a magical constant that doesn't actually write a file if (args.file === 'stdout') { - console.log(JSON.stringify(graph, null, 2)); + console.log( + JSON.stringify( + createJsonOutput(graph, args.projects, args.targets), + null, + 2 + ) + ); process.exit(0); } @@ -300,10 +311,20 @@ export async function generateGraph( } else if (ext === '.json') { ensureDirSync(dirname(fullFilePath)); - writeJsonFile(fullFilePath, { - graph, - affectedProjects, - criticalPath: affectedProjects, + const json = createJsonOutput(graph, args.projects, args.targets); + json.affectedProjects = affectedProjects; + json.criticalPath = affectedProjects; + + writeJsonFile(fullFilePath, json); + + output.warn({ + title: 'JSON output contains deprecated fields:', + bodyLines: [ + '- affectedProjects', + '- criticalPath', + '', + 'These fields will be removed in Nx 18. If you need to see which projects were affected, use `nx show projects --affected`.', + ], }); output.success({ @@ -691,3 +712,47 @@ function createTaskId( return `${projectId}:${targetId}`; } } + +interface GraphJsonResponse { + tasks?: TaskGraph; + graph: ProjectGraph; + + /** + * @deprecated To see affected projects, use `nx show projects --affected`. This will be removed in Nx 18. + */ + affectedProjects?: string[]; + + /** + * @deprecated To see affected projects, use `nx show projects --affected`. This will be removed in Nx 18. + */ + criticalPath?: string[]; +} + +function createJsonOutput( + graph: ProjectGraph, + projects: string[], + targets?: string[] +): GraphJsonResponse { + const response: GraphJsonResponse = { + graph, + }; + + if (targets?.length) { + const nxJson = readNxJson(); + + const defaultDependencyConfigs = mapTargetDefaultsToDependencies( + nxJson.targetDefaults + ); + + response.tasks = createTaskGraph( + graph, + defaultDependencyConfigs, + projects, + targets, + undefined, + {} + ); + } + + return response; +} diff --git a/packages/nx/src/command-line/run-many/run-many.ts b/packages/nx/src/command-line/run-many/run-many.ts index 968c243043475..0a7733e115683 100644 --- a/packages/nx/src/command-line/run-many/run-many.ts +++ b/packages/nx/src/command-line/run-many/run-many.ts @@ -36,7 +36,7 @@ export async function runMany( const { nxArgs, overrides } = splitArgsIntoNxArgsAndOverrides( args, 'run-many', - { printWarnings: true }, + { printWarnings: args.graph !== 'stdout' }, nxJson ); if (nxArgs.verbose) { diff --git a/packages/nx/src/command-line/run/run-one.ts b/packages/nx/src/command-line/run/run-one.ts index 6b4a6c6eb3fbb..68beffaa06b4e 100644 --- a/packages/nx/src/command-line/run/run-one.ts +++ b/packages/nx/src/command-line/run/run-one.ts @@ -50,7 +50,7 @@ export async function runOne( targets: [opts.target], }, 'run-one', - { printWarnings: true }, + { printWarnings: args.graph !== 'stdout' }, nxJson ); if (nxArgs.verbose) {