fix(core): do not error when hashing executors which are local plugins (#18372)
This commit is contained in:
parent
63521d7e13
commit
80c470ac7f
@ -44,6 +44,52 @@ exports[`TaskHasher dependentTasksOutputFiles should work with dependent tasks w
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`TaskHasher hashTarget should hash entire subtree in a deterministic way 1`] = `
|
||||
{
|
||||
"details": {
|
||||
"command": "81188892120010785",
|
||||
"implicitDeps": {},
|
||||
"nodes": {
|
||||
"ProjectConfiguration": "8474322003863204060",
|
||||
"TsConfig": "8767608672024750088",
|
||||
"appA:{projectRoot}/**/*": "3244421341483603138",
|
||||
"npm:@nx/webpack": "$@nx/webpack0.0.0$",
|
||||
"npm:packageA": "$packageA0.0.0$",
|
||||
"npm:packageB": "$packageB0.0.0$",
|
||||
"npm:packageC": "$packageC0.0.0$",
|
||||
"{workspaceRoot}/.gitignore": "3244421341483603138",
|
||||
"{workspaceRoot}/.nxignore": "3244421341483603138",
|
||||
"{workspaceRoot}/nx.json": "8942239360311677987",
|
||||
},
|
||||
"runtime": {},
|
||||
},
|
||||
"value": "12756041818139421941",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`TaskHasher hashTarget should hash entire subtree in a deterministic way 2`] = `
|
||||
{
|
||||
"details": {
|
||||
"command": "9096392622609675764",
|
||||
"implicitDeps": {},
|
||||
"nodes": {
|
||||
"ProjectConfiguration": "17724470359684527282",
|
||||
"TsConfig": "8767608672024750088",
|
||||
"appB:{projectRoot}/**/*": "3244421341483603138",
|
||||
"npm:@nx/webpack": "$@nx/webpack0.0.0$",
|
||||
"npm:packageA": "$packageA0.0.0$",
|
||||
"npm:packageB": "$packageB0.0.0$",
|
||||
"npm:packageC": "$packageC0.0.0$",
|
||||
"{workspaceRoot}/.gitignore": "3244421341483603138",
|
||||
"{workspaceRoot}/.nxignore": "3244421341483603138",
|
||||
"{workspaceRoot}/nx.json": "8942239360311677987",
|
||||
},
|
||||
"runtime": {},
|
||||
},
|
||||
"value": "15326312070983573452",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`TaskHasher hashTarget should hash entire subtree of dependencies 1`] = `
|
||||
{
|
||||
"details": {
|
||||
|
||||
@ -1257,6 +1257,9 @@ describe('TaskHasher', () => {
|
||||
|
||||
expect(hashAppB1).toEqual(hashAppB2);
|
||||
expect(hashAppA1).toEqual(hashAppA2);
|
||||
|
||||
expect(hashAppA1).toMatchSnapshot();
|
||||
expect(hashAppB1).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not hash when nx:run-commands executor', async () => {
|
||||
|
||||
@ -20,6 +20,7 @@ import { getHashEnv } from './set-hash-env';
|
||||
import { workspaceRoot } from '../utils/workspace-root';
|
||||
import { join, relative } from 'path';
|
||||
import { normalizePath } from '../utils/path';
|
||||
import { findAllProjectNodeDependencies } from '../utils/project-graph-utils';
|
||||
|
||||
type ExpandedSelfInput =
|
||||
| { fileset: string }
|
||||
@ -429,60 +430,39 @@ class TaskHasherImpl {
|
||||
return combinedHash;
|
||||
}
|
||||
|
||||
private hashExternalDependency(
|
||||
externalNodeName: string,
|
||||
visited: Set<string>
|
||||
): PartialHash[] {
|
||||
// try to retrieve the hash from cache
|
||||
if (this.externalDependencyHashes.has(externalNodeName)) {
|
||||
return this.externalDependencyHashes.get(externalNodeName);
|
||||
}
|
||||
visited.add(externalNodeName);
|
||||
private hashSingleExternalDependency(externalNodeName: string): PartialHash {
|
||||
const node = this.projectGraph.externalNodes[externalNodeName];
|
||||
const partialHashes: Set<PartialHash> = new Set<PartialHash>();
|
||||
if (node) {
|
||||
if (node.data.hash) {
|
||||
// we already know the hash of this dependency
|
||||
partialHashes.add({
|
||||
value: node.data.hash,
|
||||
details: {
|
||||
[externalNodeName]: node.data.hash,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// we take version as a hash
|
||||
partialHashes.add({
|
||||
value: node.data.version,
|
||||
details: {
|
||||
[externalNodeName]: node.data.version,
|
||||
},
|
||||
});
|
||||
}
|
||||
// we want to calculate the hash of the entire dependency tree
|
||||
if (this.projectGraph.dependencies[externalNodeName]) {
|
||||
this.projectGraph.dependencies[externalNodeName].forEach((d) => {
|
||||
if (!visited.has(d.target)) {
|
||||
for (const hash of this.hashExternalDependency(d.target, visited)) {
|
||||
partialHashes.add(hash);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} 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
|
||||
partialHashes.add({
|
||||
value: `__${externalNodeName}__`,
|
||||
if (node.data.hash) {
|
||||
// we already know the hash of this dependency
|
||||
return {
|
||||
value: node.data.hash,
|
||||
details: {
|
||||
[externalNodeName]: `__${externalNodeName}__`,
|
||||
[externalNodeName]: node.data.hash,
|
||||
},
|
||||
});
|
||||
};
|
||||
} else {
|
||||
// we take version as a hash
|
||||
return {
|
||||
value: node.data.version,
|
||||
details: {
|
||||
[externalNodeName]: node.data.version,
|
||||
},
|
||||
};
|
||||
}
|
||||
const partialHashArray = Array.from(partialHashes);
|
||||
this.externalDependencyHashes.set(externalNodeName, partialHashArray);
|
||||
return partialHashArray;
|
||||
}
|
||||
|
||||
private hashExternalDependency(externalNodeName: string) {
|
||||
const partialHashes: Set<PartialHash> = new Set<PartialHash>();
|
||||
partialHashes.add(this.hashSingleExternalDependency(externalNodeName));
|
||||
const deps = findAllProjectNodeDependencies(
|
||||
externalNodeName,
|
||||
this.projectGraph,
|
||||
true
|
||||
);
|
||||
for (const dep of deps) {
|
||||
partialHashes.add(this.hashSingleExternalDependency(dep));
|
||||
}
|
||||
return Array.from(partialHashes);
|
||||
}
|
||||
|
||||
private hashTarget(
|
||||
@ -507,6 +487,13 @@ class TaskHasherImpl {
|
||||
const executorPackage = target.executor.split(':')[0];
|
||||
const executorNodeName =
|
||||
this.findExternalDependencyNodeName(executorPackage);
|
||||
|
||||
// This is either a local plugin or a non-existent executor
|
||||
if (!executorNodeName) {
|
||||
// TODO: This should not return null if it is a local plugin's executor
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getExternalDependencyHash(executorNodeName);
|
||||
} else {
|
||||
// use command external dependencies if available to construct the hash
|
||||
@ -519,6 +506,12 @@ class TaskHasherImpl {
|
||||
const externalDependencies = input['externalDependencies'];
|
||||
for (let dep of externalDependencies) {
|
||||
dep = this.findExternalDependencyNodeName(dep);
|
||||
if (!dep) {
|
||||
throw new Error(
|
||||
`The externalDependency "${dep}" for "${projectName}:${targetName}" could not be found`
|
||||
);
|
||||
}
|
||||
|
||||
partialHashes.push(this.getExternalDependencyHash(dep));
|
||||
}
|
||||
}
|
||||
@ -543,7 +536,7 @@ class TaskHasherImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private findExternalDependencyNodeName(packageName: string): string {
|
||||
private findExternalDependencyNodeName(packageName: string): string | null {
|
||||
if (this.projectGraph.externalNodes[packageName]) {
|
||||
return packageName;
|
||||
}
|
||||
@ -555,8 +548,8 @@ class TaskHasherImpl {
|
||||
return node.name;
|
||||
}
|
||||
}
|
||||
// not found, just return the package name
|
||||
return packageName;
|
||||
// not found
|
||||
return null;
|
||||
}
|
||||
|
||||
private async hashSingleProjectInputs(
|
||||
@ -768,7 +761,10 @@ class TaskHasherImpl {
|
||||
private calculateExternalDependencyHashes() {
|
||||
const keys = Object.keys(this.projectGraph.externalNodes);
|
||||
for (const externalNodeName of keys) {
|
||||
this.hashExternalDependency(externalNodeName, new Set<string>());
|
||||
this.externalDependencyHashes.set(
|
||||
externalNodeName,
|
||||
this.hashExternalDependency(externalNodeName)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,21 +75,24 @@ export function getSourceDirOfDependentProjects(
|
||||
|
||||
/**
|
||||
* Find all internal project dependencies.
|
||||
* All the external (npm) dependencies will be filtered out
|
||||
* All the external (npm) dependencies will be filtered out unless includeExternalDependencies is set to true
|
||||
* @param {string} parentNodeName
|
||||
* @param {ProjectGraph} projectGraph
|
||||
* @param includeExternalDependencies
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function findAllProjectNodeDependencies(
|
||||
parentNodeName: string,
|
||||
projectGraph = readCachedProjectGraph()
|
||||
projectGraph = readCachedProjectGraph(),
|
||||
includeExternalDependencies = false
|
||||
): string[] {
|
||||
const dependencyNodeNames = new Set<string>();
|
||||
|
||||
collectDependentProjectNodesNames(
|
||||
projectGraph as ProjectGraph,
|
||||
dependencyNodeNames,
|
||||
parentNodeName
|
||||
parentNodeName,
|
||||
includeExternalDependencies
|
||||
);
|
||||
|
||||
return Array.from(dependencyNodeNames);
|
||||
@ -99,7 +102,8 @@ export function findAllProjectNodeDependencies(
|
||||
function collectDependentProjectNodesNames(
|
||||
nxDeps: ProjectGraph,
|
||||
dependencyNodeNames: Set<string>,
|
||||
parentNodeName: string
|
||||
parentNodeName: string,
|
||||
includeExternalDependencies: boolean
|
||||
) {
|
||||
const dependencies = nxDeps.dependencies[parentNodeName];
|
||||
if (!dependencies) {
|
||||
@ -111,23 +115,28 @@ function collectDependentProjectNodesNames(
|
||||
for (const dependency of dependencies) {
|
||||
const dependencyName = dependency.target;
|
||||
|
||||
// we're only interested in internal nodes, not external
|
||||
if (nxDeps.externalNodes?.[dependencyName]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip dependencies already added (avoid circular dependencies)
|
||||
if (dependencyNodeNames.has(dependencyName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// we're only interested in internal nodes, not external
|
||||
if (nxDeps.externalNodes?.[dependencyName]) {
|
||||
if (includeExternalDependencies) {
|
||||
dependencyNodeNames.add(dependencyName);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
dependencyNodeNames.add(dependencyName);
|
||||
|
||||
// Get the dependencies of the dependencies
|
||||
collectDependentProjectNodesNames(
|
||||
nxDeps,
|
||||
dependencyNodeNames,
|
||||
dependencyName
|
||||
dependencyName,
|
||||
includeExternalDependencies
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user