diff --git a/docs/generated/cli/affected-apps.md b/docs/generated/cli/affected-apps.md index 74f2f90dfa5784..1859861fdfd3e3 100644 --- a/docs/generated/cli/affected-apps.md +++ b/docs/generated/cli/affected-apps.md @@ -109,6 +109,12 @@ Default: false Rerun the tasks even when the results are available in the cache +### tags + +Type: string + +Tags to run (comma delimited) + ### uncommitted Type: boolean diff --git a/docs/generated/cli/affected-graph.md b/docs/generated/cli/affected-graph.md index 9db3ed6a475c49..5ad4c7da113823 100644 --- a/docs/generated/cli/affected-graph.md +++ b/docs/generated/cli/affected-graph.md @@ -161,6 +161,12 @@ Default: false Rerun the tasks even when the results are available in the cache +### tags + +Type: string + +Tags to run (comma delimited) + ### uncommitted Type: boolean diff --git a/docs/generated/cli/affected-libs.md b/docs/generated/cli/affected-libs.md index ccf029174bb88b..ee4d4b6b05ba09 100644 --- a/docs/generated/cli/affected-libs.md +++ b/docs/generated/cli/affected-libs.md @@ -109,6 +109,12 @@ Default: false Rerun the tasks even when the results are available in the cache +### tags + +Type: string + +Tags to run (comma delimited) + ### uncommitted Type: boolean diff --git a/docs/generated/cli/affected.md b/docs/generated/cli/affected.md index 3d3f16b58275c4..301472214dfe71 100644 --- a/docs/generated/cli/affected.md +++ b/docs/generated/cli/affected.md @@ -137,6 +137,12 @@ Default: false Rerun the tasks even when the results are available in the cache +### tags + +Type: string + +Tags to run (comma delimited) + ### target Type: string diff --git a/docs/generated/cli/format-check.md b/docs/generated/cli/format-check.md index ffff3813379349..f0a41d126cce4b 100644 --- a/docs/generated/cli/format-check.md +++ b/docs/generated/cli/format-check.md @@ -97,6 +97,12 @@ Default: false Rerun the tasks even when the results are available in the cache +### tags + +Type: string + +Tags to run (comma delimited) + ### uncommitted Type: boolean diff --git a/docs/generated/cli/format-write.md b/docs/generated/cli/format-write.md index a9e280aa32f3e4..8a5cb9e1d879a4 100644 --- a/docs/generated/cli/format-write.md +++ b/docs/generated/cli/format-write.md @@ -97,6 +97,12 @@ Default: false Rerun the tasks even when the results are available in the cache +### tags + +Type: string + +Tags to run (comma delimited) + ### uncommitted Type: boolean diff --git a/docs/generated/cli/print-affected.md b/docs/generated/cli/print-affected.md index bc176dec03bdd3..88e87631cefc15 100644 --- a/docs/generated/cli/print-affected.md +++ b/docs/generated/cli/print-affected.md @@ -123,6 +123,12 @@ Default: false Rerun the tasks even when the results are available in the cache +### tags + +Type: string + +Tags to run (comma delimited) + ### uncommitted Type: boolean diff --git a/docs/generated/cli/run-many.md b/docs/generated/cli/run-many.md index a95aff07240cfe..4e10e1d8560c00 100644 --- a/docs/generated/cli/run-many.md +++ b/docs/generated/cli/run-many.md @@ -107,6 +107,12 @@ Default: false Rerun the tasks even when the results are available in the cache +### tags + +Type: string + +Tags to run (comma delimited) + ### target Type: string diff --git a/packages/nx/src/command-line/affected.ts b/packages/nx/src/command-line/affected.ts index 51f543970043dc..8e15ffa936421a 100644 --- a/packages/nx/src/command-line/affected.ts +++ b/packages/nx/src/command-line/affected.ts @@ -14,7 +14,7 @@ import { performance } from 'perf_hooks'; import { createProjectGraphAsync } from '../project-graph/project-graph'; import { withDeps } from '../project-graph/operators'; import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph'; -import { projectHasTarget } from '../utils/project-graph-utils'; +import { projectHasTarget, projectHasTag } from '../utils/project-graph-utils'; import { filterAffected } from '../project-graph/affected/affected-project-graph'; import { readEnvironment } from './read-environment'; @@ -132,6 +132,9 @@ function projectsToRun( nxArgs ) ); + if (!nxArgs.all && nxArgs.tags) { + affectedGraph = filterByTag(affectedGraph, nxArgs); + } if (!nxArgs.all && nxArgs.withDeps) { affectedGraph = withDeps( projectGraph, @@ -149,6 +152,39 @@ function projectsToRun( return Object.values(affectedGraph.nodes) as ProjectGraphProjectNode[]; } +function filterByTag( + affectedGraph: ProjectGraph, + nxArgs: NxArgs +): ProjectGraph { + const filteredProjects = allProjectsWithTag( + Object.values(affectedGraph.nodes), + nxArgs + ); + const res = { + nodes: filteredProjects.reduce( + (nodes, project) => ({ + ...nodes, + [project.name]: project, + }), + {} + ), + dependencies: affectedGraph.dependencies, + } as ProjectGraph; + return res; +} + +function allProjectsWithTag( + projects: ProjectGraphProjectNode[], + nxArgs: NxArgs +) { + return projects.filter((p) => + nxArgs.tags.reduce( + (matched, tag) => matched || projectHasTag(p, tag), + false + ) + ); +} + function allProjectsWithTarget( projects: ProjectGraphProjectNode[], nxArgs: NxArgs diff --git a/packages/nx/src/command-line/nx-commands.ts b/packages/nx/src/command-line/nx-commands.ts index 79815cccb3cd08..57849a913d5257 100644 --- a/packages/nx/src/command-line/nx-commands.ts +++ b/packages/nx/src/command-line/nx-commands.ts @@ -369,6 +369,11 @@ function withAffectedOptions(yargs: yargs.Argv): yargs.Argv { type: 'boolean', default: undefined, }) + .option('tags', { + describe: 'Tags to run (comma delimited)', + type: 'string', + default: undefined, + }) .option('all', { describe: 'All projects', type: 'boolean', @@ -426,10 +431,11 @@ function withAffectedOptions(yargs: yargs.Argv): yargs.Argv { describe: 'Print additional error stack trace on failure', }) .conflicts({ - files: ['uncommitted', 'untracked', 'base', 'head', 'all'], - untracked: ['uncommitted', 'files', 'base', 'head', 'all'], - uncommitted: ['files', 'untracked', 'base', 'head', 'all'], - all: ['files', 'untracked', 'uncommitted', 'base', 'head'], + files: ['uncommitted', 'untracked', 'base', 'head', 'all', 'tags'], + untracked: ['uncommitted', 'files', 'base', 'head', 'all', 'tags'], + uncommitted: ['files', 'untracked', 'base', 'head', 'all', 'tags'], + all: ['files', 'untracked', 'uncommitted', 'base', 'head', 'tags'], + tags: ['files', 'untracked', 'uncommitted', 'base', 'head', 'all'], }); } @@ -443,15 +449,21 @@ function withRunManyOptions(yargs: yargs.Argv): yargs.Argv { .option('projects', { describe: 'Projects to run (comma delimited)', type: 'string', + default: undefined, + }) + .option('tags', { + describe: 'Tags to run (comma delimited)', + type: 'string', + default: undefined, }) .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 && !tags) + throw new Error('You must provide either --all, --projects or --tags'); return true; }) .options('runner', { @@ -486,7 +498,9 @@ function withRunManyOptions(yargs: yargs.Argv): yargs.Argv { describe: 'Print additional error stack trace on failure', }) .conflicts({ - all: 'projects', + all: ['projects', 'tags'], + projects: ['all', 'tags'], + tags: ['all', 'projects'], }); } diff --git a/packages/nx/src/command-line/run-many.ts b/packages/nx/src/command-line/run-many.ts index 0fda9fadcd1e71..aab61ad269ad4b 100644 --- a/packages/nx/src/command-line/run-many.ts +++ b/packages/nx/src/command-line/run-many.ts @@ -2,7 +2,7 @@ import * as yargs from 'yargs'; import { runCommand } from '../tasks-runner/run-command'; import type { NxArgs, RawNxArgs } from '../utils/command-line-utils'; import { splitArgsIntoNxArgsAndOverrides } from '../utils/command-line-utils'; -import { projectHasTarget } from '../utils/project-graph-utils'; +import { projectHasTarget, projectHasTag } from '../utils/project-graph-utils'; import { output } from '../utils/output'; import { connectToNxCloudUsingScan } from './connect-to-nx-cloud'; import { performance } from 'perf_hooks'; @@ -38,9 +38,13 @@ function projectsToRun( ); } checkForInvalidProjects(nxArgs, allProjects); - let selectedProjects = nxArgs.projects.map((name) => - allProjects.find((project) => project.name === name) - ); + let selectedProjects = nxArgs.tags + ? nxArgs.tags.map((tag) => + allProjects.find((project) => project.data.tags.includes(tag)) + ) + : (nxArgs.projects ?? []).map((name) => + allProjects.find((project) => project.name === name) + ); return runnableForTarget(selectedProjects, nxArgs.target, true).filter( (proj) => !excludedProjects.has(proj.name) ); @@ -50,7 +54,7 @@ function checkForInvalidProjects( nxArgs: NxArgs, allProjects: ProjectGraphProjectNode[] ) { - const invalid = nxArgs.projects.filter( + const invalid = (nxArgs.projects ?? []).filter( (name) => !allProjects.find((p) => p.name === name) ); if (invalid.length !== 0) { diff --git a/packages/nx/src/utils/command-line-utils.ts b/packages/nx/src/utils/command-line-utils.ts index 7806bf911a4c06..733c48e2d29f54 100644 --- a/packages/nx/src/utils/command-line-utils.ts +++ b/packages/nx/src/utils/command-line-utils.ts @@ -84,7 +84,7 @@ const runOne: string[] = [ 'outputStyle', ]; -const runMany: string[] = [...runOne, 'projects', 'all']; +const runMany: string[] = [...runOne, 'projects', 'tags', 'all']; const runAffected: string[] = [ ...runOne, @@ -96,6 +96,7 @@ const runAffected: string[] = [ 'files', 'plain', 'select', + 'tags', ]; export interface RawNxArgs extends NxArgs { @@ -129,6 +130,7 @@ export interface NxArgs { 'output-style'?: string; outputStyle?: string; scan?: boolean; + tags?: string[]; } const ignoreArgs = ['$0', '_']; @@ -163,9 +165,7 @@ export function splitArgsIntoNxArgsAndOverrides( }); if (mode === 'run-many') { - if (!nxArgs.projects) { - nxArgs.projects = []; - } else { + if (nxArgs.projects) { nxArgs.projects = (args.projects as string) .split(',') .map((p: string) => p.trim()); @@ -206,7 +206,7 @@ export function splitArgsIntoNxArgsAndOverrides( !nxArgs.untracked && !nxArgs.base && !nxArgs.head && - !nxArgs.all && + (!nxArgs.all || !nxArgs.tags) && args._.length >= 3 ) { nxArgs.base = args._[1] as string; @@ -246,7 +246,7 @@ export function splitArgsIntoNxArgsAndOverrides( !nxArgs.files && !nxArgs.uncommitted && !nxArgs.untracked && - !nxArgs.all + (!nxArgs.all || !nxArgs.tags) ) { output.note({ title: `Affected criteria defaulted to --base=${output.bold( @@ -255,6 +255,12 @@ export function splitArgsIntoNxArgsAndOverrides( }); } } + + if (nxArgs.tags) { + nxArgs.tags = (args.tags as string) + .split(',') + .map((p: string) => p.trim()); + } } if (!nxArgs.skipNxCache) { diff --git a/packages/nx/src/utils/project-graph-utils.ts b/packages/nx/src/utils/project-graph-utils.ts index 3e4eeea82e41cc..75ac82ffbe5364 100644 --- a/packages/nx/src/utils/project-graph-utils.ts +++ b/packages/nx/src/utils/project-graph-utils.ts @@ -29,6 +29,14 @@ export function projectHasTargetAndConfiguration( ); } +export function projectHasTag(project: ProjectGraphProjectNode, tag: string) { + return !!( + project.data && + project.data.tags && + project.data.tags.includes(tag) + ); +} + export function mergeNpmScriptsWithTargets( projectRoot: string, targets