renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
359 lines • 17.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUpdatedPackageFiles = getUpdatedPackageFiles;
const tslib_1 = require("tslib");
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const error_messages_1 = require("../../../../constants/error-messages");
const logger_1 = require("../../../../logger");
const manager_1 = require("../../../../modules/manager");
const git_1 = require("../../../../util/git");
const string_1 = require("../../../../util/string");
const auto_replace_1 = require("./auto-replace");
async function getFileContent(updatedFileContents, filePath, config) {
let fileContent = updatedFileContents[filePath];
if (!fileContent) {
fileContent = await (0, git_1.getFile)(filePath, config.reuseExistingBranch ? config.branchName : config.baseBranch);
}
return fileContent;
}
function sortPackageFiles(config, manager, packageFiles) {
const managerPackageFiles = config.packageFiles?.[manager];
if (!managerPackageFiles) {
return;
}
packageFiles.sort((lhs, rhs) => {
const lhsIndex = managerPackageFiles.findIndex((entry) => entry.packageFile === lhs.path);
const rhsIndex = managerPackageFiles.findIndex((entry) => entry.packageFile === rhs.path);
return lhsIndex - rhsIndex;
});
}
function hasAny(set, targets) {
for (const target of targets) {
if (set.has(target)) {
return true;
}
}
return false;
}
function getManagersForPackageFiles(packageFiles, managerPackageFiles) {
const packageFileNames = packageFiles.map((packageFile) => packageFile.path);
return new Set(Object.keys(managerPackageFiles).filter((manager) => hasAny(managerPackageFiles[manager], packageFileNames)));
}
function getPackageFilesForManager(packageFiles, managerPackageFiles) {
return packageFiles.filter((packageFile) => managerPackageFiles.has(packageFile.path));
}
async function getUpdatedPackageFiles(config) {
logger_1.logger.trace({ config });
const reuseExistingBranch = config.reuseExistingBranch;
logger_1.logger.debug(`manager.getUpdatedPackageFiles() reuseExistingBranch=${reuseExistingBranch}`);
let updatedFileContents = {};
const nonUpdatedFileContents = {};
const managerPackageFiles = {};
const packageFileUpdatedDeps = {};
const lockFileMaintenanceFiles = [];
let firstUpdate = true;
for (const upgrade of config.upgrades) {
const manager = upgrade.manager;
const packageFile = upgrade.packageFile;
const depName = upgrade.depName;
// TODO: fix types, can be undefined (#22198)
const newVersion = upgrade.newVersion;
const currentVersion = upgrade.currentVersion;
const updateLockedDependency = (0, manager_1.get)(manager, 'updateLockedDependency');
managerPackageFiles[manager] ??= new Set();
managerPackageFiles[manager].add(packageFile);
packageFileUpdatedDeps[packageFile] ??= [];
packageFileUpdatedDeps[packageFile].push({ ...upgrade });
const packageFileContent = await getFileContent(updatedFileContents, packageFile, config);
let lockFileContent = null;
const lockFile = upgrade.lockFile ?? upgrade.lockFiles?.[0] ?? '';
if (lockFile) {
lockFileContent = await getFileContent(updatedFileContents, lockFile, config);
}
// istanbul ignore if
if (reuseExistingBranch &&
(!packageFileContent || (lockFile && !lockFileContent))) {
logger_1.logger.debug({ packageFile, depName }, 'Rebasing branch after file not found');
return getUpdatedPackageFiles({
...config,
reuseExistingBranch: false,
});
}
if (upgrade.updateType === 'lockFileMaintenance') {
lockFileMaintenanceFiles.push(packageFile);
}
else if (upgrade.isRemediation) {
const { status, files } = await updateLockedDependency({
...upgrade,
depName,
newVersion,
currentVersion,
packageFile,
packageFileContent: packageFileContent,
lockFile,
lockFileContent: lockFileContent,
allowParentUpdates: true,
allowHigherOrRemoved: true,
});
if (reuseExistingBranch && status !== 'already-updated') {
logger_1.logger.debug({ lockFile, depName, status }, 'Need to retry branch as it is not already up-to-date');
return getUpdatedPackageFiles({
...config,
reuseExistingBranch: false,
});
}
if (files) {
updatedFileContents = { ...updatedFileContents, ...files };
Object.keys(files).forEach((file) => delete nonUpdatedFileContents[file]);
}
if (status === 'update-failed' || status === 'unsupported') {
upgrade.remediationNotPossible = true;
}
}
else if (upgrade.isLockfileUpdate) {
if (updateLockedDependency) {
const { status, files } = await updateLockedDependency({
...upgrade,
depName,
newVersion,
currentVersion,
packageFile,
packageFileContent: packageFileContent,
lockFile,
lockFileContent: lockFileContent,
allowParentUpdates: false,
});
if (status === 'unsupported') {
// incompatible lock file
if (!updatedFileContents[packageFile]) {
nonUpdatedFileContents[packageFile] = packageFileContent;
}
}
else if (status === 'already-updated') {
logger_1.logger.debug(`Upgrade of ${depName} to ${newVersion} is already done in existing branch`);
}
else {
// something changed
if (reuseExistingBranch) {
logger_1.logger.debug({ lockFile, depName, status }, 'Need to retry branch as upgrade requirements are not mets');
return getUpdatedPackageFiles({
...config,
reuseExistingBranch: false,
});
}
if (files) {
updatedFileContents = { ...updatedFileContents, ...files };
Object.keys(files).forEach((file) => delete nonUpdatedFileContents[file]);
}
}
}
else {
logger_1.logger.debug({ manager }, 'isLockFileUpdate without updateLockedDependency');
if (!updatedFileContents[packageFile]) {
nonUpdatedFileContents[packageFile] = packageFileContent;
}
}
}
else {
const updateDependency = (0, manager_1.get)(manager, 'updateDependency');
if (!updateDependency) {
let res = await (0, auto_replace_1.doAutoReplace)(upgrade, packageFileContent, reuseExistingBranch, firstUpdate);
firstUpdate = false;
if (res) {
res = await applyManagerBumpPackageVersion(res, upgrade);
if (res === packageFileContent) {
logger_1.logger.debug({ packageFile, depName }, 'No content changed');
}
else {
logger_1.logger.debug({ packageFile, depName }, 'Contents updated');
updatedFileContents[packageFile] = res;
delete nonUpdatedFileContents[packageFile];
}
continue;
}
else if (reuseExistingBranch) {
return getUpdatedPackageFiles({
...config,
reuseExistingBranch: false,
});
}
logger_1.logger.error({ packageFile, depName }, 'Could not autoReplace');
throw new Error(error_messages_1.WORKER_FILE_UPDATE_FAILED);
}
let newContent = await updateDependency({
fileContent: packageFileContent,
upgrade,
});
newContent = await applyManagerBumpPackageVersion(newContent, upgrade);
if (!newContent) {
if (reuseExistingBranch) {
logger_1.logger.debug({ packageFile, depName }, 'Rebasing branch after error updating content');
return getUpdatedPackageFiles({
...config,
reuseExistingBranch: false,
});
}
logger_1.logger.debug({ existingContent: packageFileContent, config: upgrade }, 'Error updating file');
throw new Error(error_messages_1.WORKER_FILE_UPDATE_FAILED);
}
if (newContent !== packageFileContent) {
if (reuseExistingBranch) {
// This ensure it's always 1 commit from the bot
logger_1.logger.debug({ packageFile, depName }, 'Need to update package file so will rebase first');
return getUpdatedPackageFiles({
...config,
reuseExistingBranch: false,
});
}
logger_1.logger.debug(`Updating ${depName} in ${(0, string_1.coerceString)(packageFile, lockFile)}`);
updatedFileContents[packageFile] = newContent;
delete nonUpdatedFileContents[packageFile];
}
if (newContent === packageFileContent) {
if (upgrade.manager === 'git-submodules') {
updatedFileContents[packageFile] = newContent;
delete nonUpdatedFileContents[packageFile];
}
}
}
}
const updatedPackageFiles = Object.keys(updatedFileContents).map((name) => ({
type: 'addition',
path: name,
contents: updatedFileContents[name],
}));
const updatedArtifacts = [];
const artifactErrors = [];
const artifactNotices = [];
if (is_1.default.nonEmptyArray(updatedPackageFiles)) {
logger_1.logger.debug('updateArtifacts for updatedPackageFiles');
const updatedPackageFileManagers = getManagersForPackageFiles(updatedPackageFiles, managerPackageFiles);
for (const manager of updatedPackageFileManagers) {
const packageFilesForManager = getPackageFilesForManager(updatedPackageFiles, managerPackageFiles[manager]);
sortPackageFiles(config, manager, packageFilesForManager);
for (const packageFile of packageFilesForManager) {
const updatedDeps = packageFileUpdatedDeps[packageFile.path];
const results = await managerUpdateArtifacts(manager, {
packageFileName: packageFile.path,
updatedDeps,
// TODO #22198
newPackageFileContent: packageFile.contents.toString(),
config: patchConfigForArtifactsUpdate(config, manager, packageFile.path),
});
processUpdateArtifactResults(results, updatedArtifacts, artifactErrors, artifactNotices);
}
}
}
const nonUpdatedPackageFiles = Object.keys(nonUpdatedFileContents).map((name) => ({
type: 'addition',
path: name,
contents: nonUpdatedFileContents[name],
}));
if (is_1.default.nonEmptyArray(nonUpdatedPackageFiles)) {
logger_1.logger.debug('updateArtifacts for nonUpdatedPackageFiles');
const nonUpdatedPackageFileManagers = getManagersForPackageFiles(nonUpdatedPackageFiles, managerPackageFiles);
for (const manager of nonUpdatedPackageFileManagers) {
const packageFilesForManager = getPackageFilesForManager(nonUpdatedPackageFiles, managerPackageFiles[manager]);
sortPackageFiles(config, manager, packageFilesForManager);
for (const packageFile of packageFilesForManager) {
const updatedDeps = packageFileUpdatedDeps[packageFile.path];
const results = await managerUpdateArtifacts(manager, {
packageFileName: packageFile.path,
updatedDeps,
// TODO #22198
newPackageFileContent: packageFile.contents.toString(),
config: patchConfigForArtifactsUpdate(config, manager, packageFile.path),
});
processUpdateArtifactResults(results, updatedArtifacts, artifactErrors, artifactNotices);
if (is_1.default.nonEmptyArray(results)) {
updatedPackageFiles.push(packageFile);
}
}
}
}
if (!reuseExistingBranch) {
const lockFileMaintenancePackageFiles = lockFileMaintenanceFiles.map((name) => ({
path: name,
}));
// Only perform lock file maintenance if it's a fresh commit
if (is_1.default.nonEmptyArray(lockFileMaintenanceFiles)) {
logger_1.logger.debug('updateArtifacts for lockFileMaintenanceFiles');
const lockFileMaintenanceManagers = getManagersForPackageFiles(lockFileMaintenancePackageFiles, managerPackageFiles);
for (const manager of lockFileMaintenanceManagers) {
const packageFilesForManager = getPackageFilesForManager(lockFileMaintenancePackageFiles, managerPackageFiles[manager]);
sortPackageFiles(config, manager, packageFilesForManager);
for (const packageFile of packageFilesForManager) {
const contents = updatedFileContents[packageFile.path] ||
(await (0, git_1.getFile)(packageFile.path, config.baseBranch));
const results = await managerUpdateArtifacts(manager, {
packageFileName: packageFile.path,
updatedDeps: [],
newPackageFileContent: contents,
config: patchConfigForArtifactsUpdate(config, manager, packageFile.path),
});
processUpdateArtifactResults(results, updatedArtifacts, artifactErrors, artifactNotices);
}
}
}
}
return {
reuseExistingBranch, // Need to overwrite original config
updatedPackageFiles,
updatedArtifacts,
artifactErrors,
artifactNotices,
};
}
// workaround, see #27319
function patchConfigForArtifactsUpdate(config, manager, packageFileName) {
// drop any lockFiles that happen to be defined on the branch config
const { lockFiles, ...updatedConfig } = config;
if (is_1.default.nonEmptyArray(updatedConfig.packageFiles?.[manager])) {
const managerPackageFiles = updatedConfig.packageFiles?.[manager];
const packageFile = managerPackageFiles.find((p) => p.packageFile === packageFileName);
if (packageFile && is_1.default.nonEmptyArray(packageFile.lockFiles)) {
updatedConfig.lockFiles = packageFile.lockFiles;
}
}
return updatedConfig;
}
async function managerUpdateArtifacts(manager, updateArtifact) {
const updateArtifacts = (0, manager_1.get)(manager, 'updateArtifacts');
if (!updateArtifacts) {
return null;
}
if (updateArtifact.config.skipArtifactsUpdate) {
logger_1.logger.debug({ manager, packageFileName: updateArtifact.packageFileName }, 'Skipping artifact update');
return null;
}
return await updateArtifacts(updateArtifact);
}
function processUpdateArtifactResults(results, updatedArtifacts, artifactErrors, artifactNotices) {
if (is_1.default.nonEmptyArray(results)) {
for (const res of results) {
const { file, notice, artifactError } = res;
if (file) {
updatedArtifacts.push(file);
}
if (artifactError) {
artifactErrors.push(artifactError);
}
if (notice) {
artifactNotices.push(notice);
}
}
}
}
async function applyManagerBumpPackageVersion(packageFileContent, upgrade) {
const bumpPackageVersion = (0, manager_1.get)(upgrade.manager, 'bumpPackageVersion');
if (!bumpPackageVersion ||
!packageFileContent ||
!upgrade.bumpVersion ||
!upgrade.packageFileVersion) {
return packageFileContent;
}
const result = await bumpPackageVersion(packageFileContent, upgrade.packageFileVersion, upgrade.bumpVersion, upgrade.packageFile);
return result.bumpedContent;
}
//# sourceMappingURL=get-updated.js.map