feat(core): add parallelism to target configuration (#26820)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
This commit is contained in:
Emily Xiong 2024-07-12 13:35:45 -07:00 committed by GitHub
parent 37f0ccaa20
commit 39e104b6c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 624 additions and 38 deletions

View File

@ -22,6 +22,7 @@ Target's configuration
- [metadata](../../devkit/documents/TargetConfiguration#metadata): TargetMetadata
- [options](../../devkit/documents/TargetConfiguration#options): T
- [outputs](../../devkit/documents/TargetConfiguration#outputs): string[]
- [parallelism](../../devkit/documents/TargetConfiguration#parallelism): boolean
## Properties
@ -109,3 +110,12 @@ Target's options. They are passed in to the executor.
List of the target's outputs. The outputs will be cached by the Nx computation
caching engine.
---
### parallelism
`Optional` **parallelism**: `boolean`
Whether this target can be run in parallel with other tasks
Default is true

View File

@ -13,6 +13,7 @@ A representation of the invocation of an Executor
- [id](../../devkit/documents/Task#id): string
- [outputs](../../devkit/documents/Task#outputs): string[]
- [overrides](../../devkit/documents/Task#overrides): any
- [parallelism](../../devkit/documents/Task#parallelism): boolean
- [projectRoot](../../devkit/documents/Task#projectroot): string
- [startTime](../../devkit/documents/Task#starttime): number
- [target](../../devkit/documents/Task#target): Object
@ -84,6 +85,14 @@ Overrides for the configured options of the target
---
### parallelism
**parallelism**: `boolean`
Determines if a given task should be parallelizable.
---
### projectRoot
`Optional` **projectRoot**: `string`

View File

@ -121,6 +121,7 @@ describe('@nx/cypress/plugin', () => {
"{projectRoot}/dist/videos",
"{projectRoot}/dist/screenshots",
],
"parallelism": false,
},
"open-cypress": {
"command": "cypress open",
@ -329,6 +330,7 @@ describe('@nx/cypress/plugin', () => {
"{projectRoot}/dist/videos",
"{projectRoot}/dist/screenshots",
],
"parallelism": false,
},
"e2e-ci": {
"cache": true,
@ -369,6 +371,7 @@ describe('@nx/cypress/plugin', () => {
"{projectRoot}/dist/videos",
"{projectRoot}/dist/screenshots",
],
"parallelism": false,
},
"e2e-ci--src/test.cy.ts": {
"cache": true,
@ -404,6 +407,7 @@ describe('@nx/cypress/plugin', () => {
"{projectRoot}/dist/videos",
"{projectRoot}/dist/screenshots",
],
"parallelism": false,
},
"open-cypress": {
"command": "cypress open",

View File

@ -211,6 +211,7 @@ async function buildCypressTargets(
cache: true,
inputs: getInputs(namedInputs),
outputs: getOutputs(projectRoot, cypressConfig, 'e2e'),
parallelism: false,
metadata: {
technologies: ['cypress'],
description: 'Runs Cypress Tests',
@ -276,6 +277,7 @@ async function buildCypressTargets(
options: {
cwd: projectRoot,
},
parallelism: false,
metadata: {
technologies: ['cypress'],
description: `Runs Cypress Tests in ${relativeSpecFilePath} in CI`,
@ -300,6 +302,7 @@ async function buildCypressTargets(
inputs,
outputs,
dependsOn,
parallelism: false,
metadata: {
technologies: ['cypress'],
description: 'Runs Cypress Tests in CI',

View File

@ -389,30 +389,35 @@ describe('calculateDependenciesFromTaskGraph', () => {
overrides: {},
target: { project: 'lib1', target: 'build' },
outputs: [],
parallelism: true,
},
'lib2:build': {
id: 'lib2:build',
overrides: {},
target: { project: 'lib2', target: 'build' },
outputs: [],
parallelism: true,
},
'lib2:build-base': {
id: 'lib2:build-base',
overrides: {},
target: { project: 'lib2', target: 'build-base' },
outputs: [],
parallelism: true,
},
'lib3:build': {
id: 'lib3:build',
overrides: {},
target: { project: 'lib3', target: 'build' },
outputs: [],
parallelism: true,
},
'lib4:build': {
id: 'lib4:build',
overrides: {},
target: { project: 'lib4', target: 'build' },
outputs: [],
parallelism: true,
},
},
};
@ -559,48 +564,56 @@ describe('calculateDependenciesFromTaskGraph', () => {
overrides: {},
target: { project: 'lib1', target: 'build' },
outputs: [],
parallelism: true,
},
'lib1:build-base': {
id: 'lib1:build-base',
overrides: {},
target: { project: 'lib1', target: 'build-base' },
outputs: [],
parallelism: true,
},
'lib2:build': {
id: 'lib2:build',
overrides: {},
target: { project: 'lib2', target: 'build' },
outputs: [],
parallelism: true,
},
'lib2:build-base': {
id: 'lib2:build-base',
overrides: {},
target: { project: 'lib2', target: 'build-base' },
outputs: [],
parallelism: true,
},
'lib3:build': {
id: 'lib3:build',
overrides: {},
target: { project: 'lib3', target: 'build' },
outputs: [],
parallelism: true,
},
'lib3:build-base': {
id: 'lib3:build-base',
overrides: {},
target: { project: 'lib3', target: 'build-base' },
outputs: [],
parallelism: true,
},
'lib4:build': {
id: 'lib4:build',
overrides: {},
target: { project: 'lib4', target: 'build' },
outputs: [],
parallelism: true,
},
'lib4:build-base': {
id: 'lib4:build-base',
overrides: {},
target: { project: 'lib4', target: 'build-base' },
outputs: [],
parallelism: true,
},
},
};

View File

@ -4,6 +4,27 @@
"title": "JSON schema for Nx projects",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Project's name. Optional if specified in workspace.json"
},
"root": {
"type": "string",
"description": "Project's location relative to the root of the workspace"
},
"sourceRoot": {
"type": "string",
"description": "The location of project's sources relative to the root of the workspace"
},
"projectType": {
"type": "string",
"description": "Type of project supported",
"enum": ["library", "application"]
},
"generators": {
"type": "object",
"description": "List of default values used by generators"
},
"namedInputs": {
"type": "object",
"description": "Named inputs used by inputs defined in targets",
@ -112,6 +133,11 @@
"cache": {
"type": "boolean",
"description": "Specifies if the given target should be cacheable"
},
"parallelism": {
"type": "boolean",
"default": true,
"description": "Whether this target can be run in parallel with other tasks"
}
}
}

View File

@ -76,6 +76,11 @@ export interface Task {
* Determines if a given task should be cacheable.
*/
cache?: boolean;
/**
* Determines if a given task should be parallelizable.
*/
parallelism: boolean;
}
/**

View File

@ -73,6 +73,7 @@ describe('Workspaces', () => {
],
"executor": "@nx/js:release-publish",
"options": {},
"parallelism": true,
},
},
}

View File

@ -230,4 +230,10 @@ export interface TargetConfiguration<T = any> {
* Metadata about the target
*/
metadata?: TargetMetadata;
/**
* Whether this target can be run in parallel with other tasks
* Default is true
*/
parallelism?: boolean;
}

View File

@ -155,21 +155,21 @@ describe('native task hasher', () => {
"AllExternalDependencies": "3244421341483603138",
"env:NONEXISTENTENV": "3244421341483603138",
"env:TESTENV": "11441948532827618368",
"parent:ProjectConfiguration": "4131510303084753861",
"parent:ProjectConfiguration": "3608670998275221195",
"parent:TsConfig": "2264969541778889434",
"parent:{projectRoot}/**/*": "15295586939211629225",
"runtime:echo runtime123": "29846575039086708",
"tagged:ProjectConfiguration": "1604492097835699503",
"tagged:ProjectConfiguration": "8596726088057301092",
"tagged:TsConfig": "2264969541778889434",
"tagged:{projectRoot}/**/*": "112200405683630828",
"unrelated:ProjectConfiguration": "439515135357674343",
"unrelated:ProjectConfiguration": "11133337791644294114",
"unrelated:TsConfig": "2264969541778889434",
"unrelated:{projectRoot}/**/*": "10505120368757496776",
"{workspaceRoot}/.gitignore": "3244421341483603138",
"{workspaceRoot}/.nxignore": "3244421341483603138",
"{workspaceRoot}/nx.json": "5219582320960288192",
},
"value": "6332317845632665670",
"value": "17193008237392864712",
},
]
`);
@ -226,17 +226,17 @@ describe('native task hasher', () => {
{
"details": {
"AllExternalDependencies": "3244421341483603138",
"child:ProjectConfiguration": "7051130583729928229",
"child:ProjectConfiguration": "710102491746666394",
"child:TsConfig": "2264969541778889434",
"child:{projectRoot}/**/*": "7694964870822928111",
"parent:ProjectConfiguration": "7704699416930647320",
"parent:ProjectConfiguration": "8031122597231773116",
"parent:TsConfig": "2264969541778889434",
"parent:{projectRoot}/**/*": "15295586939211629225",
"{workspaceRoot}/.gitignore": "3244421341483603138",
"{workspaceRoot}/.nxignore": "3244421341483603138",
"{workspaceRoot}/nx.json": "5219582320960288192",
},
"value": "18412450685244791672",
"value": "4141725338792606519",
}
`);
});
@ -307,17 +307,17 @@ describe('native task hasher', () => {
{
"details": {
"AllExternalDependencies": "3244421341483603138",
"child:ProjectConfiguration": "2562552455862160288",
"child:ProjectConfiguration": "13051054958929525761",
"child:TsConfig": "2264969541778889434",
"child:{projectRoot}/**/*": "7694964870822928111",
"parent:!{projectRoot}/**/*.spec.ts": "7663204892242899157",
"parent:ProjectConfiguration": "4131510303084753861",
"parent:ProjectConfiguration": "3608670998275221195",
"parent:TsConfig": "2264969541778889434",
"{workspaceRoot}/.gitignore": "3244421341483603138",
"{workspaceRoot}/.nxignore": "3244421341483603138",
"{workspaceRoot}/nx.json": "4641558175996703359",
},
"value": "5825507912633865657",
"value": "12061654175538209437",
}
`);
});
@ -379,25 +379,25 @@ describe('native task hasher', () => {
"details": {
"AllExternalDependencies": "3244421341483603138",
"parent:!{projectRoot}/**/*.spec.ts": "7663204892242899157",
"parent:ProjectConfiguration": "8008830016795210968",
"parent:ProjectConfiguration": "16402137858974842465",
"parent:TsConfig": "2264969541778889434",
"{workspaceRoot}/.gitignore": "3244421341483603138",
"{workspaceRoot}/.nxignore": "3244421341483603138",
"{workspaceRoot}/nx.json": "4641558175996703359",
},
"value": "16919987205625802616",
"value": "1683972350273460485",
},
{
"details": {
"AllExternalDependencies": "3244421341483603138",
"parent:ProjectConfiguration": "8008830016795210968",
"parent:ProjectConfiguration": "16402137858974842465",
"parent:TsConfig": "2264969541778889434",
"parent:{projectRoot}/**/*": "15295586939211629225",
"{workspaceRoot}/.gitignore": "3244421341483603138",
"{workspaceRoot}/.nxignore": "3244421341483603138",
"{workspaceRoot}/nx.json": "4641558175996703359",
},
"value": "2732213649703581334",
"value": "2469956415584213984",
},
]
`);
@ -478,10 +478,10 @@ describe('native task hasher', () => {
"details": {
"AllExternalDependencies": "3244421341483603138",
"child:!{projectRoot}/**/*.spec.ts": "13790135045935437026",
"child:ProjectConfiguration": "11541456798478268276",
"child:ProjectConfiguration": "10085593111011845427",
"child:TsConfig": "2264969541778889434",
"env:MY_TEST_HASH_ENV": "17357374746554314488",
"parent:ProjectConfiguration": "2287392686890337925",
"parent:ProjectConfiguration": "14398811678394411425",
"parent:TsConfig": "2264969541778889434",
"parent:{projectRoot}/**/*": "15295586939211629225",
"{workspaceRoot}/.gitignore": "3244421341483603138",
@ -490,7 +490,7 @@ describe('native task hasher', () => {
"{workspaceRoot}/global2": "13625885481717016690",
"{workspaceRoot}/nx.json": "10897751101872977225",
},
"value": "1217581064022758580",
"value": "12563443797830627612",
},
]
`);
@ -539,14 +539,14 @@ describe('native task hasher', () => {
{
"details": {
"AllExternalDependencies": "3244421341483603138",
"parent:ProjectConfiguration": "4131510303084753861",
"parent:ProjectConfiguration": "3608670998275221195",
"parent:TsConfig": "8661678577354855152",
"parent:{projectRoot}/**/*": "15295586939211629225",
"{workspaceRoot}/.gitignore": "3244421341483603138",
"{workspaceRoot}/.nxignore": "3244421341483603138",
"{workspaceRoot}/nx.json": "5219582320960288192",
},
"value": "9574395623667735815",
"value": "192468752006013407",
}
`);
});
@ -616,17 +616,17 @@ describe('native task hasher', () => {
{
"details": {
"AllExternalDependencies": "3244421341483603138",
"child:ProjectConfiguration": "3898391056798628885",
"child:ProjectConfiguration": "13748859057138736105",
"child:TsConfig": "2264969541778889434",
"child:{projectRoot}/**/*": "7694964870822928111",
"parent:ProjectConfiguration": "4131510303084753861",
"parent:ProjectConfiguration": "3608670998275221195",
"parent:TsConfig": "2264969541778889434",
"parent:{projectRoot}/**/*": "15295586939211629225",
"{workspaceRoot}/.gitignore": "3244421341483603138",
"{workspaceRoot}/.nxignore": "3244421341483603138",
"{workspaceRoot}/nx.json": "5219582320960288192",
},
"value": "3140483997697830788",
"value": "571545311225175014",
}
`);
@ -640,17 +640,17 @@ describe('native task hasher', () => {
{
"details": {
"AllExternalDependencies": "3244421341483603138",
"child:ProjectConfiguration": "3898391056798628885",
"child:ProjectConfiguration": "13748859057138736105",
"child:TsConfig": "2264969541778889434",
"child:{projectRoot}/**/*": "7694964870822928111",
"parent:ProjectConfiguration": "4131510303084753861",
"parent:ProjectConfiguration": "3608670998275221195",
"parent:TsConfig": "2264969541778889434",
"parent:{projectRoot}/**/*": "15295586939211629225",
"{workspaceRoot}/.gitignore": "3244421341483603138",
"{workspaceRoot}/.nxignore": "3244421341483603138",
"{workspaceRoot}/nx.json": "5219582320960288192",
},
"value": "3140483997697830788",
"value": "571545311225175014",
}
`);
});

View File

@ -121,6 +121,7 @@ describe('TaskHasher', () => {
id: 'parent-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['parent-build'],
@ -130,6 +131,7 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -188,6 +190,7 @@ describe('TaskHasher', () => {
id: 'parent-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['child-build'],
@ -197,12 +200,14 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
'child-build': {
id: 'child-build',
target: { project: 'child', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {
@ -275,6 +280,7 @@ describe('TaskHasher', () => {
id: 'parent-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['child-build'],
@ -284,12 +290,14 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
'child-build': {
id: 'child-build',
target: { project: 'child', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {
@ -354,6 +362,7 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'test' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -365,6 +374,7 @@ describe('TaskHasher', () => {
id: 'parent-test',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
taskGraph,
{}
@ -378,6 +388,7 @@ describe('TaskHasher', () => {
id: 'parent-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
taskGraph,
{}
@ -459,12 +470,14 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'test' },
overrides: {},
outputs: [],
parallelism: true,
},
'child-test': {
id: 'child-test',
target: { project: 'child', target: 'test' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {
@ -478,6 +491,7 @@ describe('TaskHasher', () => {
id: 'parent-test',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
taskGraph,
{ MY_TEST_HASH_ENV: 'MY_TEST_HASH_ENV_VALUE' }
@ -491,6 +505,7 @@ describe('TaskHasher', () => {
id: 'child-test',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
taskGraph,
{ MY_TEST_HASH_ENV: 'MY_TEST_HASH_ENV_VALUE' }
@ -559,6 +574,7 @@ describe('TaskHasher', () => {
id: 'parent-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['child-build'],
@ -568,12 +584,14 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
'child-build': {
id: 'child-build',
target: { project: 'child', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {
@ -622,6 +640,7 @@ describe('TaskHasher', () => {
id: 'parent-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['parent:build'],
@ -631,6 +650,7 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -687,12 +707,14 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
'child-build': {
id: 'child-build',
target: { project: 'child', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {
@ -706,6 +728,7 @@ describe('TaskHasher', () => {
id: 'parent-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
taskGraph,
{}
@ -719,6 +742,7 @@ describe('TaskHasher', () => {
id: 'child-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
taskGraph,
{}
@ -763,6 +787,7 @@ describe('TaskHasher', () => {
id: 'parent-build',
overrides: {},
outputs: [],
parallelism: true,
},
{
roots: ['parent:build'],
@ -772,6 +797,7 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -834,6 +860,7 @@ describe('TaskHasher', () => {
id: 'app-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['app-build'],
@ -843,6 +870,7 @@ describe('TaskHasher', () => {
target: { project: 'app', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -902,6 +930,7 @@ describe('TaskHasher', () => {
id: 'app-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['app-build'],
@ -911,6 +940,7 @@ describe('TaskHasher', () => {
target: { project: 'app', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -961,6 +991,7 @@ describe('TaskHasher', () => {
id: 'app-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['app-build'],
@ -970,6 +1001,7 @@ describe('TaskHasher', () => {
target: { project: 'app', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -1051,12 +1083,14 @@ describe('TaskHasher', () => {
target: { project: 'a', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
'b-build': {
id: 'b-build',
target: { project: 'b', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -1071,6 +1105,7 @@ describe('TaskHasher', () => {
target: { project: 'a', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
taskGraph,
{}
@ -1081,6 +1116,7 @@ describe('TaskHasher', () => {
target: { project: 'b', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
taskGraph,
{}
@ -1092,6 +1128,7 @@ describe('TaskHasher', () => {
target: { project: 'b', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
taskGraph,
{}
@ -1102,6 +1139,7 @@ describe('TaskHasher', () => {
target: { project: 'a', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
taskGraph,
{}
@ -1201,6 +1239,7 @@ describe('TaskHasher', () => {
id: 'app-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['app-build'],
@ -1210,6 +1249,7 @@ describe('TaskHasher', () => {
target: { project: 'app', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -1418,6 +1458,7 @@ describe('TaskHasher', () => {
id: 'app-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['app-build'],
@ -1427,6 +1468,7 @@ describe('TaskHasher', () => {
target: { project: 'app', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -1502,6 +1544,7 @@ describe('TaskHasher', () => {
id: 'app-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['app-build'],
@ -1511,6 +1554,7 @@ describe('TaskHasher', () => {
target: { project: 'app', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -1584,6 +1628,7 @@ describe('TaskHasher', () => {
id: 'app-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['app-build'],
@ -1593,6 +1638,7 @@ describe('TaskHasher', () => {
target: { project: 'app', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {},
@ -1710,6 +1756,7 @@ describe('TaskHasher', () => {
id: 'parent-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['grandchild-build'],
@ -1719,18 +1766,21 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'build' },
overrides: {},
outputs: ['dist/libs/libs/parent'],
parallelism: true,
},
'child-build': {
id: 'child-build',
target: { project: 'child', target: 'build' },
overrides: {},
outputs: ['dist/libs/libs/child'],
parallelism: true,
},
'grandchild-build': {
id: 'grandchild-build',
target: { project: 'grandchild', target: 'build' },
overrides: {},
outputs: ['dist/libs/libs/grandchild'],
parallelism: true,
},
},
dependencies: {
@ -1850,6 +1900,7 @@ describe('TaskHasher', () => {
id: 'parent-build',
overrides: { prop: 'prop-value' },
outputs: [],
parallelism: true,
},
{
roots: ['grandchild-build'],
@ -1859,18 +1910,21 @@ describe('TaskHasher', () => {
target: { project: 'parent', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
'child-build': {
id: 'child-build',
target: { project: 'child', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
'grandchild-build': {
id: 'grandchild-build',
target: { project: 'grandchild', target: 'build' },
overrides: {},
outputs: [],
parallelism: true,
},
},
dependencies: {

View File

@ -185,6 +185,7 @@ export interface Target {
outputs?: Array<string>
options?: string
configurations?: string
parallelism?: boolean
}
export interface Task {

View File

@ -16,6 +16,7 @@ pub struct Target {
pub outputs: Option<Vec<String>>,
pub options: Option<String>,
pub configurations: Option<String>,
pub parallelism: Option<bool>,
}
#[napi(object)]

View File

@ -21,12 +21,13 @@ pub fn hash_project_config(
.sorted_by(|a, b| a.0.cmp(b.0))
.map(|(k, v)| {
format!(
"{}{}{}{}{}",
"{}{}{}{}{}{}",
k,
v.executor.as_deref().unwrap_or_default(),
v.outputs.as_deref().unwrap_or_default().concat(),
v.options.as_deref().unwrap_or_default(),
v.configurations.as_deref().unwrap_or_default(),
v.parallelism.unwrap_or_default()
)
})
.collect::<Vec<_>>()
@ -144,7 +145,7 @@ mod tests {
assert_eq!(nx_project_hash.unwrap(), "3244421341483603138");
let js_project_hash = hash_project_config("js", &projects).unwrap();
assert_eq!(js_project_hash, "18342193044952101577");
assert_eq!(js_project_hash, "13565578942842640362");
let js_unsorted = hash_project_config("js-unsorted", &projects);
assert_eq!(js_unsorted.unwrap(), js_project_hash);

View File

@ -23,6 +23,7 @@ export function transformProjectGraphForRust(
outputs: targetConfig.outputs,
options: JSON.stringify(targetConfig.options),
configurations: JSON.stringify(targetConfig.configurations),
parallelism: targetConfig.parallelism,
};
}
nodes[projectName] = {

View File

@ -1520,6 +1520,7 @@ describe('project-configuration-utils', () => {
"options": {
"command": "echo libs/project",
},
"parallelism": true,
}
`);
});
@ -1676,6 +1677,7 @@ describe('project-configuration-utils', () => {
"options": {
"command": "echo a @ libs/a",
},
"parallelism": true,
}
`);
});

View File

@ -1122,5 +1122,7 @@ export function normalizeTarget(
);
}
target.parallelism ??= true;
return target;
}

View File

@ -107,6 +107,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: { a: 123 },
projectRoot: 'app1-root',
parallelism: true,
},
},
dependencies: {
@ -137,6 +138,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: { a: 123 },
projectRoot: 'app1-root',
parallelism: true,
},
'lib1:test': {
id: 'lib1:test',
@ -147,6 +149,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: { a: 123 },
projectRoot: 'lib1-root',
parallelism: true,
},
},
dependencies: {
@ -286,6 +289,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: {},
projectRoot: 'lib1-root',
parallelism: true,
},
'lib2:compile': {
id: 'lib2:compile',
@ -298,6 +302,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'lib2-root',
parallelism: true,
},
},
dependencies: {
@ -328,6 +333,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: {},
projectRoot: 'app1-root',
parallelism: true,
},
'lib1:compile:libDefault': {
id: 'lib1:compile:libDefault',
@ -341,6 +347,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'lib1-root',
parallelism: true,
},
'lib2:compile:ci': {
id: 'lib2:compile:ci',
@ -354,6 +361,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'lib2-root',
parallelism: true,
},
},
dependencies: {
@ -454,6 +462,7 @@ describe('createTaskGraph', () => {
project: 'app1',
target: 'compile',
},
parallelism: true,
},
'lib3:compile': {
id: 'lib3:compile',
@ -466,6 +475,7 @@ describe('createTaskGraph', () => {
project: 'lib3',
target: 'compile',
},
parallelism: true,
},
},
});
@ -494,6 +504,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: { a: '--value=app1-root' },
projectRoot: 'app1-root',
parallelism: true,
},
},
dependencies: {
@ -525,6 +536,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: { a: '--base-href=/app1-root${deploymentId}' },
projectRoot: 'app1-root',
parallelism: true,
},
},
dependencies: {
@ -639,6 +651,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: { myFlag: 'flag value' },
projectRoot: 'app1-root',
parallelism: true,
},
'app1:precompile': {
id: 'app1:precompile',
@ -649,6 +662,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: { myFlag: 'flag value' },
projectRoot: 'app1-root',
parallelism: true,
},
'lib1:compile': {
id: 'lib1:compile',
@ -659,6 +673,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: { myFlag: 'flag value' },
projectRoot: 'lib1-root',
parallelism: true,
},
'lib2:compile': {
id: 'lib2:compile',
@ -669,6 +684,7 @@ describe('createTaskGraph', () => {
outputs: [],
overrides: { __overrides_unparsed__: [] },
projectRoot: 'lib2-root',
parallelism: true,
},
},
dependencies: {
@ -706,6 +722,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'app1:precompile': {
id: 'app1:precompile',
@ -718,6 +735,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'app1:precompile2': {
id: 'app1:precompile2',
@ -730,6 +748,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'lib1:compile': {
id: 'lib1:compile',
@ -742,6 +761,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'lib1-root',
parallelism: true,
},
},
dependencies: {
@ -779,6 +799,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'app1:precompile': {
id: 'app1:precompile',
@ -791,6 +812,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'app1:precompile2': {
id: 'app1:precompile2',
@ -803,6 +825,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'lib1:compile': {
id: 'lib1:compile',
@ -815,6 +838,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'lib1-root',
parallelism: true,
},
},
dependencies: {
@ -921,6 +945,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'lib1:compile': {
id: 'lib1:compile',
@ -933,6 +958,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'lib1-root',
parallelism: true,
},
'lib2:compile': {
id: 'lib2:compile',
@ -945,6 +971,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'lib2-root',
parallelism: true,
},
'lib3:compile': {
id: 'lib3:compile',
@ -957,6 +984,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'lib3-root',
parallelism: true,
},
},
dependencies: {
@ -1080,6 +1108,7 @@ describe('createTaskGraph', () => {
projectRoot: 'infra1-root',
outputs: [],
overrides: { myFlag: 'flag value' },
parallelism: true,
},
'app2:compile': {
id: 'app2:compile',
@ -1087,6 +1116,7 @@ describe('createTaskGraph', () => {
projectRoot: 'app2-root',
outputs: [],
overrides: { __overrides_unparsed__: [] },
parallelism: true,
},
'coreInfra:apply': {
id: 'coreInfra:apply',
@ -1094,6 +1124,7 @@ describe('createTaskGraph', () => {
projectRoot: 'infra3-root',
outputs: [],
overrides: { myFlag: 'flag value' },
parallelism: true,
},
'app1:compile': {
id: 'app1:compile',
@ -1101,6 +1132,7 @@ describe('createTaskGraph', () => {
projectRoot: 'app1-root',
outputs: [],
overrides: { __overrides_unparsed__: [] },
parallelism: true,
},
'infra2:apply': {
id: 'infra2:apply',
@ -1108,6 +1140,7 @@ describe('createTaskGraph', () => {
projectRoot: 'infra2-root',
outputs: [],
overrides: { myFlag: 'flag value' },
parallelism: true,
},
},
dependencies: {
@ -1174,6 +1207,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'app1:test': {
id: 'app1:test',
@ -1186,6 +1220,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
},
dependencies: {
@ -1267,6 +1302,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'app3:compile': {
id: 'app3:compile',
@ -1279,6 +1315,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app3-root',
parallelism: true,
},
},
dependencies: {
@ -1357,6 +1394,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'app3:compile': {
id: 'app3:compile',
@ -1369,6 +1407,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app3-root',
parallelism: true,
},
},
dependencies: {
@ -1445,6 +1484,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
},
dependencies: {
@ -1478,6 +1518,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app1-root',
parallelism: true,
},
'app2:compile': {
id: 'app2:compile',
@ -1490,6 +1531,7 @@ describe('createTaskGraph', () => {
__overrides_unparsed__: [],
},
projectRoot: 'app2-root',
parallelism: true,
},
},
dependencies: {

View File

@ -35,7 +35,7 @@ export class ProcessTasks {
configuration: string,
overrides: Object,
excludeTaskDependencies: boolean
) {
): string[] {
for (const projectName of projectNames) {
for (const target of targets) {
const project = this.projectGraph.nodes[projectName];
@ -319,6 +319,7 @@ export class ProcessTasks {
interpolatedOverrides
),
cache: project.data.targets[target].cache,
parallelism: project.data.targets[target].parallelism ?? true,
};
}

View File

@ -5,7 +5,7 @@ import { DependencyType, ProjectGraph } from '../config/project-graph';
import * as nxJsonUtils from '../config/nx-json';
import * as executorUtils from '../command-line/run/executor-utils';
function createMockTask(id: string): Task {
function createMockTask(id: string, parallelism: boolean = true): Task {
const [project, target] = id.split(':');
return {
id,
@ -15,6 +15,7 @@ function createMockTask(id: string): Task {
},
outputs: [],
overrides: {},
parallelism,
};
}
@ -411,4 +412,367 @@ describe('TasksSchedule', () => {
});
});
});
describe('tasks with parallelism false', () => {
describe('dependent tasks', () => {
let taskSchedule: TasksSchedule;
let taskGraph: TaskGraph;
let app1Build: Task;
let app2Build: Task;
let lib1Build: Task;
let lifeCycle: any;
beforeEach(() => {
// app1 depends on lib1
// app2 does not depend on anything
// lib1 does not depend on anything
// all tasks have parallelism set to false
app1Build = createMockTask('app1:build', false);
app2Build = createMockTask('app2:build', false);
lib1Build = createMockTask('lib1:build', false);
taskGraph = {
tasks: {
'app1:build': app1Build,
'app2:build': app2Build,
'lib1:build': lib1Build,
},
dependencies: {
'app1:build': ['lib1:build'],
'app2:build': [],
'lib1:build': [],
},
roots: ['lib1:build', 'app2:build'],
};
jest.spyOn(nxJsonUtils, 'readNxJson').mockReturnValue({});
jest.spyOn(executorUtils, 'getExecutorInformation').mockReturnValue({
schema: {
version: 2,
properties: {},
},
implementationFactory: jest.fn(),
batchImplementationFactory: jest.fn(),
isNgCompat: true,
isNxExecutor: true,
});
const projectGraph: ProjectGraph = {
nodes: {
app1: {
data: {
root: 'app1',
targets: {
build: {
executor: 'awesome-executors:build',
},
},
},
name: 'app1',
type: 'app',
},
app2: {
name: 'app2',
type: 'app',
data: {
root: 'app2',
targets: {
build: {
executor: 'awesome-executors:app2-build',
},
},
},
},
lib1: {
name: 'lib1',
type: 'lib',
data: {
root: 'lib1',
targets: {
build: {
executor: 'awesome-executors:build',
},
},
},
},
} as any,
dependencies: {
app1: [
{
source: 'app1',
target: 'lib1',
type: DependencyType.static,
},
],
app2: [
{
source: 'app2',
target: 'lib1',
type: DependencyType.static,
},
],
},
externalNodes: {},
version: '5',
};
lifeCycle = {
startTask: jest.fn(),
endTask: jest.fn(),
scheduleTask: jest.fn(),
};
taskSchedule = new TasksSchedule(projectGraph, taskGraph, {
lifeCycle,
});
});
describe('Without Batch Mode', () => {
let original;
beforeEach(() => {
original = process.env['NX_BATCH_MODE'];
process.env['NX_BATCH_MODE'] = 'false';
});
afterEach(() => {
process.env['NX_BATCH_MODE'] = original;
});
it('should begin with no scheduled tasks', () => {
expect(taskSchedule.nextBatch()).toBeNull();
expect(taskSchedule.nextTask()).toBeNull();
});
it('should schedule root tasks first', async () => {
// app1 depends on lib1, app2 has no dependencies
await taskSchedule.scheduleNextTasks();
expect(taskSchedule.nextTask()).toEqual(lib1Build);
// since lib1 is not parallel, app2 should not be scheduled even though it has no dependencies
expect(taskSchedule.nextTask()).toBeNull();
taskSchedule.complete([lib1Build.id]);
await taskSchedule.scheduleNextTasks();
expect(taskSchedule.nextTask()).toEqual(app1Build);
expect(taskSchedule.nextTask()).toBeNull(); // app2 should not be scheduled since app1 is not parallel and not completed
taskSchedule.complete([app1Build.id]);
await taskSchedule.scheduleNextTasks();
expect(taskSchedule.nextTask()).toEqual(app2Build);
taskSchedule.complete([app2Build.id]);
expect(taskSchedule.hasTasks()).toEqual(false);
});
it('should not schedule batches', async () => {
await taskSchedule.scheduleNextTasks();
expect(taskSchedule.nextTask()).not.toBeNull();
expect(taskSchedule.nextBatch()).toBeNull();
});
});
describe('With Batch Mode', () => {
let original;
beforeEach(() => {
original = process.env['NX_BATCH_MODE'];
process.env['NX_BATCH_MODE'] = 'true';
});
afterEach(() => {
process.env['NX_BATCH_MODE'] = original;
});
it('should not schedule batches of tasks by different executors if task has parallelism false', async () => {
await taskSchedule.scheduleNextTasks();
// since all tasks have parallelism false, they should not be batched
expect(taskSchedule.nextTask()).toEqual(lib1Build);
expect(taskSchedule.nextBatch()).toBeNull();
});
});
});
describe('non-dependent tasks', () => {
let taskSchedule: TasksSchedule;
let taskGraph: TaskGraph;
let app1Test: Task;
let app2Test: Task;
let lib1Test: Task;
let lifeCycle: any;
beforeEach(() => {
// app1, app2, and lib1 do not depend on each other
// all tasks have parallelism set to false
app1Test = createMockTask('app1:test', false);
app2Test = createMockTask('app2:test', false);
lib1Test = createMockTask('lib1:test', false);
taskGraph = {
tasks: {
'app1:test': app1Test,
'app2:test': app2Test,
'lib1:test': lib1Test,
},
dependencies: {
'app1:test': [],
'app2:test': [],
'lib1:test': [],
},
roots: ['app1:test', 'app2:test', 'lib1:test'],
};
jest.spyOn(nxJsonUtils, 'readNxJson').mockReturnValue({});
jest.spyOn(executorUtils, 'getExecutorInformation').mockReturnValue({
schema: {
version: 2,
properties: {},
},
implementationFactory: jest.fn(),
batchImplementationFactory: jest.fn(),
isNgCompat: true,
isNxExecutor: true,
});
const projectGraph: ProjectGraph = {
nodes: {
app1: {
data: {
root: 'app1',
targets: {
test: {
executor: 'awesome-executors:test',
parallelism: false,
},
},
},
name: 'app1',
type: 'app',
},
app2: {
name: 'app2',
type: 'app',
data: {
root: 'app2',
targets: {
test: {
executor: 'awesome-executors:app2-test',
parallelism: false,
},
},
},
},
lib1: {
name: 'lib1',
type: 'lib',
data: {
root: 'lib1',
targets: {
test: {
executor: 'awesome-executors:test',
},
},
},
},
} as any,
dependencies: {
app1: [
{
source: 'app1',
target: 'lib1',
type: DependencyType.static,
},
],
app2: [
{
source: 'app2',
target: 'lib1',
type: DependencyType.static,
},
],
},
externalNodes: {},
version: '5',
};
lifeCycle = {
startTask: jest.fn(),
endTask: jest.fn(),
scheduleTask: jest.fn(),
};
taskSchedule = new TasksSchedule(projectGraph, taskGraph, {
lifeCycle,
});
});
describe('Without Batch Mode', () => {
let original;
beforeEach(() => {
original = process.env['NX_BATCH_MODE'];
process.env['NX_BATCH_MODE'] = 'false';
});
afterEach(() => {
process.env['NX_BATCH_MODE'] = original;
});
it('should begin with no scheduled tasks', () => {
expect(taskSchedule.nextBatch()).toBeNull();
expect(taskSchedule.nextTask()).toBeNull();
});
it('should schedule root tasks in topological order', async () => {
await taskSchedule.scheduleNextTasks();
expect(taskSchedule.nextTask()).toEqual(app1Test);
let nextTask = taskSchedule.nextTask();
expect(nextTask).not.toEqual(app2Test); // app2 should not be scheduled since app1 is not parallel and not completed
expect(nextTask).not.toEqual(lib1Test); // lib1 should not be scheduled since app1 is not parallel and not completed
expect(nextTask).toBeNull();
taskSchedule.complete([app1Test.id]);
await taskSchedule.scheduleNextTasks();
nextTask = taskSchedule.nextTask();
expect(nextTask).toEqual(app2Test); // app2 should be scheduled since app1 is completed now
nextTask = taskSchedule.nextTask();
expect(nextTask).not.toEqual(lib1Test); // lib1 should not be scheduled since app2 is not parallel and not completed
expect(nextTask).toBeNull();
taskSchedule.complete([app1Test.id]); // this should not do anything since app1 is already completed
await taskSchedule.scheduleNextTasks();
nextTask = taskSchedule.nextTask();
expect(nextTask).not.toEqual(lib1Test); // lib1 should not be scheduled since app2 is not parallel and not completed
expect(nextTask).toBeNull();
taskSchedule.complete([app2Test.id]);
await taskSchedule.scheduleNextTasks();
expect(taskSchedule.nextTask()).toEqual(lib1Test); // lib1 should be scheduled since app2 is completed now
taskSchedule.complete([lib1Test.id]);
expect(taskSchedule.hasTasks()).toEqual(false);
});
it('should not schedule batches', async () => {
await taskSchedule.scheduleNextTasks();
expect(taskSchedule.nextTask()).not.toBeNull();
expect(taskSchedule.nextBatch()).toBeNull();
});
});
describe('With Batch Mode', () => {
let original;
beforeEach(() => {
original = process.env['NX_BATCH_MODE'];
process.env['NX_BATCH_MODE'] = 'true';
});
afterEach(() => {
process.env['NX_BATCH_MODE'] = original;
});
it('should not schedule batches of tasks by different executors if task have parallelism false', async () => {
await taskSchedule.scheduleNextTasks();
// app1, app2, and lib1 are not parallel, so they should not be batched
expect(taskSchedule.nextTask()).toEqual(app1Test);
expect(taskSchedule.nextBatch()).toBeNull();
});
});
});
});
});

View File

@ -21,6 +21,7 @@ export class TasksSchedule {
private reverseProjectGraph = reverse(this.projectGraph);
private scheduledBatches: Batch[] = [];
private scheduledTasks: string[] = [];
private runningTasks = new Set<string>();
private completedTasks = new Set<string>();
private scheduleRequestsExecutionChain = Promise.resolve();
@ -48,6 +49,7 @@ export class TasksSchedule {
public complete(taskIds: string[]) {
for (const taskId of taskIds) {
this.completedTasks.add(taskId);
this.runningTasks.delete(taskId);
}
this.notScheduledTaskGraph = removeTasksFromTaskGraph(
this.notScheduledTaskGraph,
@ -117,6 +119,7 @@ export class TasksSchedule {
.length
);
});
this.runningTasks.add(taskId);
}
private async scheduleBatches() {
@ -146,8 +149,8 @@ export class TasksSchedule {
task: Task,
rootExecutorName: string,
isRoot: boolean
) {
if (!this.canBatchTaskBeScheduled(task.id, batches[rootExecutorName])) {
): Promise<void> {
if (!this.canBatchTaskBeScheduled(task, batches[rootExecutorName])) {
return;
}
@ -191,18 +194,45 @@ export class TasksSchedule {
}
private canBatchTaskBeScheduled(
taskId: string,
task: Task,
batchTaskGraph: TaskGraph | undefined
): boolean {
// task self needs to have parallelism true
// all deps have either completed or belong to the same batch
return this.taskGraph.dependencies[taskId].every(
(id) => this.completedTasks.has(id) || !!batchTaskGraph?.tasks[id]
return (
task.parallelism === true &&
this.taskGraph.dependencies[task.id].every(
(id) => this.completedTasks.has(id) || !!batchTaskGraph?.tasks[id]
)
);
}
private canBeScheduled(taskId: string) {
return this.taskGraph.dependencies[taskId].every((id) =>
this.completedTasks.has(id)
private canBeScheduled(taskId: string): boolean {
const hasDependenciesCompleted = this.taskGraph.dependencies[taskId].every(
(id) => this.completedTasks.has(id)
);
// if dependencies have not completed, cannot schedule
if (!hasDependenciesCompleted) {
return false;
}
// if there are no running tasks, can schedule anything
if (this.runningTasks.size === 0) {
return true;
}
const runningTasksNotSupportParallelism = Array.from(
this.runningTasks
).some((taskId) => {
return this.taskGraph.tasks[taskId].parallelism === false;
});
if (runningTasksNotSupportParallelism) {
// if any running tasks do not support parallelism, no other tasks can be scheduled
return false;
} else {
// if all running tasks support parallelism, can only schedule task with parallelism
return this.taskGraph.tasks[taskId].parallelism === true;
}
}
}

View File

@ -91,6 +91,7 @@ describe('@nx/playwright/plugin', () => {
"outputs": [
"{projectRoot}/test-results",
],
"parallelism": false,
},
"e2e-ci": {
"cache": true,
@ -123,6 +124,7 @@ describe('@nx/playwright/plugin', () => {
"outputs": [
"{projectRoot}/test-results",
],
"parallelism": false,
},
},
},
@ -200,6 +202,7 @@ describe('@nx/playwright/plugin', () => {
"{projectRoot}/test-results/html",
"{projectRoot}/test-results",
],
"parallelism": false,
},
"e2e-ci": {
"cache": true,
@ -235,6 +238,7 @@ describe('@nx/playwright/plugin', () => {
"{projectRoot}/test-results/html",
"{projectRoot}/test-results",
],
"parallelism": false,
},
},
},
@ -323,6 +327,7 @@ describe('@nx/playwright/plugin', () => {
"outputs": [
"{projectRoot}/test-results",
],
"parallelism": false,
}
`);
expect(targets['e2e-ci--tests/run-me.spec.ts']).toMatchInlineSnapshot(`
@ -358,6 +363,7 @@ describe('@nx/playwright/plugin', () => {
"outputs": [
"{projectRoot}/test-results",
],
"parallelism": false,
}
`);
expect(targets['e2e-ci--tests/run-me-2.spec.ts']).toMatchInlineSnapshot(`
@ -393,6 +399,7 @@ describe('@nx/playwright/plugin', () => {
"outputs": [
"{projectRoot}/test-results",
],
"parallelism": false,
}
`);
expect(targets['e2e-ci--tests/skip-me.spec.ts']).not.toBeDefined();

View File

@ -162,6 +162,7 @@ async function buildPlaywrightTargets(
options: {
cwd: '{projectRoot}',
},
parallelism: false,
metadata: {
technologies: ['playwright'],
description: 'Runs Playwright Tests',
@ -257,6 +258,7 @@ async function buildPlaywrightTargets(
inputs: ciBaseTargetConfig.inputs,
outputs: ciBaseTargetConfig.outputs,
dependsOn,
parallelism: false,
metadata: {
technologies: ['playwright'],
description: 'Runs Playwright Tests in CI',

View File

@ -14,7 +14,8 @@ describe('<%=propertyName%>Hasher', () => {
target: 'target'
},
overrides: {},
outputs: []
outputs: [],
parallelism: true
}, {
hasher: mockHasher
} as unknown as HasherContext)