cli: add ability to analyze lockfile changes when listing changed packages

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-12-13 23:23:11 +01:00
parent a11a8082ce
commit 79bc82828f
+68 -2
View File
@@ -18,7 +18,8 @@ import path from 'path';
import { getPackages, Package } from '@manypkg/get-packages';
import { paths } from '../paths';
import { PackageRole } from '../role';
import { listChangedFiles } from '../git';
import { listChangedFiles, readFileAtRef } from '../git';
import { Lockfile } from '../versioning';
type PackageJSON = Package['packageJson'];
@@ -191,7 +192,10 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
return targets;
}
async listChangedPackages(options: { ref: string }) {
async listChangedPackages(options: {
ref: string;
analyzeLockfile?: boolean;
}) {
const changedFiles = await listChangedFiles(options.ref);
const dirMap = new Map(
@@ -234,6 +238,68 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
}
}
if (changedFiles.includes('yarn.lock') && options.analyzeLockfile) {
// Load the lockfile in the working tree and the one at the ref and diff them
const thisLockfile = await Lockfile.load(
paths.resolveTargetRoot('yarn.lock'),
);
const otherLockfile = Lockfile.parse(
await readFileAtRef('yarn.lock', options.ref),
);
const diff = thisLockfile.diff(otherLockfile);
// Create a simplified dependency graph only keeps track of package names
const graph = thisLockfile.createSimplifiedDependencyGraph();
// Merge the dependency graph from the other lockfile into this one in
// order to be able to detect removals accurately.
{
const otherGraph = thisLockfile.createSimplifiedDependencyGraph();
for (const [name, dependencies] of otherGraph) {
const node = graph.get(name);
if (node) {
dependencies.forEach(d => node.add(d));
} else {
graph.set(name, dependencies);
}
}
}
// The check is simplified by only considering the package names rather
// than the exact version range queries that were changed.
// TODO(Rugvip): Use a more exact check
const changedPackages = new Set(
[...diff.added, ...diff.changed, ...diff.removed].map(e => e.name),
);
// Starting with our set of changed packages from the diff, we loop through
// the full graph and add any package that has a dependency on a changed package.
// We keep looping until all transitive dependencies have been detected.
let changed = false;
do {
changed = false;
for (const [name, dependencies] of graph) {
if (changedPackages.has(name)) {
continue;
}
for (const dep of dependencies) {
if (changedPackages.has(dep)) {
changed = true;
changedPackages.add(name);
break;
}
}
}
} while (changed);
// Add all local packages that had a transitive dependency change to the result set
for (const node of this.values()) {
if (changedPackages.has(node.name) && !result.includes(node)) {
result.push(node);
}
}
}
return result;
}
}