diff --git a/lib/workers/repository/finalize/prune.spec.ts b/lib/workers/repository/finalize/prune.spec.ts index 496fc82ab426f2..4048a752231d7a 100644 --- a/lib/workers/repository/finalize/prune.spec.ts +++ b/lib/workers/repository/finalize/prune.spec.ts @@ -82,6 +82,38 @@ describe('workers/repository/finalize/prune', () => { expect(platform.updatePr).toHaveBeenCalledTimes(1); }); + it('deletes with base branches', async () => { + config.branchList = ['renovate/main-a']; + config.baseBranches = ['main', 'maint/v7']; + git.getBranchList.mockReturnValueOnce( + config.branchList.concat([ + 'renovate/main-b', + 'renovate/maint/v7-a', + 'renovate/maint/v7-b', + ]), + ); + scm.isBranchModified.mockResolvedValueOnce(true); + scm.isBranchModified.mockResolvedValueOnce(false); + scm.isBranchModified.mockResolvedValueOnce(true); + await cleanup.pruneStaleBranches(config, config.branchList); + expect(git.getBranchList).toHaveBeenCalledTimes(1); + expect(scm.deleteBranch).toHaveBeenCalledTimes(1); + expect(scm.deleteBranch).toHaveBeenCalledWith('renovate/maint/v7-a'); + expect(scm.isBranchModified).toHaveBeenCalledTimes(3); + expect(scm.isBranchModified).toHaveBeenCalledWith( + 'renovate/main-b', + 'main', + ); + expect(scm.isBranchModified).toHaveBeenCalledWith( + 'renovate/maint/v7-a', + 'maint/v7', + ); + expect(scm.isBranchModified).toHaveBeenCalledWith( + 'renovate/maint/v7-b', + 'maint/v7', + ); + }); + it('does nothing on dryRun', async () => { config.branchList = ['renovate/a', 'renovate/b']; GlobalConfig.set({ dryRun: 'full' }); diff --git a/lib/workers/repository/finalize/prune.ts b/lib/workers/repository/finalize/prune.ts index ae904b854bafc8..7d30c01f97a554 100644 --- a/lib/workers/repository/finalize/prune.ts +++ b/lib/workers/repository/finalize/prune.ts @@ -1,3 +1,4 @@ +import is from '@sindresorhus/is'; import { GlobalConfig } from '../../../config/global'; import type { RenovateConfig } from '../../../config/types'; import { REPOSITORY_CHANGED } from '../../../constants/error-messages'; @@ -6,6 +7,8 @@ import { platform } from '../../../modules/platform'; import { ensureComment } from '../../../modules/platform/comment'; import { scm } from '../../../modules/platform/scm'; import { getBranchList, setUserRepoConfig } from '../../../util/git'; +import { escapeRegExp, regEx } from '../../../util/regex'; +import { uniqueStrings } from '../../../util/string'; import { getReconfigureBranchName } from '../reconfigure'; async function cleanUpBranches( @@ -18,16 +21,25 @@ async function cleanUpBranches( } // set Git author in case the repository is not initialized yet 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.findPr({ branchName, state: 'open', - targetBranch: config.baseBranch, + targetBranch: baseBranch, }); const branchIsModified = await scm.isBranchModified( branchName, - config.defaultBranch!, + baseBranch, ); if (pr) { if (branchIsModified) { @@ -100,6 +112,32 @@ async function cleanUpBranches( } } +/** + * Calculates a {RegExp} to extract the base branch from a branch name if base branches are configured. + * @param config Renovate configuration + */ +function calculateBaseBranchRegex(config: RenovateConfig): RegExp | null { + if (!config.baseBranches?.length) { + return null; + } + + // calculate possible branch prefixes and escape for regex + const branchPrefixes = [config.branchPrefix, config.branchPrefixOld] + .filter(is.nonEmptyStringAndNotWhitespace) + .filter(uniqueStrings) + .map(escapeRegExp); + + // calculate possible base branches and escape for regex + const baseBranches = config.baseBranches.map(escapeRegExp); + + // create regex to extract base branche from branch name + const baseBranchRe = regEx( + `^(?:${branchPrefixes.join('|')})(${baseBranches.join('|')})-`, + ); + + return baseBranchRe; +} + export async function pruneStaleBranches( config: RenovateConfig, branchList: string[] | null | undefined,