Skip to content

Commit

Permalink
feat(core): include entire external node dependency tree in hash
Browse files Browse the repository at this point in the history
  • Loading branch information
meeroslav committed May 11, 2023
1 parent 09525e8 commit 671e64a
Show file tree
Hide file tree
Showing 2 changed files with 235 additions and 22 deletions.
201 changes: 193 additions & 8 deletions packages/nx/src/hasher/hasher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ describe('Hasher', () => {
data: {
root: 'libs/parent',
targets: {
build: { executor: '@nx/workspace:run-commands' },
build: { executor: 'nx:run-commands' },
},
files: [
{ file: 'libs/parent/filea.ts', hash: 'a.hash' },
Expand All @@ -472,7 +472,7 @@ describe('Hasher', () => {
type: 'lib',
data: {
root: 'libs/child',
targets: { build: { executor: '@nx/workspace:run-commands' } },
targets: { build: { executor: 'nx:run-commands' } },
files: [
{ file: 'libs/child/fileb.ts', hash: 'b.hash' },
{ file: 'libs/child/fileb.spec.ts', hash: 'b.spec.hash' },
Expand Down Expand Up @@ -526,7 +526,7 @@ describe('Hasher', () => {
type: 'lib',
data: {
root: 'libs/parent',
targets: { build: { executor: '@nx/workspace:run-commands' } },
targets: { build: { executor: 'nx:run-commands' } },
files: [{ file: '/file', hash: 'file.hash' }],
},
},
Expand Down Expand Up @@ -575,7 +575,7 @@ describe('Hasher', () => {
type: 'lib',
data: {
root: 'libs/parent',
targets: { build: { executor: '@nx/workspace:run-commands' } },
targets: { build: { executor: 'nx:run-commands' } },
files: [{ file: '/filea.ts', hash: 'a.hash' }],
},
},
Expand All @@ -584,7 +584,7 @@ describe('Hasher', () => {
type: 'lib',
data: {
root: 'libs/child',
targets: { build: { executor: '@nx/workspace:run-commands' } },
targets: { build: { executor: 'nx:run-commands' } },
files: [{ file: '/fileb.ts', hash: 'b.hash' }],
},
},
Expand Down Expand Up @@ -656,7 +656,7 @@ describe('Hasher', () => {
type: 'lib',
data: {
root: 'libs/parent',
targets: { build: { executor: '@nx/workspace:run-commands' } },
targets: { build: { executor: 'nx:run-commands' } },
files: [{ file: '/file', hash: 'some-hash' }],
},
},
Expand Down Expand Up @@ -698,7 +698,7 @@ describe('Hasher', () => {
type: 'app',
data: {
root: 'apps/app',
targets: { build: { executor: '@nx/workspace:run-commands' } },
targets: { build: { executor: 'nx:run-commands' } },
files: [{ file: '/filea.ts', hash: 'a.hash' }],
},
},
Expand Down Expand Up @@ -747,7 +747,7 @@ describe('Hasher', () => {
type: 'app',
data: {
root: 'apps/app',
targets: { build: { executor: '@nx/workspace:run-commands' } },
targets: { build: { executor: 'nx:run-commands' } },
files: [{ file: '/filea.ts', hash: 'a.hash' }],
},
},
Expand Down Expand Up @@ -782,6 +782,191 @@ describe('Hasher', () => {
});
});

describe('hashTarget', () => {
it('should hash executor dependencies of @nx packages', async () => {
const hasher = new Hasher(
{
nodes: {
app: {
name: 'app',
type: 'app',
data: {
root: 'apps/app',
targets: { build: { executor: '@nx/webpack:webpack' } },
files: [{ file: '/filea.ts', hash: 'a.hash' }],
},
},
},
externalNodes: {
'npm:@nx/webpack': {
name: 'npm:@nx/webpack',
type: 'npm',
data: {
packageName: '@nx/webpack',
version: '16.0.0',
},
},
},
dependencies: {},
allWorkspaceFiles,
},
{} as any,
{},
createHashing()
);

const hash = await hasher.hashTask({
target: { project: 'app', target: 'build' },
id: 'app-build',
overrides: { prop: 'prop-value' },
});

assertFilesets(hash, {
'npm:@nx/webpack': { contains: '16.0.0' },
});
});

it('should hash entire subtree of dependencies', async () => {
const hasher = new Hasher(
{
nodes: {
app: {
name: 'app',
type: 'app',
data: {
root: 'apps/app',
targets: { build: { executor: '@nx/webpack:webpack' } },
files: [{ file: '/filea.ts', hash: 'a.hash' }],
},
},
},
externalNodes: {
'npm:@nx/webpack': {
name: 'npm:@nx/webpack',
type: 'npm',
data: {
packageName: '@nx/webpack',
version: '16.0.0',
hash: '$nx/webpack16$',
},
},
'npm:@nx/devkit': {
name: 'npm:@nx/devkit',
type: 'npm',
data: {
packageName: '@nx/devkit',
version: '16.0.0',
hash: '$nx/devkit16$',
},
},
'npm:nx': {
name: 'npm:nx',
type: 'npm',
data: {
packageName: 'nx',
version: '16.0.0',
hash: '$nx16$',
},
},
'npm:webpack': {
name: 'npm:webpack',
type: 'npm',
data: {
packageName: 'webpack',
version: '5.0.0', // no hash intentionally
},
},
},
dependencies: {
'npm:@nx/webpack': [
{
source: 'npm:@nx/webpack',
target: 'npm:@nx/devkit',
type: DependencyType.static,
},
{
source: 'npm:@nx/webpack',
target: 'npm:nx',
type: DependencyType.static,
},
{
source: 'npm:@nx/webpack',
target: 'npm:webpack',
type: DependencyType.static,
},
],
'npm:@nx/devkit': [
{
source: 'npm:@nx/devkit',
target: 'npm:nx',
type: DependencyType.static,
},
],
},
allWorkspaceFiles,
},
{} as any,
{},
createHashing()
);

const hash = await hasher.hashTask({
target: { project: 'app', target: 'build' },
id: 'app-build',
overrides: { prop: 'prop-value' },
});

assertFilesets(hash, {
'npm:@nx/webpack': { contains: '$nx/webpack16$' },
'npm:@nx/devkit': { contains: '$nx/devkit16$' },
'npm:nx': { contains: '$nx16$' },
'npm:webpack': { contains: '5.0.0' },
});
});

it('should not hash when nx:run-commands executor', async () => {
const hasher = new Hasher(
{
nodes: {
app: {
name: 'app',
type: 'app',
data: {
root: 'apps/app',
targets: { build: { executor: 'nx:run-commands' } },
files: [{ file: '/filea.ts', hash: 'a.hash' }],
},
},
},
externalNodes: {
'npm:nx': {
name: 'npm:nx',
type: 'npm',
data: {
packageName: 'nx',
version: '16.0.0',
},
},
},
dependencies: {},
allWorkspaceFiles,
},
{} as any,
{},
createHashing()
);

const hash = await hasher.hashTask({
target: { project: 'app', target: 'build' },
id: 'app-build',
overrides: { prop: 'prop-value' },
});

expect(hash.details.nodes['npm:nx']).not.toBeDefined();
expect(hash.details.nodes['app']).toEqual('nx:run-commands');
});
});

describe('expandNamedInput', () => {
it('should expand named inputs', () => {
const expanded = expandNamedInput('c', {
Expand Down
56 changes: 42 additions & 14 deletions packages/nx/src/hasher/hasher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ class TaskHasher {
private runtimeHashes: {
[runtime: string]: Promise<PartialHash>;
} = {};
private externalDepsHashCache: { [packageName: string]: PartialHash } = {};

constructor(
private readonly nxJson: NxJsonConfiguration,
Expand Down Expand Up @@ -321,26 +322,53 @@ class TaskHasher {
.filter((r) => !!r);
}

private hashExternalDependency(projectName: string) {
const n = this.projectGraph.externalNodes[projectName];
const version = n?.data?.version;
let hash: string;
if (n?.data?.hash) {
// we already know the hash of this dependency
hash = n.data.hash;
private hashExternalDependency(projectName: string): PartialHash {
// try to retrieve the hash from cache
if (this.externalDepsHashCache[projectName]) {
return this.externalDepsHashCache[projectName];
}
const node = this.projectGraph.externalNodes[projectName];
let partialHash;
if (node) {
let hash;
if (node.data.hash) {
// we already know the hash of this dependency
hash = node.data.hash;
} else {
// we take version as a hash
hash = node.data.version;
}
// we want to calculate the hash of the entire dependency tree
const partialHashes: PartialHash[] = [];
if (this.projectGraph.dependencies[projectName]) {
this.projectGraph.dependencies[projectName].forEach((d) => {
partialHashes.push(this.hashExternalDependency(d.target));
});
}
partialHash = {
value: this.hashing.hashArray([
hash,
...partialHashes.map((p) => p.value),
]),
details: {
[projectName]: hash,
...partialHashes.reduce((m, c) => ({ ...m, ...c.details }), {}),
},
};
} else {
// unknown dependency
// this may occur if dependency is not an npm package
// but rather symlinked in node_modules or it's pointing to a remote git repo
// in this case we have no information about the versioning of the given package
hash = version ? `__${projectName}@${version}__` : `__${projectName}__`;
partialHash = {
value: `__${projectName}__`,
details: {
[projectName]: `__${projectName}__`,
},
};
}
return {
value: hash,
details: {
[projectName]: version || hash,
},
};
this.externalDepsHashCache[projectName] = partialHash;
return partialHash;
}

private hashTarget(projectName: string, targetName: string): PartialHash {
Expand Down

0 comments on commit 671e64a

Please sign in to comment.