renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
217 lines • 10.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractPackageFile = extractPackageFile;
exports.extractAllPackageFiles = extractAllPackageFiles;
const tslib_1 = require("tslib");
const upath_1 = tslib_1.__importDefault(require("upath"));
const logger_1 = require("../../../logger");
const array_1 = require("../../../util/array");
const fs_1 = require("../../../util/fs");
const util_1 = require("../../../util/fs/util");
const extract_1 = require("../pip_requirements/extract");
const pip_setup_1 = require("../pip_setup");
const common_1 = require("./common");
const utils_1 = require("./utils");
function extractPackageFile(content, packageFile, _config) {
logger_1.logger.trace('pip-compile.extractPackageFile()');
const manager = (0, common_1.matchManager)(packageFile);
switch (manager) {
case 'pip_setup':
return (0, pip_setup_1.extractPackageFile)(content, packageFile, _config);
case 'pip_requirements':
return (0, extract_1.extractPackageFile)(content);
case 'unknown':
logger_1.logger.warn({ packageFile }, `pip-compile: could not determine manager for source file`);
return null;
default:
logger_1.logger.warn({ packageFile, manager }, `pip-compile: support for manager is not yet implemented`);
return null;
}
}
async function extractAllPackageFiles(config, matchedFiles) {
logger_1.logger.trace('pip-compile.extractAllPackageFiles()');
const lockFileArgs = new Map();
const depsBetweenFiles = [];
const packageFiles = new Map();
const lockFileSources = new Map();
for (const matchedFile of matchedFiles) {
const fileContent = await (0, fs_1.readLocalFile)(matchedFile, 'utf8');
if (!fileContent) {
logger_1.logger.debug(`pip-compile: no content found for file ${matchedFile}`);
continue;
}
let compileArgs;
let compileDir;
try {
compileArgs = (0, common_1.extractHeaderCommand)(fileContent, matchedFile);
compileDir = (0, utils_1.inferCommandExecDir)(matchedFile, compileArgs.outputFile);
}
catch (error) {
logger_1.logger.warn({ matchedFile, errorMessage: error.message }, 'pip-compile error');
continue;
}
lockFileArgs.set(matchedFile, compileArgs);
for (const constraint of (0, array_1.coerceArray)(compileArgs.constraintsFiles)) {
depsBetweenFiles.push({
sourceFile: constraint,
outputFile: matchedFile,
type: 'constraint',
});
}
const lockedDeps = (0, extract_1.extractPackageFile)(fileContent)?.deps;
if (!lockedDeps) {
logger_1.logger.debug({ matchedFile }, 'pip-compile: Failed to extract dependencies from lock file');
continue;
}
for (const relativeSourceFile of compileArgs.sourceFiles) {
const packageFile = upath_1.default.normalizeTrim(upath_1.default.join(compileDir, relativeSourceFile));
try {
(0, util_1.ensureLocalPath)(packageFile);
}
catch {
logger_1.logger.warn({ matchedFile, packageFile }, 'pip-compile: Source file path outside of repository');
continue;
}
depsBetweenFiles.push({
sourceFile: packageFile,
outputFile: matchedFile,
type: 'requirement',
});
if (matchedFiles.includes(packageFile)) {
// TODO(not7cd): do something about it
logger_1.logger.warn({ sourceFile: packageFile, lockFile: matchedFile }, 'pip-compile: lock file acts as source file for another lock file');
continue;
}
if (packageFiles.has(packageFile)) {
logger_1.logger.debug(`pip-compile: ${packageFile} used in multiple output files`);
const existingPackageFile = packageFiles.get(packageFile);
existingPackageFile.lockFiles.push(matchedFile);
extendWithIndirectDeps(existingPackageFile, lockedDeps);
const source = lockFileSources.get(matchedFile) ?? [];
source.push(existingPackageFile);
lockFileSources.set(matchedFile, source);
continue;
}
const content = await (0, fs_1.readLocalFile)(packageFile, 'utf8');
if (!content) {
logger_1.logger.debug(`pip-compile: No content for source file ${packageFile}`);
continue;
}
const packageFileContent = extractPackageFile(content, packageFile, config);
if (packageFileContent) {
if (packageFileContent.managerData?.requirementsFiles) {
packageFileContent.managerData.requirementsFiles =
packageFileContent.managerData.requirementsFiles.map((file) => upath_1.default.normalize(upath_1.default.join(compileDir, file)));
for (const file of packageFileContent.managerData.requirementsFiles) {
depsBetweenFiles.push({
sourceFile: file,
outputFile: packageFile,
type: 'requirement',
});
}
}
if (packageFileContent.managerData?.constraintsFiles) {
packageFileContent.managerData.constraintsFiles =
packageFileContent.managerData.constraintsFiles.map((file) => upath_1.default.normalize(upath_1.default.join(compileDir, file)));
for (const file of packageFileContent.managerData.constraintsFiles) {
depsBetweenFiles.push({
sourceFile: file,
outputFile: packageFile,
type: 'requirement',
});
}
}
for (const dep of packageFileContent.deps) {
const lockedVersion = lockedDeps?.find((lockedDep) => lockedDep.packageName === dep.packageName)?.currentVersion;
if (lockedVersion) {
dep.lockedVersion = lockedVersion;
}
else {
logger_1.logger.warn({ depName: dep.depName, lockFile: matchedFile }, 'pip-compile: dependency not found in lock file');
}
}
extendWithIndirectDeps(packageFileContent, lockedDeps);
const newPackageFile = {
...packageFileContent,
lockFiles: [matchedFile],
packageFile,
};
packageFiles.set(packageFile, newPackageFile);
const source = lockFileSources.get(matchedFile) ?? [];
source.push(newPackageFile);
lockFileSources.set(matchedFile, source);
}
else {
logger_1.logger.warn({ packageFile }, 'pip-compile: failed to find dependencies in source file');
}
}
}
if (packageFiles.size === 0) {
return null;
}
const result = (0, utils_1.sortPackageFiles)(depsBetweenFiles, packageFiles);
// This needs to go in reverse order to handle transitive dependencies
for (const packageFile of [...result].reverse()) {
for (const reqFile of packageFile.managerData?.requirementsFiles ?? []) {
let sourceFiles = undefined;
if (matchedFiles.includes(reqFile)) {
sourceFiles = lockFileSources.get(reqFile);
}
else if (packageFiles.has(reqFile)) {
sourceFiles = [packageFiles.get(reqFile)];
}
if (!sourceFiles) {
logger_1.logger.warn({ packageFile: packageFile.packageFile, requirementsFile: reqFile }, 'pip-compile: Package file references a file which does not appear to be a requirements file managed by pip-compile');
continue;
}
// These get reversed before merging so that we keep the last instance of any common
// lock files, since a file that -r includes multiple lock files needs to be updated after
// all of the lock files it includes
const files = new Set([...packageFile.lockFiles].reverse());
for (const sourceFile of sourceFiles) {
const merged = new Set(files);
for (const lockFile of [...sourceFile.lockFiles].reverse()) {
merged.add(lockFile);
}
sourceFile.lockFiles = Array.from(merged).reverse();
}
}
}
logger_1.logger.debug('pip-compile: dependency graph:\n' +
(0, utils_1.generateMermaidGraph)(depsBetweenFiles, lockFileArgs));
return result;
}
function extendWithIndirectDeps(packageFileContent, lockedDeps) {
for (const lockedDep of lockedDeps) {
if (!packageFileContent.deps.find((dep) => lockedDep.packageName === dep.packageName)) {
packageFileContent.deps.push(indirectDep(lockedDep));
}
}
}
/**
* As indirect dependecies don't exist in the package file, we need to
* create them from the lock file.
*
* By removing currentValue and currentVersion, we ensure that they
* are handled like unconstrained dependencies with locked version.
* Such packages are updated when their update strategy
* is set to 'update-lockfile',
* see: lib/workers/repository/process/lookup/index.ts.
*
* By disabling them by default, we won't create noise by updating them.
* Unless they have vulnerability alert, then they are forced to be updated.
* @param dep dependency extracted from lock file (requirements.txt)
* @returns unconstrained dependency with locked version
*/
function indirectDep(dep) {
const result = {
...dep,
lockedVersion: dep.currentVersion,
depType: 'indirect',
enabled: false,
};
delete result.currentValue;
delete result.currentVersion;
return result;
}
//# sourceMappingURL=extract.js.map