renovate/lib/modules/manager/pip-compile/utils.ts

98 lines
3.3 KiB
TypeScript

import { Graph } from 'graph-data-structure';
import upath from 'upath';
import { logger } from '../../../logger';
import type { PackageFile } from '../types';
import type { DependencyBetweenFiles, PipCompileArgs } from './types';
export function sortPackageFiles(
depsBetweenFiles: DependencyBetweenFiles[],
packageFiles: Map<string, PackageFile>,
): PackageFile[] {
const result: PackageFile[] = [];
const graph: ReturnType<typeof Graph> = Graph();
depsBetweenFiles.forEach(({ sourceFile, outputFile }) => {
graph.addEdge(sourceFile, outputFile);
});
const sorted = graph.topologicalSort();
for (const file of sorted) {
if (packageFiles.has(file)) {
const packageFile = packageFiles.get(file)!;
const sortedLockFiles = [];
// TODO(not7cd): this needs better test case
for (const lockFile of packageFile.lockFiles!) {
if (sorted.includes(lockFile)) {
sortedLockFiles.push(lockFile);
}
}
packageFile.lockFiles = sortedLockFiles;
result.push(packageFile);
}
}
// istanbul ignore if: should never happen
if (result.length !== packageFiles.size) {
throw new Error('Topological sort failed to include all package files');
}
return result;
}
export function generateMermaidGraph(
depsBetweenFiles: DependencyBetweenFiles[],
lockFileArgs: Map<string, PipCompileArgs>,
): string {
const lockFiles = [];
for (const lockFile of lockFileArgs.keys()) {
// TODO: add extra args to the lock file ${extraArgs ? '\n' + extraArgs : ''}
// const extraArgs = pipCompileArgs.extra
// ?.map((v) => '--extra=' + v)
// .join('\n');
lockFiles.push(` ${lockFile}[[${lockFile}]]`);
}
const edges = depsBetweenFiles.map(({ sourceFile, outputFile, type }) => {
return ` ${sourceFile} -${type === 'constraint' ? '.' : ''}-> ${outputFile}`;
});
return `graph TD\n${lockFiles.join('\n')}\n${edges.join('\n')}`;
}
export function inferCommandExecDir(
outputFilePath: string,
outputFileArg: string | undefined,
): string {
if (!outputFileArg) {
// implicit output file is in the same directory where command was executed
return upath.normalize(upath.dirname(outputFilePath));
}
if (upath.normalize(outputFileArg).startsWith('..')) {
throw new Error(
`Cannot infer command execution directory from path ${outputFileArg}`,
);
}
if (upath.basename(outputFileArg) !== upath.basename(outputFilePath)) {
throw new Error(
`Output file name mismatch: ${upath.basename(outputFileArg)} vs ${upath.basename(outputFilePath)}`,
);
}
const outputFileDir = upath.normalize(upath.dirname(outputFileArg));
let commandExecDir = upath.normalize(upath.dirname(outputFilePath));
for (const dir of outputFileDir.split('/').reverse()) {
if (commandExecDir.endsWith(dir)) {
commandExecDir = upath.join(commandExecDir.slice(0, -dir.length), '.');
// outputFileDir = upath.join(outputFileDir.slice(0, -dir.length), '.');
} else {
break;
}
}
commandExecDir = upath.normalizeTrim(commandExecDir);
if (commandExecDir !== '.') {
logger.debug(
{
commandExecDir,
outputFileArg,
outputFilePath,
},
`pip-compile: command was not executed in repository root`,
);
}
return commandExecDir;
}