diff --git a/docs/angular/cli/run-many.md b/docs/angular/cli/run-many.md index 0a247289e0d7f8..6a6df3e8ebb1a6 100644 --- a/docs/angular/cli/run-many.md +++ b/docs/angular/cli/run-many.md @@ -36,6 +36,12 @@ Build proj1 and proj2 and all their dependencies.: nx run-many --target=test --projects=proj1,proj2 --with-deps ``` +Test all projects that are tagged with foo or bar.: + +```bash +nx run-many --target=test --tags=foo,bar +``` + ## Options ### all @@ -78,6 +84,10 @@ Default: `false` Rerun the tasks even when the results are available in the cache +### tags + +Run projects having any of the given tags (comma delimited) + ### target Task to run for affected projects diff --git a/docs/node/cli/run-many.md b/docs/node/cli/run-many.md index 0a247289e0d7f8..6a6df3e8ebb1a6 100644 --- a/docs/node/cli/run-many.md +++ b/docs/node/cli/run-many.md @@ -36,6 +36,12 @@ Build proj1 and proj2 and all their dependencies.: nx run-many --target=test --projects=proj1,proj2 --with-deps ``` +Test all projects that are tagged with foo or bar.: + +```bash +nx run-many --target=test --tags=foo,bar +``` + ## Options ### all @@ -78,6 +84,10 @@ Default: `false` Rerun the tasks even when the results are available in the cache +### tags + +Run projects having any of the given tags (comma delimited) + ### target Task to run for affected projects diff --git a/docs/react/cli/run-many.md b/docs/react/cli/run-many.md index 0a247289e0d7f8..6a6df3e8ebb1a6 100644 --- a/docs/react/cli/run-many.md +++ b/docs/react/cli/run-many.md @@ -36,6 +36,12 @@ Build proj1 and proj2 and all their dependencies.: nx run-many --target=test --projects=proj1,proj2 --with-deps ``` +Test all projects that are tagged with foo or bar.: + +```bash +nx run-many --target=test --tags=foo,bar +``` + ## Options ### all @@ -78,6 +84,10 @@ Default: `false` Rerun the tasks even when the results are available in the cache +### tags + +Run projects having any of the given tags (comma delimited) + ### target Task to run for affected projects diff --git a/e2e/workspace/src/workspace.test.ts b/e2e/workspace/src/workspace.test.ts index b85ced6c1688ef..fb669c83146684 100644 --- a/e2e/workspace/src/workspace.test.ts +++ b/e2e/workspace/src/workspace.test.ts @@ -80,6 +80,7 @@ describe('run-many', () => { const libB = uniq('libb-rand'); const libC = uniq('libc-rand'); const libD = uniq('libd-rand'); + const tagA = uniq('taga-rand'); runCLI(`generate @nrwl/angular:app ${appA}`); runCLI(`generate @nrwl/angular:lib ${libA} --buildable --defaults`); @@ -87,6 +88,12 @@ describe('run-many', () => { runCLI(`generate @nrwl/angular:lib ${libC} --buildable --defaults`); runCLI(`generate @nrwl/angular:lib ${libD} --defaults`); + // Add tagA to libA and libB + const nxJson: NxJson = readJson('nx.json'); + nxJson.projects[libA].tags = [tagA]; + nxJson.projects[libB].tags = [tagA]; + updateFile('nx.json', JSON.stringify(nxJson)); + // libA depends on libC updateFile( `libs/${libA}/src/lib/${libA}.module.spec.ts`, @@ -111,6 +118,15 @@ describe('run-many', () => { expect(buildParallel).not.toContain(`- ${libD}`); expect(buildParallel).toContain('Running target "build" succeeded'); + // testing run many tags starting + const buildTags = runCLI(`run-many --target=build --tags="${tagA}"`); + expect(buildTags).toContain(`Running target build for projects:`); + expect(buildTags).toContain(`- ${libA}`); + expect(buildTags).toContain(`- ${libB}`); + expect(buildTags).not.toContain(`- ${libC}`); + expect(buildTags).not.toContain(`- ${libD}`); + expect(buildTags).toContain('Running target "build" succeeded'); + // testing run many --all starting const buildAllParallel = runCLI(`run-many --target=build --all`); expect(buildAllParallel).toContain(`Running target build for projects:`); diff --git a/packages/workspace/src/command-line/nx-commands.ts b/packages/workspace/src/command-line/nx-commands.ts index 80375f39f63a2f..a3f819959aabf3 100644 --- a/packages/workspace/src/command-line/nx-commands.ts +++ b/packages/workspace/src/command-line/nx-commands.ts @@ -320,15 +320,23 @@ function withRunManyOptions(yargs: yargs.Argv): yargs.Argv { describe: 'Projects to run (comma delimited)', type: 'string', }) + .option('tags', { + describe: 'Run projects having any of the given tags (comma delimited)', + type: 'string', + }) .option('all', { describe: 'Run the target on all projects in the workspace', type: 'boolean', default: undefined, }) - .check(({ all, projects }) => { - if ((all && projects) || (!all && !projects)) - throw new Error('You must provide either --all or --projects'); - return true; + .check(({ all, projects, tags }) => { + const suppliedArgs = +!!all + +!!projects + +!!tags; + if (suppliedArgs === 1) { + return true; + } + throw new Error( + 'You must provide either --all or --projects or --tags' + ); }) .options('runner', { describe: 'Override the tasks runner in `nx.json`', diff --git a/packages/workspace/src/command-line/run-many.ts b/packages/workspace/src/command-line/run-many.ts index 8da2081f1cab55..aa269ee7de2d15 100644 --- a/packages/workspace/src/command-line/run-many.ts +++ b/packages/workspace/src/command-line/run-many.ts @@ -8,7 +8,7 @@ import { ProjectGraphNode, withDeps, } from '../core/project-graph'; -import { readEnvironment } from '../core/file-utils'; +import { readEnvironment, readNxJson } from '../core/file-utils'; import { DefaultReporter } from '../tasks-runner/default-reporter'; import { projectHasTarget } from '../utilities/project-graph-utils'; import { output } from '../utilities/output'; @@ -43,14 +43,28 @@ export async function runMany(parsedArgs: yargs.Arguments) { ); } +function selectProjectsFromTags(tags: string[]): string[] { + let nxJson = readNxJson(); + return Object.entries(nxJson.projects) + .filter(([name, project]) => { + let projectTags = project.tags || []; + return tags.some((tag) => projectTags.includes(tag)); + }) + .map(([name]) => name); +} + function projectsToRun(nxArgs: NxArgs, projectGraph: ProjectGraph) { const allProjects = Object.values(projectGraph.nodes); if (nxArgs.all) { return runnableForTarget(allProjects, nxArgs.target); } else { + let projects = nxArgs.projects; + if (projects.length === 0) { + projects = selectProjectsFromTags(nxArgs.tags); + } checkForInvalidProjects(nxArgs, allProjects); let selectedProjects = allProjects.filter( - (p) => nxArgs.projects.indexOf(p.name) > -1 + (p) => projects.indexOf(p.name) > -1 ); if (nxArgs.withDeps) { selectedProjects = Object.values( diff --git a/packages/workspace/src/command-line/utils.ts b/packages/workspace/src/command-line/utils.ts index 08c3c38a5593fa..cd29762c010eba 100644 --- a/packages/workspace/src/command-line/utils.ts +++ b/packages/workspace/src/command-line/utils.ts @@ -21,7 +21,7 @@ const runOne = [ 'scan', ]; -const runMany = [...runOne, 'projects', 'quiet', 'all', 'verbose']; +const runMany = [...runOne, 'projects', 'tags', 'quiet', 'all', 'verbose']; const runAffected = [ ...runOne, @@ -65,6 +65,7 @@ export interface NxArgs { withDeps?: boolean; 'with-deps'?: boolean; projects?: string[]; + tags?: string[]; select?: string; skipNxCache?: boolean; 'skip-nx-cache'?: boolean; @@ -102,6 +103,13 @@ export function splitArgsIntoNxArgsAndOverrides( .split(',') .map((p: string) => p.trim()); } + if (!nxArgs.tags) { + nxArgs.tags = []; + } else { + nxArgs.tags = (args.tags as string) + .split(',') + .map((p: string) => p.trim()); + } } if (nxArgs.prod) { diff --git a/scripts/documentation/generate-cli-data.ts b/scripts/documentation/generate-cli-data.ts index 49c4b27a9833b5..609816c4ddeff6 100644 --- a/scripts/documentation/generate-cli-data.ts +++ b/scripts/documentation/generate-cli-data.ts @@ -348,6 +348,10 @@ const examples = { command: 'run-many --target=test --projects=proj1,proj2 --with-deps', description: 'Build proj1 and proj2 and all their dependencies.', }, + { + command: 'run-many --target=test --tags=foo,bar', + description: 'Test all projects that are tagged with foo or bar.', + }, ], migrate: [ {