diff --git a/.changeset/fix-lockfile-removal-detection.md b/.changeset/fix-lockfile-removal-detection.md new file mode 100644 index 0000000000..3f7d9541ae --- /dev/null +++ b/.changeset/fix-lockfile-removal-detection.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli-node': patch +--- + +Fixed a bug in `PackageGraph.listChangedPackages` where removed dependencies were not detected during lockfile analysis. The dependency graph from the previous lockfile was not being merged, causing transitive dependency removals to be missed. diff --git a/packages/cli-node/src/monorepo/PackageGraph.test.ts b/packages/cli-node/src/monorepo/PackageGraph.test.ts index 51d8f7b7be..7ee41e939d 100644 --- a/packages/cli-node/src/monorepo/PackageGraph.test.ts +++ b/packages/cli-node/src/monorepo/PackageGraph.test.ts @@ -222,4 +222,51 @@ c-dep@^2: 'origin/master', ); }); + + it('detects packages affected by a removed dependency in the lockfile', async () => { + const graph = PackageGraph.fromPackages(testPackages); + + mockListChangedFiles.mockResolvedValueOnce(['yarn.lock']); + + // The old lockfile (at the ref) has b depending on b-dep + mockReadFileAtRef.mockResolvedValueOnce(` +a@^1: + version: "1.0.0" + +b@^1: + version: "1.0.0" + dependencies: + b-dep: ^1 + +b-dep@^1: + version: "1.0.0" + integrity: sha512-old + +c@^1: + version: "1.0.0" +`); + + // The current lockfile no longer has b-dep at all + jest.spyOn(Lockfile, 'load').mockResolvedValueOnce( + Lockfile.parse(` +a@^1: + version: "1.0.0" + +b@^1: + version: "1.0.0" + +c@^1: + version: "1.0.0" +`), + ); + + await expect( + graph + .listChangedPackages({ + ref: 'origin/master', + analyzeLockfile: true, + }) + .then(pkgs => pkgs.map(pkg => pkg.name)), + ).resolves.toEqual(['b']); + }); }); diff --git a/packages/cli-node/src/monorepo/PackageGraph.ts b/packages/cli-node/src/monorepo/PackageGraph.ts index 9d1745c7c5..35fe943606 100644 --- a/packages/cli-node/src/monorepo/PackageGraph.ts +++ b/packages/cli-node/src/monorepo/PackageGraph.ts @@ -393,7 +393,7 @@ export class PackageGraph extends Map { // Merge the dependency graph from the other lockfile into this one in // order to be able to detect removals accurately. { - const otherGraph = thisLockfile.createSimplifiedDependencyGraph(); + const otherGraph = otherLockfile.createSimplifiedDependencyGraph(); for (const [name, dependencies] of otherGraph) { const node = graph.get(name); if (node) {