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 fcff674735a1c5..f7ab4b067ebc63 100644 --- a/e2e/workspace/src/workspace.test.ts +++ b/e2e/workspace/src/workspace.test.ts @@ -66,6 +66,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`); @@ -73,6 +74,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`, @@ -97,6 +104,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 29b159ca13d2aa..8372c08129d200 100644 --- a/packages/workspace/src/command-line/nx-commands.ts +++ b/packages/workspace/src/command-line/nx-commands.ts @@ -315,14 +315,25 @@ 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'); + .check(({ all, projects, tags }) => { + if ( + (all && projects) || + (projects && tags) || + (all && tags) || + (!all && !projects && !tags) + ) + throw new Error( + 'You must provide either --all or --projects or --tags' + ); return true; }) .options('runner', { diff --git a/packages/workspace/src/command-line/run-many.ts b/packages/workspace/src/command-line/run-many.ts index ff4d8bcb1862d2..18e96476acfa1a 100644 --- a/packages/workspace/src/command-line/run-many.ts +++ b/packages/workspace/src/command-line/run-many.ts @@ -9,7 +9,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 '../utils/project-graph-utils'; import { output } from '../utils/output'; @@ -44,14 +44,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 2b0e40fb78c4ee..fce23a292d356a 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 01f979f9574e09..3e4c39d751fcf3 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: [ {