Skip to content

Commit

Permalink
fix(core): don't cache full dependency configuration when expanding t…
Browse files Browse the repository at this point in the history
…arget name (#27308)

<!-- 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
There is a cache that maps target pattern -> dependency configuration,
but the dependency configuration contains more than just the target
name. This results in retrieving cached dependency configurations that
contain the wrong projects to run.

## Expected Behavior
The cache maps target pattern -> target names, which we can then map to
the full dependency configuration with the extra info we know.

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

Fixes #

(cherry picked from commit 26f46c2)
  • Loading branch information
AgentEnder authored and FrozenPandaz committed Aug 7, 2024
1 parent aba87f8 commit a4ea9bc
Show file tree
Hide file tree
Showing 5 changed files with 547 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1878,6 +1878,46 @@ describe('project-configuration-utils', () => {
// other source map entries should be left unchanged
expect(sourceMap['targets']).toEqual(['dummy', 'dummy.ts']);
});

it('should not overwrite dependsOn', () => {
const sourceMap: Record<string, SourceInformation> = {
targets: ['dummy', 'dummy.ts'],
'targets.build': ['dummy', 'dummy.ts'],
'targets.build.options': ['dummy', 'dummy.ts'],
'targets.build.options.command': ['dummy', 'dummy.ts'],
'targets.build.options.cwd': ['project.json', 'nx/project-json'],
'targets.build.dependsOn': ['project.json', 'nx/project-json'],
};
const result = mergeTargetDefaultWithTargetDefinition(
'build',
{
name: 'myapp',
root: 'apps/myapp',
targets: {
build: {
executor: 'nx:run-commands',
options: {
command: 'echo',
cwd: '{workspaceRoot}',
},
dependsOn: [],
},
},
},
{
options: {
command: 'tsc',
cwd: 'apps/myapp',
},
dependsOn: ['^build'],
},
sourceMap
);

// Command was defined by a core plugin so it should
// not be replaced by target default
expect(result.dependsOn).toEqual([]);
});
});
});

Expand Down
314 changes: 313 additions & 1 deletion packages/nx/src/tasks-runner/create-task-graph.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { DependencyType, ProjectGraph } from '../config/project-graph';
import {
DependencyType,
ProjectGraph,
ProjectGraphProjectNode,
} from '../config/project-graph';
import { ProjectConfiguration } from '../config/workspace-json-project-json';
import { createTaskGraph } from './create-task-graph';

describe('createTaskGraph', () => {
Expand Down Expand Up @@ -1677,4 +1682,311 @@ describe('createTaskGraph', () => {
'lib3:build',
]);
});

it('should handle multiple dependsOn task groups', () => {
const taskGraph = createTaskGraph(
{
nodes: {
a: {
name: 'a',
type: 'app',
data: {
root: 'a-root',
targets: {
deploy: {
executor: 'nx:run-commands',
dependsOn: [{ target: 'build' }],
},
build: {
executor: 'nx:run-commands',
dependsOn: [{ target: 'compile' }],
},
compile: {
executor: 'nx:run-commands',
dependsOn: ['^compile'],
},
},
},
},
b: {
name: 'b',
type: 'lib',
data: {
root: 'b-root',
targets: {
deploy: {
executor: 'nx:run-commands',
dependsOn: [{ target: 'build' }],
},
build: {
executor: 'nx:run-commands',
dependsOn: [{ target: 'compile' }],
},
compile: {
executor: 'nx:run-commands',
dependsOn: ['^compile'],
},
},
},
},
c: {
name: 'c',
type: 'lib',
data: {
root: 'c-root',
targets: {
deploy: {
executor: 'nx:run-commands',
dependsOn: [{ target: 'build' }],
},
build: {
executor: 'nx:run-commands',
dependsOn: [{ target: 'compile' }],
},
compile: {
executor: 'nx:run-commands',
dependsOn: ['^compile'],
},
},
},
},
d: {
name: 'd',
type: 'lib',
data: {
root: 'd-root',
targets: {
deploy: {
executor: 'nx:run-commands',
dependsOn: [{ target: 'build' }],
},
build: {
executor: 'nx:run-commands',
dependsOn: [{ target: 'compile' }],
},
compile: {
executor: 'nx:run-commands',
dependsOn: ['^compile'],
},
},
},
},
},
dependencies: {
a: [],
b: [
{
source: 'b',
target: 'd',
type: 'static',
},
],
c: [
{
source: 'c',
target: 'd',
type: 'static',
},
],
d: [],
},
},
{},
['a', 'b'],
['deploy'],
null,
{}
);

expect(taskGraph.dependencies['a:deploy']).toEqual(['a:build']);
expect(taskGraph.dependencies['a:build']).toEqual(['a:compile']);
expect(taskGraph.dependencies['a:compile']).toEqual([]);
expect(taskGraph.dependencies['b:deploy']).toEqual(['b:build']);
expect(taskGraph.dependencies['b:build']).toEqual(['b:compile']);
expect(taskGraph.dependencies['b:compile']).toEqual(['d:compile']);
expect(taskGraph.dependencies['d:compile']).toEqual([]);
});

it('should handle deep dependsOn groups', () => {
const taskGraph = createTaskGraph(
new GraphBuilder()
.addProjectConfiguration({
name: 'app-1',
targets: {
deploy: {
executor: 'foo',
dependsOn: ['build'],
},
build: {
executor: 'foo',
dependsOn: ['^build', 'codegen'],
},
codegen: {
executor: 'foo',
},
},
})
.addProjectConfiguration({
name: 'app-2',
targets: {
deploy: {
executor: 'foo',
dependsOn: ['build'],
},
build: {
executor: 'foo',
dependsOn: [
'^build',
{
target: 'codegen',
params: 'forward',
},
],
},
codegen: {
executor: 'foo',
},
},
})
.addProjectConfiguration({
name: 'app-3',
targets: {
deploy: {
executor: 'foo',
dependsOn: ['build'],
},
build: {
executor: 'foo',
dependsOn: [
'^build',
{
target: 'codegen',
params: 'forward',
},
],
},
codegen: {
executor: 'foo',
},
},
})
.addProjectConfiguration({
name: 'lib-1',
targets: {
build: {
executor: 'foo',
dependsOn: ['^build', 'codegen'],
},
codegen: {
executor: 'foo',
},
},
})
.addProjectConfiguration({
name: 'lib-2',
targets: {
build: {
executor: 'foo',
dependsOn: ['^build', 'codegen'],
},
codegen: {
executor: 'foo',
},
},
})
.addDependencies({
'app-1': ['lib-1'],
'app-2': ['lib-2'],
'app-3': ['lib-2'],
'lib-1': ['lib-2'],
'lib-2': [],
})
.build(),
{},
['app-1', 'app-2', 'app-3'],
['deploy', 'test'],
null,
{},
false
);

expect(taskGraph.dependencies).toMatchInlineSnapshot(`
{
"app-1:build": [
"lib-1:build",
"app-1:codegen",
],
"app-1:codegen": [],
"app-1:deploy": [
"app-1:build",
],
"app-2:build": [
"lib-2:build",
"app-2:codegen",
],
"app-2:codegen": [],
"app-2:deploy": [
"app-2:build",
],
"app-3:build": [
"lib-2:build",
"app-3:codegen",
],
"app-3:codegen": [],
"app-3:deploy": [
"app-3:build",
],
"lib-1:build": [
"lib-2:build",
"lib-1:codegen",
],
"lib-1:codegen": [],
"lib-2:build": [
"lib-2:codegen",
],
"lib-2:codegen": [],
}
`);
});
});

class GraphBuilder {
nodes: Record<string, ProjectGraphProjectNode> = {};
deps: Record<string, string[]> = {};

addProjectConfiguration(
project: Omit<ProjectConfiguration, 'root'>,
type?: ProjectGraph['nodes'][string]['type']
) {
const t = type ?? 'lib';
this.nodes[project.name] = {
name: project.name,
type: t,
data: { ...project, root: `${t}/${project.name}` },
};
return this;
}

addDependencies(deps: Record<string, string[]>) {
for (const source of Object.keys(deps)) {
if (!this.deps[source]) {
this.deps[source] = [];
}
this.deps[source].push(...deps[source]);
}
return this;
}

build(): ProjectGraph {
return {
nodes: this.nodes,
dependencies: Object.fromEntries(
Object.entries(this.deps).map(([k, v]) => [
k,
v.map((d) => ({ source: k, target: d, type: 'static' })),
])
),
externalNodes: {},
};
}
}
Loading

0 comments on commit a4ea9bc

Please sign in to comment.