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 - [metadata](../../devkit/documents/TargetConfiguration#metadata): TargetMetadata
- [options](../../devkit/documents/TargetConfiguration#options): T - [options](../../devkit/documents/TargetConfiguration#options): T
- [outputs](../../devkit/documents/TargetConfiguration#outputs): string[] - [outputs](../../devkit/documents/TargetConfiguration#outputs): string[]
- [parallelism](../../devkit/documents/TargetConfiguration#parallelism): boolean
## Properties ## 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 List of the target's outputs. The outputs will be cached by the Nx computation
caching engine. 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 - [id](../../devkit/documents/Task#id): string
- [outputs](../../devkit/documents/Task#outputs): string[] - [outputs](../../devkit/documents/Task#outputs): string[]
- [overrides](../../devkit/documents/Task#overrides): any - [overrides](../../devkit/documents/Task#overrides): any
- [parallelism](../../devkit/documents/Task#parallelism): boolean
- [projectRoot](../../devkit/documents/Task#projectroot): string - [projectRoot](../../devkit/documents/Task#projectroot): string
- [startTime](../../devkit/documents/Task#starttime): number - [startTime](../../devkit/documents/Task#starttime): number
- [target](../../devkit/documents/Task#target): Object - [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 ### projectRoot
`Optional` **projectRoot**: `string` `Optional` **projectRoot**: `string`

View File

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

View File

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

View File

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

View File

@ -4,6 +4,27 @@
"title": "JSON schema for Nx projects", "title": "JSON schema for Nx projects",
"type": "object", "type": "object",
"properties": { "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": { "namedInputs": {
"type": "object", "type": "object",
"description": "Named inputs used by inputs defined in targets", "description": "Named inputs used by inputs defined in targets",
@ -112,6 +133,11 @@
"cache": { "cache": {
"type": "boolean", "type": "boolean",
"description": "Specifies if the given target should be cacheable" "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. * Determines if a given task should be cacheable.
*/ */
cache?: boolean; 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", "executor": "@nx/js:release-publish",
"options": {}, "options": {},
"parallelism": true,
}, },
}, },
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ export class ProcessTasks {
configuration: string, configuration: string,
overrides: Object, overrides: Object,
excludeTaskDependencies: boolean excludeTaskDependencies: boolean
) { ): string[] {
for (const projectName of projectNames) { for (const projectName of projectNames) {
for (const target of targets) { for (const target of targets) {
const project = this.projectGraph.nodes[projectName]; const project = this.projectGraph.nodes[projectName];
@ -319,6 +319,7 @@ export class ProcessTasks {
interpolatedOverrides interpolatedOverrides
), ),
cache: project.data.targets[target].cache, 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 nxJsonUtils from '../config/nx-json';
import * as executorUtils from '../command-line/run/executor-utils'; 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(':'); const [project, target] = id.split(':');
return { return {
id, id,
@ -15,6 +15,7 @@ function createMockTask(id: string): Task {
}, },
outputs: [], outputs: [],
overrides: {}, 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 reverseProjectGraph = reverse(this.projectGraph);
private scheduledBatches: Batch[] = []; private scheduledBatches: Batch[] = [];
private scheduledTasks: string[] = []; private scheduledTasks: string[] = [];
private runningTasks = new Set<string>();
private completedTasks = new Set<string>(); private completedTasks = new Set<string>();
private scheduleRequestsExecutionChain = Promise.resolve(); private scheduleRequestsExecutionChain = Promise.resolve();
@ -48,6 +49,7 @@ export class TasksSchedule {
public complete(taskIds: string[]) { public complete(taskIds: string[]) {
for (const taskId of taskIds) { for (const taskId of taskIds) {
this.completedTasks.add(taskId); this.completedTasks.add(taskId);
this.runningTasks.delete(taskId);
} }
this.notScheduledTaskGraph = removeTasksFromTaskGraph( this.notScheduledTaskGraph = removeTasksFromTaskGraph(
this.notScheduledTaskGraph, this.notScheduledTaskGraph,
@ -117,6 +119,7 @@ export class TasksSchedule {
.length .length
); );
}); });
this.runningTasks.add(taskId);
} }
private async scheduleBatches() { private async scheduleBatches() {
@ -146,8 +149,8 @@ export class TasksSchedule {
task: Task, task: Task,
rootExecutorName: string, rootExecutorName: string,
isRoot: boolean isRoot: boolean
) { ): Promise<void> {
if (!this.canBatchTaskBeScheduled(task.id, batches[rootExecutorName])) { if (!this.canBatchTaskBeScheduled(task, batches[rootExecutorName])) {
return; return;
} }
@ -191,18 +194,45 @@ export class TasksSchedule {
} }
private canBatchTaskBeScheduled( private canBatchTaskBeScheduled(
taskId: string, task: Task,
batchTaskGraph: TaskGraph | undefined batchTaskGraph: TaskGraph | undefined
): boolean { ): boolean {
// task self needs to have parallelism true
// all deps have either completed or belong to the same batch // all deps have either completed or belong to the same batch
return this.taskGraph.dependencies[taskId].every( return (
task.parallelism === true &&
this.taskGraph.dependencies[task.id].every(
(id) => this.completedTasks.has(id) || !!batchTaskGraph?.tasks[id] (id) => this.completedTasks.has(id) || !!batchTaskGraph?.tasks[id]
)
); );
} }
private canBeScheduled(taskId: string) { private canBeScheduled(taskId: string): boolean {
return this.taskGraph.dependencies[taskId].every((id) => const hasDependenciesCompleted = this.taskGraph.dependencies[taskId].every(
this.completedTasks.has(id) (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": [ "outputs": [
"{projectRoot}/test-results", "{projectRoot}/test-results",
], ],
"parallelism": false,
}, },
"e2e-ci": { "e2e-ci": {
"cache": true, "cache": true,
@ -123,6 +124,7 @@ describe('@nx/playwright/plugin', () => {
"outputs": [ "outputs": [
"{projectRoot}/test-results", "{projectRoot}/test-results",
], ],
"parallelism": false,
}, },
}, },
}, },
@ -200,6 +202,7 @@ describe('@nx/playwright/plugin', () => {
"{projectRoot}/test-results/html", "{projectRoot}/test-results/html",
"{projectRoot}/test-results", "{projectRoot}/test-results",
], ],
"parallelism": false,
}, },
"e2e-ci": { "e2e-ci": {
"cache": true, "cache": true,
@ -235,6 +238,7 @@ describe('@nx/playwright/plugin', () => {
"{projectRoot}/test-results/html", "{projectRoot}/test-results/html",
"{projectRoot}/test-results", "{projectRoot}/test-results",
], ],
"parallelism": false,
}, },
}, },
}, },
@ -323,6 +327,7 @@ describe('@nx/playwright/plugin', () => {
"outputs": [ "outputs": [
"{projectRoot}/test-results", "{projectRoot}/test-results",
], ],
"parallelism": false,
} }
`); `);
expect(targets['e2e-ci--tests/run-me.spec.ts']).toMatchInlineSnapshot(` expect(targets['e2e-ci--tests/run-me.spec.ts']).toMatchInlineSnapshot(`
@ -358,6 +363,7 @@ describe('@nx/playwright/plugin', () => {
"outputs": [ "outputs": [
"{projectRoot}/test-results", "{projectRoot}/test-results",
], ],
"parallelism": false,
} }
`); `);
expect(targets['e2e-ci--tests/run-me-2.spec.ts']).toMatchInlineSnapshot(` expect(targets['e2e-ci--tests/run-me-2.spec.ts']).toMatchInlineSnapshot(`
@ -393,6 +399,7 @@ describe('@nx/playwright/plugin', () => {
"outputs": [ "outputs": [
"{projectRoot}/test-results", "{projectRoot}/test-results",
], ],
"parallelism": false,
} }
`); `);
expect(targets['e2e-ci--tests/skip-me.spec.ts']).not.toBeDefined(); expect(targets['e2e-ci--tests/skip-me.spec.ts']).not.toBeDefined();

View File

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

View File

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