cli-node: add utility for generating package dependency hash
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli-node': patch
|
||||
---
|
||||
|
||||
Added new `packageGraph.getDependencyHash(name)` utility.
|
||||
@@ -92,6 +92,7 @@ export function isMonoRepo(): Promise<boolean>;
|
||||
export class Lockfile {
|
||||
createSimplifiedDependencyGraph(): Map<string, Set<string>>;
|
||||
diff(otherLockfile: Lockfile): LockfileDiff;
|
||||
getVersions(name: string): string[];
|
||||
static load(path: string): Promise<Lockfile>;
|
||||
static parse(content: string): Lockfile;
|
||||
}
|
||||
@@ -116,6 +117,7 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
|
||||
collectFn: (pkg: PackageGraphNode) => Iterable<string> | undefined,
|
||||
): Set<string>;
|
||||
static fromPackages(packages: Package[]): PackageGraph;
|
||||
getDependencyHash(name: string): Promise<string>;
|
||||
listChangedPackages(options: {
|
||||
ref: string;
|
||||
analyzeLockfile?: boolean;
|
||||
|
||||
@@ -133,6 +133,14 @@ export class Lockfile {
|
||||
private readonly data: LockfileData,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Returns all versions of a package in the lockfile.
|
||||
*/
|
||||
getVersions(name: string): string[] {
|
||||
const queries = this.packages.get(name);
|
||||
return queries ? queries.map(q => q.version) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a simplified dependency graph from the lockfile data, where each
|
||||
* key is a package, and the value is a set of all packages that it depends on
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import crypto from 'node:crypto';
|
||||
import { getPackages, Package } from '@manypkg/get-packages';
|
||||
import { paths } from '../paths';
|
||||
import { PackageRole } from '../roles';
|
||||
@@ -283,6 +284,43 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
|
||||
return targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a sha1 hex hash of the dependency graph for a package.
|
||||
*/
|
||||
async getDependencyHash(name: string): Promise<string> {
|
||||
const pkg = this.get(name);
|
||||
if (!pkg) {
|
||||
throw new Error(`Package '${name}' not found`);
|
||||
}
|
||||
|
||||
const lockfile = await this.#getLockfile();
|
||||
const depGraph = lockfile.createSimplifiedDependencyGraph();
|
||||
|
||||
const seen = new Set<string>();
|
||||
const queue = [name];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const deps = depGraph.get(queue.pop()!);
|
||||
if (deps) {
|
||||
for (const dep of deps) {
|
||||
if (!seen.has(dep)) {
|
||||
seen.add(dep);
|
||||
queue.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hash = crypto.createHash('sha1');
|
||||
for (const dep of Array.from(seen).sort()) {
|
||||
hash.update(dep);
|
||||
hash.update('\0');
|
||||
hash.update(lockfile.getVersions(dep).join(' '));
|
||||
hash.update('\0');
|
||||
}
|
||||
return hash.digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all packages that have changed since a given git ref.
|
||||
*
|
||||
@@ -342,9 +380,7 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
|
||||
let thisLockfile: Lockfile;
|
||||
let otherLockfile: Lockfile;
|
||||
try {
|
||||
thisLockfile = await Lockfile.load(
|
||||
paths.resolveTargetRoot('yarn.lock'),
|
||||
);
|
||||
thisLockfile = await this.#getLockfile();
|
||||
otherLockfile = Lockfile.parse(
|
||||
await GitUtils.readFileAtRef('yarn.lock', options.ref),
|
||||
);
|
||||
@@ -410,4 +446,13 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#lockfilePromise?: Promise<Lockfile>;
|
||||
#getLockfile(): Promise<Lockfile> {
|
||||
if (this.#lockfilePromise) {
|
||||
return this.#lockfilePromise;
|
||||
}
|
||||
this.#lockfilePromise = Lockfile.load(paths.resolveTargetRoot('yarn.lock'));
|
||||
return this.#lockfilePromise;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user