renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
150 lines • 7.41 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.pruneStaleBranches = pruneStaleBranches;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const global_1 = require("../../../config/global");
const error_messages_1 = require("../../../constants/error-messages");
const logger_1 = require("../../../logger");
const platform_1 = require("../../../modules/platform");
const comment_1 = require("../../../modules/platform/comment");
const scm_1 = require("../../../modules/platform/scm");
const git_1 = require("../../../util/git");
const regex_1 = require("../../../util/regex");
const string_1 = require("../../../util/string");
const utils_1 = require("../reconfigure/utils");
async function cleanUpBranches(config, remainingBranches) {
if (!config.pruneStaleBranches) {
logger_1.logger.debug('Branch/PR pruning is disabled - skipping');
return;
}
// set Git author in case the repository is not initialized yet
(0, git_1.setUserRepoConfig)(config);
// calculate regex to extract base branch from branch name
const baseBranchRe = calculateBaseBranchRegex(config);
for (const branchName of remainingBranches) {
try {
// get base branch from branch name if base branches are configured
// use default branch if no base branches are configured
// use defaul branch name if no match (can happen when base branches are configured later)
const baseBranch = baseBranchRe?.exec(branchName)?.[1] ?? config.defaultBranch;
const pr = await platform_1.platform.findPr({
branchName,
state: 'open',
targetBranch: baseBranch,
});
const branchIsModified = await scm_1.scm.isBranchModified(branchName, baseBranch);
if (pr) {
if (branchIsModified) {
logger_1.logger.debug({ prNo: pr.number, prTitle: pr.title }, 'Branch is modified - skipping PR autoclosing');
if (global_1.GlobalConfig.get('dryRun')) {
logger_1.logger.info(`DRY-RUN: Would update PR title and ensure comment.`);
}
else {
if (!pr.title.endsWith('- abandoned')) {
const newPrTitle = pr.title + ' - abandoned';
await platform_1.platform.updatePr({
number: pr.number,
prTitle: newPrTitle,
state: 'open',
});
}
await (0, comment_1.ensureComment)({
number: pr.number,
topic: 'Autoclosing Skipped',
content: 'This PR has been flagged for autoclosing. However, it is being skipped due to the branch being already modified. Please close/delete it manually or report a bug if you think this is in error.',
});
}
}
else if (global_1.GlobalConfig.get('dryRun')) {
logger_1.logger.info({ prNo: pr.number, prTitle: pr.title }, `DRY-RUN: Would autoclose PR`);
}
else {
logger_1.logger.info({ branchName, prNo: pr.number, prTitle: pr.title }, 'Autoclosing PR');
let newPrTitle = pr.title;
if (!pr.title.endsWith('- autoclosed')) {
newPrTitle += ' - autoclosed';
}
await platform_1.platform.updatePr({
number: pr.number,
prTitle: newPrTitle,
state: 'closed',
});
await scm_1.scm.deleteBranch(branchName);
}
}
else if (branchIsModified) {
logger_1.logger.debug('Orphan Branch is modified - skipping branch deletion');
}
else if (global_1.GlobalConfig.get('dryRun')) {
logger_1.logger.info(`DRY-RUN: Would delete orphan branch ${branchName}`);
}
else {
logger_1.logger.info({ branch: branchName }, `Deleting orphan branch`);
await scm_1.scm.deleteBranch(branchName);
}
}
catch (err) /* istanbul ignore next */ {
if (err.message === 'config-validation') {
logger_1.logger.debug('Cannot prune branch due to collision between tags and branch names');
}
else if (err.message?.includes("bad revision 'origin/")) {
logger_1.logger.debug({ branchName }, 'Branch not found on origin when attempting to prune');
}
else if (err.message !== error_messages_1.REPOSITORY_CHANGED) {
logger_1.logger.warn({ err, branch: branchName }, 'Error pruning branch');
}
}
}
}
/**
* Calculates a {RegExp} to extract the base branch from a branch name if base branches are configured.
* @param config Renovate configuration
*/
function calculateBaseBranchRegex(config) {
if (!config.baseBranches?.length) {
return null;
}
// calculate possible branch prefixes and escape for regex
const branchPrefixes = [config.branchPrefix, config.branchPrefixOld]
.filter(is_1.default.nonEmptyStringAndNotWhitespace)
.filter(string_1.uniqueStrings)
.map(regex_1.escapeRegExp);
// calculate possible base branches and escape for regex
const baseBranches = config.baseBranches.map(regex_1.escapeRegExp);
// create regex to extract base branche from branch name
const baseBranchRe = (0, regex_1.regEx)(`^(?:${branchPrefixes.join('|')})(${baseBranches.join('|')})-`);
return baseBranchRe;
}
async function pruneStaleBranches(config, branchList) {
logger_1.logger.debug('Removing any stale branches');
logger_1.logger.trace({ config }, `pruneStaleBranches`);
// TODO: types (#22198)
logger_1.logger.debug(`config.repoIsOnboarded=${config.repoIsOnboarded}`);
if (!branchList) {
logger_1.logger.debug('No branchList');
return;
}
// TODO: types (#22198)
let renovateBranches = (0, git_1.getBranchList)().filter((branchName) => branchName.startsWith(config.branchPrefix) &&
branchName !== (0, utils_1.getReconfigureBranchName)(config.branchPrefix));
if (!renovateBranches?.length) {
logger_1.logger.debug('No renovate branches found');
return;
}
logger_1.logger.debug({
branchList: branchList?.sort(),
renovateBranches: renovateBranches?.sort(),
}, 'Branch lists');
// TODO: types (#22198)
const lockFileBranch = `${config.branchPrefix}lock-file-maintenance`;
renovateBranches = renovateBranches.filter((branch) => branch !== lockFileBranch);
const remainingBranches = renovateBranches.filter((branch) => !branchList.includes(branch));
logger_1.logger.debug(`remainingBranches=${String(remainingBranches)}`);
if (remainingBranches.length === 0) {
logger_1.logger.debug('No branches to clean up');
return;
}
await cleanUpBranches(config, remainingBranches);
}
//# sourceMappingURL=prune.js.map