renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
207 lines • 11.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.postUpgradeCommandsExecutor = postUpgradeCommandsExecutor;
exports.default = executePostUpgradeCommands;
const tslib_1 = require("tslib");
const crypto_1 = tslib_1.__importDefault(require("crypto"));
// TODO #22198
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const upath_1 = tslib_1.__importDefault(require("upath"));
const config_1 = require("../../../../config");
const global_1 = require("../../../../config/global");
const logger_1 = require("../../../../logger");
const array_1 = require("../../../../util/array");
const exec_1 = require("../../../../util/exec");
const fs_1 = require("../../../../util/fs");
const git_1 = require("../../../../util/git");
const minimatch_1 = require("../../../../util/minimatch");
const regex_1 = require("../../../../util/regex");
const sanitize_1 = require("../../../../util/sanitize");
const template_1 = require("../../../../util/template");
async function postUpgradeCommandsExecutor(filteredUpgradeCommands, config) {
let updatedArtifacts = [...(config.updatedArtifacts ?? [])];
const artifactErrors = [...(config.artifactErrors ?? [])];
const allowedCommands = global_1.GlobalConfig.get('allowedCommands');
for (const upgrade of filteredUpgradeCommands) {
(0, logger_1.addMeta)({ dep: upgrade.depName });
logger_1.logger.trace({
tasks: upgrade.postUpgradeTasks,
allowedCommands,
}, `Checking for post-upgrade tasks`);
const commands = upgrade.postUpgradeTasks?.commands;
const dataFileTemplate = upgrade.postUpgradeTasks?.dataFileTemplate;
const fileFilters = upgrade.postUpgradeTasks?.fileFilters ?? ['**/*'];
if (is_1.default.nonEmptyArray(commands)) {
// Persist updated files in file system so any executed commands can see them
const previouslyModifiedFiles = config.updatedPackageFiles.concat(updatedArtifacts);
for (const file of previouslyModifiedFiles) {
const canWriteFile = await (0, fs_1.localPathIsFile)(file.path);
if (file.type === 'addition' && !file.isSymlink && canWriteFile) {
let contents;
if (typeof file.contents === 'string') {
contents = Buffer.from(file.contents);
}
else {
contents = file.contents;
}
// TODO #22198
await (0, fs_1.writeLocalFile)(file.path, contents);
}
}
let dataFilePath = null;
if (dataFileTemplate) {
const dataFileContent = (0, sanitize_1.sanitize)((0, template_1.compile)(dataFileTemplate, (0, config_1.mergeChildConfig)(config, upgrade)));
logger_1.logger.debug({ dataFileTemplate }, 'Processed post-upgrade commands data file template.');
const dataFileName = `post-upgrade-data-file-${crypto_1.default.randomBytes(8).toString('hex')}.tmp`;
dataFilePath = upath_1.default.join((0, fs_1.privateCacheDir)(), dataFileName);
try {
await (0, fs_1.outputCacheFile)(dataFilePath, dataFileContent);
logger_1.logger.debug({ dataFilePath, dataFileContent }, 'Created post-upgrade commands data file.');
}
catch (error) {
artifactErrors.push({
stderr: (0, sanitize_1.sanitize)(`Failed to create post-upgrade commands data file at ${dataFilePath}, reason: ${error.message}`),
});
dataFilePath = null;
}
}
for (const cmd of commands) {
const compiledCmd = (0, template_1.compile)(cmd, (0, config_1.mergeChildConfig)(config, upgrade));
if (compiledCmd !== cmd) {
logger_1.logger.debug({ rawCmd: cmd, compiledCmd }, 'Post-upgrade command has been compiled');
}
if (allowedCommands.some((pattern) => (0, regex_1.regEx)(pattern).test(compiledCmd))) {
try {
logger_1.logger.trace({ cmd: compiledCmd }, 'Executing post-upgrade task');
const execOpts = {
cwd: global_1.GlobalConfig.get('localDir'),
};
if (dataFilePath) {
execOpts.env = {
RENOVATE_POST_UPGRADE_COMMAND_DATA_FILE: dataFilePath,
};
}
const execResult = await (0, exec_1.exec)(compiledCmd, execOpts);
logger_1.logger.debug({ cmd: compiledCmd, ...execResult }, 'Executed post-upgrade task');
}
catch (error) {
artifactErrors.push({
lockFile: upgrade.packageFile,
stderr: (0, sanitize_1.sanitize)(error.message),
});
}
}
else {
logger_1.logger.warn({
cmd: compiledCmd,
allowedCommands,
}, 'Post-upgrade task did not match any on allowedCommands list');
artifactErrors.push({
lockFile: upgrade.packageFile,
stderr: (0, sanitize_1.sanitize)(`Post-upgrade command '${compiledCmd}' has not been added to the allowed list in allowedCommands`),
});
}
}
const status = await (0, git_1.getRepoStatus)();
logger_1.logger.trace({ status }, 'git status after post-upgrade tasks');
logger_1.logger.debug({
addedCount: status.not_added?.length,
modifiedCount: status.modified?.length,
deletedCount: status.deleted?.length,
renamedCount: status.renamed?.length,
}, 'git status counts after post-upgrade tasks');
const addedOrModifiedFiles = [
...(0, array_1.coerceArray)(status.not_added),
...(0, array_1.coerceArray)(status.modified),
...(0, array_1.coerceArray)(status.renamed?.map((x) => x.to)),
];
const changedFiles = [
...addedOrModifiedFiles,
...(0, array_1.coerceArray)(status.deleted),
...(0, array_1.coerceArray)(status.renamed?.map((x) => x.from)),
];
// Check for files which were previously deleted but have been re-added without modification
const previouslyDeletedFiles = updatedArtifacts.filter((ua) => ua.type === 'deletion');
for (const previouslyDeletedFile of previouslyDeletedFiles) {
if (!changedFiles.includes(previouslyDeletedFile.path)) {
logger_1.logger.debug({ file: previouslyDeletedFile.path }, 'Previously deleted file has been restored without modification');
updatedArtifacts = updatedArtifacts.filter((ua) => !(ua.type === 'deletion' && ua.path === previouslyDeletedFile.path));
}
}
logger_1.logger.trace({ addedOrModifiedFiles }, 'Added or modified files');
logger_1.logger.debug(`Checking ${addedOrModifiedFiles.length} added or modified files for post-upgrade changes`);
for (const relativePath of addedOrModifiedFiles) {
let fileMatched = false;
for (const pattern of fileFilters) {
if ((0, minimatch_1.minimatch)(pattern, { dot: true }).match(relativePath)) {
fileMatched = true;
logger_1.logger.debug({ file: relativePath, pattern }, 'Post-upgrade file saved');
const existingContent = await (0, fs_1.readLocalFile)(relativePath);
const existingUpdatedArtifacts = updatedArtifacts.find((ua) => ua.path === relativePath);
if (existingUpdatedArtifacts?.type === 'addition') {
existingUpdatedArtifacts.contents = existingContent;
}
else {
updatedArtifacts.push({
type: 'addition',
path: relativePath,
contents: existingContent,
});
}
// If the file is deleted by a previous post-update command, remove the deletion from updatedArtifacts
updatedArtifacts = updatedArtifacts.filter((ua) => !(ua.type === 'deletion' && ua.path === relativePath));
}
}
if (!fileMatched) {
logger_1.logger.debug({ file: relativePath }, 'Post-upgrade file did not match any file filters');
}
}
for (const relativePath of (0, array_1.coerceArray)(status.deleted)) {
for (const pattern of fileFilters) {
if ((0, minimatch_1.minimatch)(pattern, { dot: true }).match(relativePath)) {
if (!updatedArtifacts.some((ua) => ua.path === relativePath && ua.type === 'deletion')) {
logger_1.logger.debug({ file: relativePath, pattern }, 'Post-upgrade file removed');
updatedArtifacts.push({
type: 'deletion',
path: relativePath,
});
}
// If the file is created or modified by a previous post-update command, remove the modification from updatedArtifacts
updatedArtifacts = updatedArtifacts.filter((ua) => !(ua.type === 'addition' && ua.path === relativePath));
}
}
}
}
}
return { updatedArtifacts, artifactErrors };
}
async function executePostUpgradeCommands(config) {
const hasChangedFiles = (is_1.default.array(config.updatedPackageFiles) &&
config.updatedPackageFiles.length > 0) ||
(is_1.default.array(config.updatedArtifacts) && config.updatedArtifacts.length > 0);
if (!hasChangedFiles) {
/* Only run post-upgrade tasks if there are changes to package files... */
logger_1.logger.debug('No changes to package files, skipping post-upgrade tasks');
return null;
}
const branchUpgradeCommands = [
{
manager: config.manager,
depName: config.upgrades.map(({ depName }) => depName).join(' '),
branchName: config.branchName,
postUpgradeTasks: config.postUpgradeTasks.executionMode === 'branch'
? config.postUpgradeTasks
: undefined,
fileFilters: config.fileFilters,
},
];
const updateUpgradeCommands = config.upgrades.filter(({ postUpgradeTasks }) => !postUpgradeTasks?.executionMode ||
postUpgradeTasks.executionMode === 'update');
const { updatedArtifacts, artifactErrors } = await postUpgradeCommandsExecutor(updateUpgradeCommands, config);
return postUpgradeCommandsExecutor(branchUpgradeCommands, {
...config,
updatedArtifacts,
artifactErrors,
});
}
//# sourceMappingURL=execute-post-upgrade-commands.js.map