feat(core): add the ability to specify multiple targets
This commit is contained in:
parent
376a4ca496
commit
8572eed44e
@ -149,11 +149,11 @@ Default: `false`
|
||||
|
||||
Rerun the tasks even when the results are available in the cache
|
||||
|
||||
### target
|
||||
### targets
|
||||
|
||||
Type: `string`
|
||||
Type: `array`
|
||||
|
||||
Task to run for affected projects
|
||||
Tasks to run for affected projects
|
||||
|
||||
### uncommitted
|
||||
|
||||
|
||||
@ -81,11 +81,11 @@ Default: `false`
|
||||
|
||||
Rerun the tasks even when the results are available in the cache
|
||||
|
||||
### target
|
||||
### targets
|
||||
|
||||
Type: `string`
|
||||
Type: `array`
|
||||
|
||||
Task to run for affected projects
|
||||
Tasks to run for affected projects
|
||||
|
||||
### verbose
|
||||
|
||||
|
||||
@ -99,11 +99,11 @@ Type: `string`
|
||||
|
||||
Select the subset of the returned json document (e.g., --select=projects)
|
||||
|
||||
### target
|
||||
### targets
|
||||
|
||||
Type: `string`
|
||||
Type: `array`
|
||||
|
||||
Task to run for affected projects
|
||||
Tasks to run for affected projects
|
||||
|
||||
### type
|
||||
|
||||
|
||||
@ -121,11 +121,11 @@ Default: `false`
|
||||
|
||||
Rerun the tasks even when the results are available in the cache
|
||||
|
||||
### target
|
||||
### targets
|
||||
|
||||
Type: `string`
|
||||
Type: `array`
|
||||
|
||||
Task to run for affected projects
|
||||
Tasks to run for affected projects
|
||||
|
||||
### verbose
|
||||
|
||||
|
||||
@ -149,11 +149,11 @@ Default: `false`
|
||||
|
||||
Rerun the tasks even when the results are available in the cache
|
||||
|
||||
### target
|
||||
### targets
|
||||
|
||||
Type: `string`
|
||||
Type: `array`
|
||||
|
||||
Task to run for affected projects
|
||||
Tasks to run for affected projects
|
||||
|
||||
### uncommitted
|
||||
|
||||
|
||||
@ -81,11 +81,11 @@ Default: `false`
|
||||
|
||||
Rerun the tasks even when the results are available in the cache
|
||||
|
||||
### target
|
||||
### targets
|
||||
|
||||
Type: `string`
|
||||
Type: `array`
|
||||
|
||||
Task to run for affected projects
|
||||
Tasks to run for affected projects
|
||||
|
||||
### verbose
|
||||
|
||||
|
||||
@ -99,11 +99,11 @@ Type: `string`
|
||||
|
||||
Select the subset of the returned json document (e.g., --select=projects)
|
||||
|
||||
### target
|
||||
### targets
|
||||
|
||||
Type: `string`
|
||||
Type: `array`
|
||||
|
||||
Task to run for affected projects
|
||||
Tasks to run for affected projects
|
||||
|
||||
### type
|
||||
|
||||
|
||||
@ -121,11 +121,11 @@ Default: `false`
|
||||
|
||||
Rerun the tasks even when the results are available in the cache
|
||||
|
||||
### target
|
||||
### targets
|
||||
|
||||
Type: `string`
|
||||
Type: `array`
|
||||
|
||||
Task to run for affected projects
|
||||
Tasks to run for affected projects
|
||||
|
||||
### verbose
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ Build at: 2022-11-30T16:44:43.171Z - Hash: 9850ece7cc7c6b7c - Time: 6527ms
|
||||
|
||||
—————————————————————————————————————————————————————————————————————————————————————————————————————————————————
|
||||
|
||||
> NX Successfully ran target build for project store and 1 task(s) it depends on (9s)
|
||||
> NX Successfully ran target build for project store and 1 task(s) they depend on (9s)
|
||||
|
||||
```
|
||||
|
||||
@ -100,7 +100,7 @@ Build at: 2022-11-30T16:44:43.171Z - Hash: 9850ece7cc7c6b7c - Time: 6527ms
|
||||
|
||||
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
|
||||
|
||||
> NX Successfully ran target build for project store and 1 task(s) it depends on (13ms)
|
||||
> NX Successfully ran target build for project store and 1 task(s) they depend on (13ms)
|
||||
|
||||
Nx read the output from the cache instead of running the command for 2 out of 2 tasks.
|
||||
```
|
||||
|
||||
@ -198,7 +198,7 @@ Build at: 2022-11-23T22:53:29.097Z - Hash: 3cd478c78ebeead2 - Time: 5154ms
|
||||
|
||||
————————————————————————————————————————————————————————————————————————————————————————————————
|
||||
|
||||
> NX Successfully ran target build for project store and 1 task(s) it depends on (8s)
|
||||
> NX Successfully ran target build for project store and 1 task(s) they depend on (8s)
|
||||
```
|
||||
|
||||
Notice the line here:
|
||||
|
||||
@ -191,7 +191,7 @@ webpack compiled successfully (bafa37be9890ecb2)
|
||||
|
||||
———————————————————————————————————————————————————————————————————————————————————————————————
|
||||
|
||||
> NX Successfully ran target build for project products-cli and 1 task(s) it depends on (2s)
|
||||
> NX Successfully ran target build for project products-cli and 1 task(s) they depend on (2s)
|
||||
|
||||
Nx read the output from the cache instead of running the command for 1 out of 2 tasks.
|
||||
```
|
||||
|
||||
@ -25,7 +25,7 @@ dist/store/assets/index.f18c2b19.js.map 565.79 KiB
|
||||
|
||||
————————————————————————————————————————————————————————————————————————————————————————————————————————
|
||||
|
||||
> NX Successfully ran target build for project store and 2 task(s) it depends on (6s)
|
||||
> NX Successfully ran target build for project store and 2 task(s) they depend on (6s)
|
||||
|
||||
```
|
||||
|
||||
@ -86,7 +86,7 @@ dist/store/assets/index.f18c2b19.js.map 565.79 KiB
|
||||
|
||||
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
|
||||
|
||||
> NX Successfully ran target build for project store and 2 task(s) it depends on (42ms)
|
||||
> NX Successfully ran target build for project store and 2 task(s) they depend on (42ms)
|
||||
|
||||
Nx read the output from the cache instead of running the command for 3 out of 3 tasks.
|
||||
```
|
||||
|
||||
@ -181,7 +181,7 @@ webpack compiled successfully (06e95dfdacea84c7)
|
||||
|
||||
———————————————————————————————————————————————————————————————————————————————————————————————————
|
||||
|
||||
> NX Successfully ran target build for project store and 1 task(s) it depends on (5s)
|
||||
> NX Successfully ran target build for project store and 1 task(s) they depend on (5s)
|
||||
```
|
||||
|
||||
Notice the line here:
|
||||
|
||||
@ -140,7 +140,7 @@ describe('js e2e', () => {
|
||||
});
|
||||
|
||||
const output = runCLI(`build ${parentLib}`);
|
||||
expect(output).toContain('1 task(s) it depends on');
|
||||
expect(output).toContain('1 task it depends on');
|
||||
expect(output).toContain('Done compiling TypeScript files');
|
||||
|
||||
updateJson(`libs/${lib}/tsconfig.json`, (json) => {
|
||||
@ -226,7 +226,7 @@ describe('js e2e', () => {
|
||||
});
|
||||
|
||||
const output = runCLI(`build ${parentLib}`);
|
||||
expect(output).toContain('1 task(s) it depends on');
|
||||
expect(output).toContain('1 task it depends on');
|
||||
expect(output).toContain('Successfully compiled: 2 files with swc');
|
||||
|
||||
updateJson(`libs/${lib}/.lib.swcrc`, (json) => {
|
||||
|
||||
@ -398,7 +398,9 @@ export function tslibC(): string {
|
||||
const fixedStout = runCLI(`lint ${libC} --fix`, {
|
||||
silenceError: true,
|
||||
});
|
||||
expect(fixedStout).toContain('Successfully ran target lint for project');
|
||||
expect(fixedStout).toContain(
|
||||
`Successfully ran target lint for project ${libC}`
|
||||
);
|
||||
|
||||
const fileContent = readFile(`libs/${libC}/src/lib/tslib-c-another.ts`);
|
||||
expect(fileContent).toContain(`import { tslibC } from './tslib-c';`);
|
||||
@ -424,7 +426,9 @@ export function tslibC(): string {
|
||||
const fixedStout = runCLI(`lint ${libB} --fix`, {
|
||||
silenceError: true,
|
||||
});
|
||||
expect(fixedStout).toContain('Successfully ran target lint for project');
|
||||
expect(fixedStout).toContain(
|
||||
`Successfully ran target lint for project ${libB}`
|
||||
);
|
||||
|
||||
const fileContent = readFile(`libs/${libB}/src/lib/tslib-b.ts`);
|
||||
expect(fileContent).toContain(
|
||||
|
||||
@ -129,7 +129,7 @@ describe('Nx Affected and Graph Tests', () => {
|
||||
const build = runCLI(
|
||||
`affected:build --files="libs/${mylib}/src/index.ts" --parallel`
|
||||
);
|
||||
expect(build).toContain(`Running target build for 3 project(s):`);
|
||||
expect(build).toContain(`Running target build for 3 projects:`);
|
||||
expect(build).toContain(`- ${myapp}`);
|
||||
expect(build).toContain(`- ${mypublishablelib}`);
|
||||
expect(build).not.toContain('is not registered with the build command');
|
||||
@ -138,7 +138,7 @@ describe('Nx Affected and Graph Tests', () => {
|
||||
const buildExcluded = runCLI(
|
||||
`affected:build --files="libs/${mylib}/src/index.ts" --exclude ${myapp}`
|
||||
);
|
||||
expect(buildExcluded).toContain(`Running target build for 2 project(s):`);
|
||||
expect(buildExcluded).toContain(`Running target build for 2 projects:`);
|
||||
expect(buildExcluded).toContain(`- ${mypublishablelib}`);
|
||||
|
||||
// test
|
||||
|
||||
@ -290,7 +290,7 @@ describe('Nx Running Tests', () => {
|
||||
|
||||
const output = runCLI(`build ${myapp}`);
|
||||
expect(output).toContain(
|
||||
`NX Running target build for project ${myapp} and 3 task(s) it depends on`
|
||||
`NX Running target build for project ${myapp} and 3 tasks it depends on`
|
||||
);
|
||||
expect(output).toContain(myapp);
|
||||
expect(output).toContain(mylib1);
|
||||
@ -310,7 +310,7 @@ describe('Nx Running Tests', () => {
|
||||
|
||||
const output = runCLI(`build ${myapp}`);
|
||||
expect(output).toContain(
|
||||
`NX Running target build for project ${myapp} and 2 task(s) it depends on`
|
||||
`NX Running target build for project ${myapp} and 2 tasks it depends on`
|
||||
);
|
||||
expect(output).toContain(myapp);
|
||||
expect(output).toContain(mylib1);
|
||||
@ -347,7 +347,7 @@ describe('Nx Running Tests', () => {
|
||||
|
||||
const output = runCLI(`outside ${myapp}`);
|
||||
expect(output).toContain(
|
||||
`NX Running target outside for project ${myapp} and 1 task(s) it depends on`
|
||||
`NX Running target outside for project ${myapp} and 1 task it depends on`
|
||||
);
|
||||
|
||||
removeFile(`one.txt`);
|
||||
@ -391,7 +391,7 @@ describe('Nx Running Tests', () => {
|
||||
const buildParallel = runCLI(
|
||||
`run-many --target=build --projects="${libC},${libB}"`
|
||||
);
|
||||
expect(buildParallel).toContain(`Running target build for 2 project(s):`);
|
||||
expect(buildParallel).toContain(`Running target build for 2 projects:`);
|
||||
expect(buildParallel).not.toContain(`- ${libA}`);
|
||||
expect(buildParallel).toContain(`- ${libB}`);
|
||||
expect(buildParallel).toContain(`- ${libC}`);
|
||||
@ -401,7 +401,7 @@ describe('Nx Running Tests', () => {
|
||||
// testing run many --all starting
|
||||
const buildAllParallel = runCLI(`run-many --target=build`);
|
||||
expect(buildAllParallel).toContain(
|
||||
`Running target build for 4 project(s):`
|
||||
`Running target build for 4 projects:`
|
||||
);
|
||||
expect(buildAllParallel).toContain(`- ${appA}`);
|
||||
expect(buildAllParallel).toContain(`- ${libA}`);
|
||||
@ -415,7 +415,7 @@ describe('Nx Running Tests', () => {
|
||||
`run-many --target=build --projects="${libA}"`
|
||||
);
|
||||
expect(buildWithDeps).toContain(
|
||||
`Running target build for 1 project(s) and 1 task(s) they depend on:`
|
||||
`Running target build for project ${libA} and 1 task it depends on:`
|
||||
);
|
||||
expect(buildWithDeps).toContain(`- ${libA}`);
|
||||
expect(buildWithDeps).toContain(`${libC}`); // build should include libC as dependency
|
||||
@ -428,7 +428,7 @@ describe('Nx Running Tests', () => {
|
||||
`run-many --target=build --projects="${appA},${libA}" --prod`
|
||||
);
|
||||
expect(buildConfig).toContain(
|
||||
`Running target build for 2 project(s) and 1 task(s) they depend on:`
|
||||
`Running target build for 2 projects and 1 task they depend on:`
|
||||
);
|
||||
expect(buildConfig).toContain(`run ${appA}:build:production`);
|
||||
expect(buildConfig).toContain(`run ${libA}:build`);
|
||||
@ -441,6 +441,19 @@ describe('Nx Running Tests', () => {
|
||||
});
|
||||
expect(buildWithDaemon).toContain(`Successfully ran target build`);
|
||||
}, 1000000);
|
||||
|
||||
it('should run multiple targets', () => {
|
||||
const myapp1 = uniq('myapp');
|
||||
const myapp2 = uniq('myapp');
|
||||
runCLI(`generate @nrwl/web:app ${myapp1}`);
|
||||
runCLI(`generate @nrwl/web:app ${myapp2}`);
|
||||
|
||||
let outputs = runCLI(`run-many -t build test -p ${myapp1} ${myapp2}`);
|
||||
expect(outputs).toContain('Running targets build, test for 2 projects:');
|
||||
|
||||
outputs = runCLI(`run-many -t build test -p=${myapp1},${myapp2}`);
|
||||
expect(outputs).toContain('Running targets build, test for 2 projects:');
|
||||
});
|
||||
});
|
||||
|
||||
describe('exec', () => {
|
||||
|
||||
@ -93,7 +93,7 @@ export async function affected(
|
||||
break;
|
||||
|
||||
case 'print-affected':
|
||||
if (nxArgs.target) {
|
||||
if (nxArgs.targets && nxArgs.targets.length > 0) {
|
||||
await printAffected(
|
||||
allProjectsWithTarget(projects, nxArgs),
|
||||
projectGraph,
|
||||
@ -163,7 +163,9 @@ function allProjectsWithTarget(
|
||||
projects: ProjectGraphProjectNode[],
|
||||
nxArgs: NxArgs
|
||||
) {
|
||||
return projects.filter((p) => projectHasTarget(p, nxArgs.target));
|
||||
return projects.filter((p) =>
|
||||
nxArgs.targets.find((target) => projectHasTarget(p, target))
|
||||
);
|
||||
}
|
||||
|
||||
function printError(e: any, verbose?: boolean) {
|
||||
|
||||
@ -571,9 +571,12 @@ function withRunManyOptions(yargs: yargs.Argv): yargs.Argv {
|
||||
'populate--': true,
|
||||
})
|
||||
.option('projects', {
|
||||
type: 'array',
|
||||
string: true,
|
||||
alias: 'p',
|
||||
coerce: parseCSV,
|
||||
describe:
|
||||
'Projects to run. (comma delimited project names and/or patterns)',
|
||||
type: 'string',
|
||||
})
|
||||
.option('all', {
|
||||
describe: '[deprecated] Run the target on all projects in the workspace',
|
||||
@ -649,10 +652,12 @@ function withTargetAndConfigurationOption(
|
||||
yargs: yargs.Argv,
|
||||
demandOption = true
|
||||
): yargs.Argv {
|
||||
return withConfiguration(yargs).option('target', {
|
||||
describe: 'Task to run for affected projects',
|
||||
type: 'string',
|
||||
return withConfiguration(yargs).option('targets', {
|
||||
describe: 'Tasks to run for affected projects',
|
||||
type: 'array',
|
||||
alias: ['target', 't'],
|
||||
requiresArg: true,
|
||||
coerce: parseCSV,
|
||||
demandOption,
|
||||
global: false,
|
||||
});
|
||||
@ -946,6 +951,7 @@ function withWatchOptions(yargs: yargs.Argv) {
|
||||
.option('projects', {
|
||||
type: 'array',
|
||||
string: true,
|
||||
alias: 'p',
|
||||
coerce: parseCSV,
|
||||
description: 'Projects to watch (comma delimited).',
|
||||
})
|
||||
|
||||
@ -22,15 +22,16 @@ export async function printAffected(
|
||||
nxArgs.type ? p.type === nxArgs.type : true
|
||||
);
|
||||
const projectNames = projectsForType.map((p) => p.name);
|
||||
const tasksJson = nxArgs.target
|
||||
? await createTasks(
|
||||
projectsForType,
|
||||
projectGraph,
|
||||
nxArgs,
|
||||
nxJson,
|
||||
overrides
|
||||
)
|
||||
: [];
|
||||
const tasksJson =
|
||||
nxArgs.targets && nxArgs.targets.length > 0
|
||||
? await createTasks(
|
||||
projectsForType,
|
||||
projectGraph,
|
||||
nxArgs,
|
||||
nxJson,
|
||||
overrides
|
||||
)
|
||||
: [];
|
||||
const result = {
|
||||
tasks: tasksJson,
|
||||
projects: projectNames,
|
||||
@ -53,24 +54,28 @@ async function createTasks(
|
||||
const workspaces = new Workspaces(workspaceRoot);
|
||||
const hasher = new Hasher(projectGraph, nxJson, {});
|
||||
const execCommand = getPackageManagerCommand().exec;
|
||||
|
||||
const tasks: Task[] = affectedProjectsWithTargetAndConfig.map(
|
||||
(affectedProject) => {
|
||||
const p = new ProcessTasks({}, projectGraph);
|
||||
const p = new ProcessTasks({}, projectGraph);
|
||||
const tasks = [];
|
||||
for (let target of nxArgs.targets) {
|
||||
for (const affectedProject of affectedProjectsWithTargetAndConfig) {
|
||||
const resolvedConfiguration = p.resolveConfiguration(
|
||||
affectedProject,
|
||||
nxArgs.target,
|
||||
target,
|
||||
nxArgs.configuration
|
||||
);
|
||||
return p.createTask(
|
||||
p.getId(affectedProject.name, nxArgs.target, resolvedConfiguration),
|
||||
affectedProject,
|
||||
nxArgs.target,
|
||||
resolvedConfiguration,
|
||||
overrides
|
||||
);
|
||||
try {
|
||||
tasks.push(
|
||||
p.createTask(
|
||||
p.getId(affectedProject.name, target, resolvedConfiguration),
|
||||
affectedProject,
|
||||
target,
|
||||
resolvedConfiguration,
|
||||
overrides
|
||||
)
|
||||
);
|
||||
} catch (e) {}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
tasks.map((t) => hashTask(workspaces, hasher, projectGraph, {} as any, t))
|
||||
|
||||
@ -38,7 +38,7 @@ describe('run-many', () => {
|
||||
const projects = projectsToRun(
|
||||
{
|
||||
all: true,
|
||||
target: 'test',
|
||||
targets: ['test'],
|
||||
projects: [],
|
||||
},
|
||||
projectGraph
|
||||
@ -50,7 +50,7 @@ describe('run-many', () => {
|
||||
it('should select a project with a target', () => {
|
||||
const projects = projectsToRun(
|
||||
{
|
||||
target: 'test',
|
||||
targets: ['test'],
|
||||
projects: ['proj1'],
|
||||
},
|
||||
projectGraph
|
||||
@ -62,7 +62,7 @@ describe('run-many', () => {
|
||||
it('should filter projects with a pattern', () => {
|
||||
const projects = projectsToRun(
|
||||
{
|
||||
target: 'test',
|
||||
targets: ['test'],
|
||||
projects: ['proj*'],
|
||||
},
|
||||
projectGraph
|
||||
@ -75,7 +75,7 @@ describe('run-many', () => {
|
||||
expect(() => {
|
||||
projectsToRun(
|
||||
{
|
||||
target: 'test',
|
||||
targets: ['test'],
|
||||
projects: ['nomatch*'],
|
||||
},
|
||||
projectGraph
|
||||
@ -87,7 +87,7 @@ describe('run-many', () => {
|
||||
const projects = projectsToRun(
|
||||
{
|
||||
all: true,
|
||||
target: 'test',
|
||||
targets: ['test'],
|
||||
projects: [],
|
||||
exclude: ['proj1'],
|
||||
},
|
||||
@ -101,7 +101,7 @@ describe('run-many', () => {
|
||||
const projects = projectsToRun(
|
||||
{
|
||||
all: true,
|
||||
target: 'test',
|
||||
targets: ['test'],
|
||||
projects: [],
|
||||
exclude: ['proj*'],
|
||||
},
|
||||
@ -131,7 +131,7 @@ describe('run-many', () => {
|
||||
performance.mark('start');
|
||||
projectsToRun(
|
||||
{
|
||||
target: 'test',
|
||||
targets: ['test'],
|
||||
projects: ['proj1*'],
|
||||
exclude: ['proj12*'],
|
||||
},
|
||||
|
||||
@ -56,7 +56,7 @@ export function projectsToRun(
|
||||
projectGraph: ProjectGraph
|
||||
): ProjectGraphProjectNode[] {
|
||||
const selectedProjects = new Map<string, ProjectGraphProjectNode>();
|
||||
const validProjects = runnableForTarget(projectGraph.nodes, nxArgs.target);
|
||||
const validProjects = runnableForTarget(projectGraph.nodes, nxArgs.targets);
|
||||
const validProjectNames = Array.from(validProjects.keys());
|
||||
const invalidProjects: string[] = [];
|
||||
|
||||
@ -122,12 +122,12 @@ export function projectsToRun(
|
||||
|
||||
function runnableForTarget(
|
||||
projects: Record<string, ProjectGraphProjectNode>,
|
||||
target: string
|
||||
targets: string[]
|
||||
): Set<string> {
|
||||
const runnable = new Set<string>();
|
||||
for (let projectName in projects) {
|
||||
const project = projects[projectName];
|
||||
if (projectHasTarget(project, target)) {
|
||||
if (targets.find((target) => projectHasTarget(project, target))) {
|
||||
runnable.add(projectName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ export async function runOne(
|
||||
{
|
||||
...opts.parsedArgs,
|
||||
configuration: opts.configuration,
|
||||
target: opts.target,
|
||||
targets: [opts.target],
|
||||
},
|
||||
'run-one',
|
||||
{ printWarnings: true },
|
||||
|
||||
@ -31,6 +31,10 @@ export const getImplicitlyTouchedProjects: TouchedProjectLocator = (
|
||||
const globalFiles = [
|
||||
...extractGlobalFilesFromInputs(nxJson, projectGraphNodes),
|
||||
'nx.json',
|
||||
'package-lock.json',
|
||||
'yarn.lock',
|
||||
'pnpm-lock.yaml',
|
||||
'pnpm-lock.yml',
|
||||
];
|
||||
globalFiles.forEach((file) => {
|
||||
implicits[file] = '*' as any;
|
||||
|
||||
@ -412,6 +412,63 @@ describe('createTaskGraph', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly set dependencies when they are all given as inputs', () => {
|
||||
const taskGraph = createTaskGraph(
|
||||
projectGraph,
|
||||
{},
|
||||
['app1', 'lib1'],
|
||||
['build', 'prebuild'],
|
||||
'development',
|
||||
{
|
||||
__overrides_unparsed__: [],
|
||||
}
|
||||
);
|
||||
// prebuild should also be in here
|
||||
expect(taskGraph).toEqual({
|
||||
roots: ['app1:prebuild', 'lib1:build'],
|
||||
tasks: {
|
||||
'app1:build': {
|
||||
id: 'app1:build',
|
||||
target: {
|
||||
project: 'app1',
|
||||
target: 'build',
|
||||
},
|
||||
overrides: {
|
||||
__overrides_unparsed__: [],
|
||||
},
|
||||
projectRoot: 'app1-root',
|
||||
},
|
||||
'app1:prebuild': {
|
||||
id: 'app1:prebuild',
|
||||
target: {
|
||||
project: 'app1',
|
||||
target: 'prebuild',
|
||||
},
|
||||
overrides: {
|
||||
__overrides_unparsed__: [],
|
||||
},
|
||||
projectRoot: 'app1-root',
|
||||
},
|
||||
'lib1:build': {
|
||||
id: 'lib1:build',
|
||||
target: {
|
||||
project: 'lib1',
|
||||
target: 'build',
|
||||
},
|
||||
overrides: {
|
||||
__overrides_unparsed__: [],
|
||||
},
|
||||
projectRoot: 'lib1-root',
|
||||
},
|
||||
},
|
||||
dependencies: {
|
||||
'app1:build': ['lib1:build', 'app1:prebuild'],
|
||||
'app1:prebuild': [],
|
||||
'lib1:build': [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle diamond shape dependencies', () => {
|
||||
projectGraph = {
|
||||
nodes: {
|
||||
|
||||
@ -26,21 +26,24 @@ export class ProcessTasks {
|
||||
) {
|
||||
for (const projectName of projectNames) {
|
||||
for (const target of targets) {
|
||||
const resolvedConfiguration = this.resolveConfiguration(
|
||||
this.projectGraph.nodes[projectName],
|
||||
target,
|
||||
configuration
|
||||
);
|
||||
const id = this.getId(projectName, target, resolvedConfiguration);
|
||||
const task = this.createTask(
|
||||
id,
|
||||
this.projectGraph.nodes[projectName],
|
||||
target,
|
||||
resolvedConfiguration,
|
||||
overrides
|
||||
);
|
||||
this.tasks[task.id] = task;
|
||||
this.dependencies[task.id] = [];
|
||||
const project = this.projectGraph.nodes[projectName];
|
||||
if (targets.length === 1 || project.data.targets[target]) {
|
||||
const resolvedConfiguration = this.resolveConfiguration(
|
||||
project,
|
||||
target,
|
||||
configuration
|
||||
);
|
||||
const id = this.getId(projectName, target, resolvedConfiguration);
|
||||
const task = this.createTask(
|
||||
id,
|
||||
project,
|
||||
target,
|
||||
resolvedConfiguration,
|
||||
overrides
|
||||
);
|
||||
this.tasks[task.id] = task;
|
||||
this.dependencies[task.id] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import type { LifeCycle } from '../life-cycle';
|
||||
import type { TaskStatus } from '../tasks-runner';
|
||||
import { Task } from '../../config/task-graph';
|
||||
import { prettyTime } from './pretty-time';
|
||||
import { formatFlags } from './formatting-utils';
|
||||
import { formatFlags, formatTargetsAndProjects } from './formatting-utils';
|
||||
import { viewLogsFooterRows } from './view-logs-utils';
|
||||
|
||||
/**
|
||||
@ -28,7 +28,7 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
}: {
|
||||
projectNames: string[];
|
||||
tasks: Task[];
|
||||
args: { target?: string; configuration?: string; parallel?: number };
|
||||
args: { targets?: string[]; configuration?: string; parallel?: number };
|
||||
overrides: Record<string, unknown>;
|
||||
}): Promise<{ lifeCycle: LifeCycle; renderIsDone: Promise<void> }> {
|
||||
cliCursor.hide();
|
||||
@ -41,8 +41,8 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
});
|
||||
|
||||
function clearRenderInterval() {
|
||||
if (renderProjectRowsIntervalId) {
|
||||
clearInterval(renderProjectRowsIntervalId);
|
||||
if (renderIntervalId) {
|
||||
clearInterval(renderIntervalId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,14 +57,11 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
const start = process.hrtime();
|
||||
const figures = await import('figures');
|
||||
|
||||
const targets = args.targets;
|
||||
const totalTasks = tasks.length;
|
||||
const totalProjects = projectNames.length;
|
||||
const totalDependentTasks = totalTasks - totalProjects;
|
||||
const targetName = args.target;
|
||||
const configuration = args.configuration;
|
||||
const projectRows = projectNames.map((projectName) => {
|
||||
const taskRows = tasks.map((task) => {
|
||||
return {
|
||||
projectName,
|
||||
task,
|
||||
status: 'pending',
|
||||
};
|
||||
});
|
||||
@ -83,8 +80,8 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
let totalCachedTasks = 0;
|
||||
|
||||
// Used to control the rendering of the spinner on each project row
|
||||
let projectRowsCurrentFrame = 0;
|
||||
let renderProjectRowsIntervalId: NodeJS.Timeout | undefined;
|
||||
let currentFrame = 0;
|
||||
let renderIntervalId: NodeJS.Timeout | undefined;
|
||||
|
||||
const clearPinnedFooter = () => {
|
||||
for (let i = 0; i < pinnedFooterNumLines; i++) {
|
||||
@ -180,16 +177,16 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
|
||||
delete tasksToTerminalOutputs[task.id];
|
||||
renderPinnedFooter([]);
|
||||
renderProjectRows();
|
||||
renderRows();
|
||||
};
|
||||
|
||||
const renderProjectRows = () => {
|
||||
const renderRows = () => {
|
||||
const max = dots.frames.length - 1;
|
||||
const curr = projectRowsCurrentFrame;
|
||||
projectRowsCurrentFrame = curr >= max ? 0 : curr + 1;
|
||||
const curr = currentFrame;
|
||||
currentFrame = curr >= max ? 0 : curr + 1;
|
||||
|
||||
const additionalFooterRows: string[] = [''];
|
||||
const runningTasks = projectRows.filter((row) => row.status === 'running');
|
||||
const runningTasks = taskRows.filter((row) => row.status === 'running');
|
||||
const remainingTasks = totalTasks - totalCompletedTasks;
|
||||
|
||||
if (runningTasks.length > 0) {
|
||||
@ -203,16 +200,11 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
)
|
||||
);
|
||||
additionalFooterRows.push('');
|
||||
for (const projectRow of runningTasks) {
|
||||
for (const runningTask of runningTasks) {
|
||||
additionalFooterRows.push(
|
||||
` ${output.dim.cyan(
|
||||
dots.frames[projectRowsCurrentFrame]
|
||||
)} ${output.formatCommand(
|
||||
projectRow.projectName +
|
||||
':' +
|
||||
targetName +
|
||||
(configuration ? ':' + configuration : '')
|
||||
)}`
|
||||
dots.frames[currentFrame]
|
||||
)} ${output.formatCommand(runningTask.task.id)}`
|
||||
);
|
||||
}
|
||||
/**
|
||||
@ -261,15 +253,11 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
clearPinnedFooter();
|
||||
|
||||
if (additionalFooterRows.length > 1) {
|
||||
let text = `Running target ${output.bold.cyan(
|
||||
targetName
|
||||
)} for ${output.bold.cyan(totalProjects)} projects`;
|
||||
if (totalDependentTasks > 0) {
|
||||
text += ` and ${output.bold(
|
||||
totalDependentTasks
|
||||
)} task(s) they depend on`;
|
||||
}
|
||||
|
||||
const text = `Running target ${formatTargetsAndProjects(
|
||||
projectNames,
|
||||
targets,
|
||||
tasks
|
||||
)}`;
|
||||
const taskOverridesRows = [];
|
||||
if (Object.keys(overrides).length > 0) {
|
||||
const leftPadding = `${output.X_PADDING} `;
|
||||
@ -302,14 +290,17 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
};
|
||||
|
||||
lifeCycle.startCommand = () => {
|
||||
if (totalProjects <= 0) {
|
||||
let description = `with target ${output.colors.white.bold(targetName)}`;
|
||||
if (args.configuration) {
|
||||
description += ` that are configured for "${args.configuration}"`;
|
||||
}
|
||||
if (projectNames.length <= 0) {
|
||||
renderPinnedFooter([
|
||||
'',
|
||||
output.applyNxPrefix('gray', `No projects ${description} were run`),
|
||||
output.applyNxPrefix(
|
||||
'gray',
|
||||
`No projects with ${formatTargetsAndProjects(
|
||||
projectNames,
|
||||
targets,
|
||||
tasks
|
||||
)} were run`
|
||||
),
|
||||
]);
|
||||
resolveRenderIsDonePromise();
|
||||
return;
|
||||
@ -323,15 +314,11 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
|
||||
clearPinnedFooter();
|
||||
if (totalSuccessfulTasks === totalTasks) {
|
||||
let text = `Successfully ran target ${output.bold(
|
||||
targetName
|
||||
)} for ${output.bold(totalProjects)} projects`;
|
||||
if (totalDependentTasks > 0) {
|
||||
text += ` and ${output.bold(
|
||||
totalDependentTasks
|
||||
)} task(s) they depend on`;
|
||||
}
|
||||
|
||||
const text = `Successfully ran ${formatTargetsAndProjects(
|
||||
projectNames,
|
||||
targets,
|
||||
tasks
|
||||
)}`;
|
||||
const taskOverridesRows = [];
|
||||
if (Object.keys(overrides).length > 0) {
|
||||
const leftPadding = `${output.X_PADDING} `;
|
||||
@ -362,15 +349,11 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
}
|
||||
renderPinnedFooter(pinnedFooterLines, 'green');
|
||||
} else {
|
||||
let text = `Ran target ${output.bold(targetName)} for ${output.bold(
|
||||
totalProjects
|
||||
)} projects`;
|
||||
if (totalDependentTasks > 0) {
|
||||
text += ` and ${output.bold(
|
||||
totalDependentTasks
|
||||
)} task(s) they depend on`;
|
||||
}
|
||||
|
||||
const text = `Ran ${formatTargetsAndProjects(
|
||||
projectNames,
|
||||
targets,
|
||||
tasks
|
||||
)}`;
|
||||
const taskOverridesRows = [];
|
||||
if (Object.keys(overrides).length > 0) {
|
||||
const leftPadding = `${output.X_PADDING} `;
|
||||
@ -437,17 +420,13 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
for (const task of tasks) {
|
||||
tasksToProcessStartTimes[task.id] = process.hrtime();
|
||||
}
|
||||
for (const projectRow of projectRows) {
|
||||
const matchedTask = tasks.find(
|
||||
(t) => t.target.project === projectRow.projectName
|
||||
);
|
||||
if (!matchedTask) {
|
||||
continue;
|
||||
for (const taskRow of taskRows) {
|
||||
if (tasks.indexOf(taskRow.task) > -1) {
|
||||
taskRow.status = 'running';
|
||||
}
|
||||
projectRow.status = 'running';
|
||||
}
|
||||
if (!renderProjectRowsIntervalId) {
|
||||
renderProjectRowsIntervalId = setInterval(renderProjectRows, 100);
|
||||
if (!renderIntervalId) {
|
||||
renderIntervalId = setInterval(renderRows, 100);
|
||||
}
|
||||
};
|
||||
|
||||
@ -458,11 +437,9 @@ export async function createRunManyDynamicOutputRenderer({
|
||||
lifeCycle.endTasks = (taskResults) => {
|
||||
for (let t of taskResults) {
|
||||
totalCompletedTasks++;
|
||||
const matchingProjectRow = projectRows.find(
|
||||
(pr) => pr.projectName === t.task.target.project
|
||||
);
|
||||
if (matchingProjectRow) {
|
||||
matchingProjectRow.status = t.status;
|
||||
const matchingTaskRow = taskRows.find((r) => r.task === t.task);
|
||||
if (matchingTaskRow) {
|
||||
matchingTaskRow.status = t.status;
|
||||
}
|
||||
|
||||
switch (t.status) {
|
||||
|
||||
@ -6,7 +6,7 @@ import { output } from '../../utils/output';
|
||||
import type { LifeCycle } from '../life-cycle';
|
||||
import { prettyTime } from './pretty-time';
|
||||
import { Task } from '../../config/task-graph';
|
||||
import { formatFlags } from './formatting-utils';
|
||||
import { formatFlags, formatTargetsAndProjects } from './formatting-utils';
|
||||
import { viewLogsFooterRows } from './view-logs-utils';
|
||||
|
||||
/**
|
||||
@ -272,14 +272,11 @@ export async function createRunOneDynamicOutputRenderer({
|
||||
if (totalSuccessfulTasks === totalTasks) {
|
||||
state = 'COMPLETED_SUCCESSFULLY';
|
||||
|
||||
let text = `Successfully ran target ${output.bold(
|
||||
targetName
|
||||
)} for project ${output.bold(initiatingProject)}`;
|
||||
if (totalDependentTasks > 0) {
|
||||
text += ` and ${output.bold(
|
||||
totalDependentTasks
|
||||
)} task(s) it depends on`;
|
||||
}
|
||||
const text = `Successfully ran ${formatTargetsAndProjects(
|
||||
[initiatingProject],
|
||||
[tasks[0].target.target],
|
||||
tasks
|
||||
)}`;
|
||||
|
||||
const taskOverridesLines = [];
|
||||
if (Object.keys(overrides).length > 0) {
|
||||
@ -319,7 +316,7 @@ export async function createRunOneDynamicOutputRenderer({
|
||||
if (totalDependentTasks > 0) {
|
||||
text += ` and ${output.bold(
|
||||
totalDependentTasks
|
||||
)} task(s) it depends on`;
|
||||
)} task(s) they depend on`;
|
||||
}
|
||||
|
||||
const taskOverridesLines = [];
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import { Task } from '../../config/task-graph';
|
||||
import { output } from '../../utils/output';
|
||||
|
||||
export function formatFlags(
|
||||
leftPadding: string,
|
||||
flag: string,
|
||||
@ -17,3 +20,38 @@ function formatValue(value: any) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatTargetsAndProjects(
|
||||
projectNames: string[],
|
||||
targets: string[],
|
||||
tasks: Task[]
|
||||
) {
|
||||
if (tasks.length === 1)
|
||||
return `target ${targets[0]} for project ${projectNames[0]}`;
|
||||
|
||||
let text;
|
||||
const project =
|
||||
projectNames.length === 1
|
||||
? `project ${projectNames[0]}`
|
||||
: `${projectNames.length} projects`;
|
||||
if (targets.length === 1) {
|
||||
text = `target ${output.bold(targets[0])} for ${project}`;
|
||||
} else {
|
||||
text = `targets ${targets
|
||||
.map((t) => output.bold(t))
|
||||
.join(', ')} for ${project}`;
|
||||
}
|
||||
|
||||
const dependentTasks = tasks.filter(
|
||||
(t) =>
|
||||
projectNames.indexOf(t.target.project) === -1 ||
|
||||
targets.indexOf(t.target.target) === -1
|
||||
).length;
|
||||
|
||||
if (dependentTasks > 0) {
|
||||
text += ` and ${output.bold(dependentTasks)} ${
|
||||
dependentTasks === 1 ? 'task' : 'tasks'
|
||||
} ${projectNames.length === 1 ? 'it depends on' : 'they depend on'}`;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { TaskStatus } from '../tasks-runner';
|
||||
import { getPrintableCommandArgsForTask } from '../utils';
|
||||
import type { LifeCycle } from '../life-cycle';
|
||||
import { Task } from '../../config/task-graph';
|
||||
import { formatFlags } from './formatting-utils';
|
||||
import { formatFlags, formatTargetsAndProjects } from './formatting-utils';
|
||||
|
||||
/**
|
||||
* The following life cycle's outputs are static, meaning no previous content
|
||||
@ -22,7 +22,7 @@ export class StaticRunManyTerminalOutputLifeCycle implements LifeCycle {
|
||||
private readonly projectNames: string[],
|
||||
private readonly tasks: Task[],
|
||||
private readonly args: {
|
||||
target?: string;
|
||||
targets?: string[];
|
||||
configuration?: string;
|
||||
},
|
||||
private readonly taskOverrides: any
|
||||
@ -30,11 +30,13 @@ export class StaticRunManyTerminalOutputLifeCycle implements LifeCycle {
|
||||
|
||||
startCommand(): void {
|
||||
if (this.projectNames.length <= 0) {
|
||||
let description = `with "${this.args.target}"`;
|
||||
if (this.args.configuration) {
|
||||
description += ` that are configured for "${this.args.configuration}"`;
|
||||
}
|
||||
output.logSingleLine(`No projects ${description} were run`);
|
||||
output.logSingleLine(
|
||||
`No projects with ${formatTargetsAndProjects(
|
||||
this.projectNames,
|
||||
this.args.targets,
|
||||
this.tasks
|
||||
)} were run`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -49,16 +51,11 @@ export class StaticRunManyTerminalOutputLifeCycle implements LifeCycle {
|
||||
.forEach((arg) => bodyLines.push(arg));
|
||||
}
|
||||
|
||||
let title = `Running target ${output.bold(
|
||||
this.args.target
|
||||
)} for ${output.bold(this.projectNames.length)} project(s)`;
|
||||
const dependentTasksCount = this.tasks.length - this.projectNames.length;
|
||||
if (dependentTasksCount > 0) {
|
||||
title += ` and ${output.bold(
|
||||
dependentTasksCount
|
||||
)} task(s) they depend on`;
|
||||
}
|
||||
title += ':';
|
||||
const title = `Running ${formatTargetsAndProjects(
|
||||
this.projectNames,
|
||||
this.args.targets,
|
||||
this.tasks
|
||||
)}:`;
|
||||
|
||||
output.log({
|
||||
color: 'cyan',
|
||||
@ -85,9 +82,11 @@ export class StaticRunManyTerminalOutputLifeCycle implements LifeCycle {
|
||||
: [];
|
||||
|
||||
output.success({
|
||||
title: `Successfully ran target ${output.bold(
|
||||
this.args.target
|
||||
)} for ${output.bold(this.projectNames.length)} projects`,
|
||||
title: `Successfully ran ${formatTargetsAndProjects(
|
||||
this.projectNames,
|
||||
this.args.targets,
|
||||
this.tasks
|
||||
)}`,
|
||||
bodyLines,
|
||||
});
|
||||
} else {
|
||||
@ -113,7 +112,11 @@ export class StaticRunManyTerminalOutputLifeCycle implements LifeCycle {
|
||||
)
|
||||
);
|
||||
output.error({
|
||||
title: `Running target "${this.args.target}" failed`,
|
||||
title: `Running ${formatTargetsAndProjects(
|
||||
this.projectNames,
|
||||
this.args.targets,
|
||||
this.tasks
|
||||
)} failed`,
|
||||
bodyLines,
|
||||
});
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { TaskStatus } from '../tasks-runner';
|
||||
import { getPrintableCommandArgsForTask } from '../utils';
|
||||
import type { LifeCycle } from '../life-cycle';
|
||||
import { Task } from '../../config/task-graph';
|
||||
import { formatTargetsAndProjects } from './formatting-utils';
|
||||
|
||||
/**
|
||||
* The following life cycle's outputs are static, meaning no previous content
|
||||
@ -21,22 +22,22 @@ export class StaticRunOneTerminalOutputLifeCycle implements LifeCycle {
|
||||
private readonly projectNames: string[],
|
||||
private readonly tasks: Task[],
|
||||
private readonly args: {
|
||||
target?: string;
|
||||
targets?: string[];
|
||||
configuration?: string;
|
||||
}
|
||||
) {}
|
||||
|
||||
startCommand(): void {
|
||||
const numberOfDeps = this.tasks.length - 1;
|
||||
|
||||
const title = `Running ${formatTargetsAndProjects(
|
||||
this.projectNames,
|
||||
this.args.targets,
|
||||
this.tasks
|
||||
)}:`;
|
||||
if (numberOfDeps > 0) {
|
||||
output.log({
|
||||
color: 'cyan',
|
||||
title: `Running target ${output.bold(
|
||||
this.args.target
|
||||
)} for project ${output.bold(this.initiatingProject)} and ${output.bold(
|
||||
numberOfDeps
|
||||
)} task(s) it depends on`,
|
||||
title,
|
||||
});
|
||||
output.addVerticalSeparatorWithoutNewLines('cyan');
|
||||
}
|
||||
@ -58,9 +59,11 @@ export class StaticRunOneTerminalOutputLifeCycle implements LifeCycle {
|
||||
: [];
|
||||
|
||||
output.success({
|
||||
title: `Successfully ran target ${output.bold(
|
||||
this.args.target
|
||||
)} for project ${output.bold(this.initiatingProject)}`,
|
||||
title: `Successfully ran ${formatTargetsAndProjects(
|
||||
this.projectNames,
|
||||
this.args.targets,
|
||||
this.tasks
|
||||
)}`,
|
||||
bodyLines,
|
||||
});
|
||||
} else {
|
||||
@ -76,7 +79,11 @@ export class StaticRunOneTerminalOutputLifeCycle implements LifeCycle {
|
||||
)}`,
|
||||
];
|
||||
output.error({
|
||||
title: `Running target "${this.initiatingProject}:${this.args.target}" failed`,
|
||||
title: `Running ${formatTargetsAndProjects(
|
||||
this.projectNames,
|
||||
this.args.targets,
|
||||
this.tasks
|
||||
)} failed`,
|
||||
bodyLines,
|
||||
});
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ export async function runCommand(
|
||||
projectGraph,
|
||||
defaultDependencyConfigs,
|
||||
projectNames,
|
||||
[nxArgs.target],
|
||||
nxArgs.targets,
|
||||
nxArgs.configuration,
|
||||
overrides,
|
||||
extraOptions.excludeTaskDependencies
|
||||
@ -199,7 +199,6 @@ export async function runCommand(
|
||||
{
|
||||
initiatingProject:
|
||||
nxArgs.outputStyle === 'compact' ? null : initiatingProject,
|
||||
target: nxArgs.target,
|
||||
projectGraph,
|
||||
nxJson,
|
||||
nxArgs,
|
||||
|
||||
@ -12,6 +12,7 @@ export interface RawNxArgs extends NxArgs {
|
||||
|
||||
export interface NxArgs {
|
||||
target?: string;
|
||||
targets?: string[];
|
||||
configuration?: string;
|
||||
runner?: string;
|
||||
parallel?: number;
|
||||
@ -44,6 +45,13 @@ export function splitArgsIntoNxArgsAndOverrides(
|
||||
nxArgs: NxArgs;
|
||||
overrides: Arguments & { __overrides_unparsed__: string[] };
|
||||
} {
|
||||
// this is to lerna case when this function is invoked imperatively
|
||||
if (args['target'] && !args['targets']) {
|
||||
args['targets'] = [args['target']];
|
||||
}
|
||||
delete args['target'];
|
||||
delete args['t'];
|
||||
|
||||
if (!args.__overrides_unparsed__ && args._) {
|
||||
// required for backwards compatibility
|
||||
args.__overrides_unparsed__ = args._;
|
||||
@ -75,10 +83,6 @@ export function splitArgsIntoNxArgsAndOverrides(
|
||||
if (mode === 'run-many') {
|
||||
if (!nxArgs.projects) {
|
||||
nxArgs.projects = [];
|
||||
} else {
|
||||
nxArgs.projects = (args.projects as string)
|
||||
.split(',')
|
||||
.map((p: string) => p.trim());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user