Skip to content

Commit

Permalink
feat: add support for RegEx patterns in baseBranches (#20503)
Browse files Browse the repository at this point in the history
Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
  • Loading branch information
Churro and secustor authored Feb 20, 2023
1 parent fa22f5c commit 27c46cc
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 2 deletions.
19 changes: 19 additions & 0 deletions docs/usage/configuration-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,29 @@ Renovate also allows users to explicitly configure `baseBranches`, e.g. for use

- You wish Renovate to process only a non-default branch, e.g. `dev`: `"baseBranches": ["dev"]`
- You have multiple release streams you need Renovate to keep up to date, e.g. in branches `main` and `next`: `"baseBranches": ["main", "next"]`
- You want to update your main branch and consistently named release branches, e.g. `main` and `release/<version>`: `"baseBranches": ["main", "/^release\\/.*/"]`

It's possible to add this setting into the `renovate.json` file as part of the "Configure Renovate" onboarding PR.
If so then Renovate will reflect this setting in its description and use package file contents from the custom base branch(es) instead of default.

`baseBranches` supports Regular Expressions that must begin and end with `/`, e.g.:

```json
{
"baseBranches": ["main", "/^release\\/.*/"]
}
```

You can negate the regex by prefixing it with `!`.
Only use a single negation and do not mix it with other branch names, since all branches are combined with `or`.
With a negation, all branches except those matching the regex will be added to the result:

```json
{
"baseBranches": ["!/^pre-release\\/.*/"]
}
```

<!-- prettier-ignore -->
!!! note
Do _not_ use the `baseBranches` config option when you've set a `forkToken`.
Expand Down
3 changes: 2 additions & 1 deletion lib/config/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -749,8 +749,9 @@ const options: RenovateOptions[] = [
{
name: 'baseBranches',
description:
'An array of one or more custom base branches to be processed. If left empty, the default branch will be chosen.',
'List of one or more custom base branches defined as exact strings and/or via regex expressions.',
type: 'array',
subType: 'string',
stage: 'package',
cli: false,
},
Expand Down
13 changes: 13 additions & 0 deletions lib/config/validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,19 @@ describe('config/validation', () => {
expect(errors).toMatchSnapshot();
});

it('catches invalid baseBranches regex', async () => {
const config = {
baseBranches: ['/***$}{]][/'],
};
const { errors } = await configValidation.validateConfig(config);
expect(errors).toEqual([
{
topic: 'Configuration Error',
message: 'Invalid regExp for baseBranches: `/***$}{]][/`',
},
]);
});

it('returns nested errors', async () => {
const config: RenovateConfig = {
foo: 1,
Expand Down
13 changes: 13 additions & 0 deletions lib/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,19 @@ export async function validateConfig(
}
}
}
if (key === 'baseBranches') {
for (const baseBranch of val as string[]) {
if (
isConfigRegex(baseBranch) &&
!configRegexPredicate(baseBranch)
) {
errors.push({
topic: 'Configuration Error',
message: `Invalid regExp for ${currentPath}: \`${baseBranch}\``,
});
}
}
}
if (
(selectors.includes(key) ||
key === 'matchCurrentVersion' ||
Expand Down
29 changes: 29 additions & 0 deletions lib/workers/repository/process/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
RenovateConfig,
getConfig,
git,
logger,
mocked,
platform,
} from '../../../../test/util';
Expand Down Expand Up @@ -120,5 +121,33 @@ describe('workers/repository/process/index', () => {
});
expect(lookup).toHaveBeenCalledTimes(0);
});

it('finds baseBranches via regular expressions', async () => {
extract.mockResolvedValue({} as never);
config.baseBranches = ['/^release\\/.*/', 'dev', '!/^pre-release\\/.*/'];
git.getBranchList.mockReturnValue([
'dev',
'pre-release/v0',
'release/v1',
'release/v2',
'some-other',
]);
git.branchExists.mockReturnValue(true);
const res = await extractDependencies(config);
expect(res).toStrictEqual({
branchList: [undefined, undefined, undefined, undefined],
branches: [undefined, undefined, undefined, undefined],
packageFiles: undefined,
});

expect(logger.logger.debug).toHaveBeenCalledWith(
{ baseBranches: ['release/v1', 'release/v2', 'dev', 'some-other'] },
'baseBranches'
);
expect(addMeta).toHaveBeenCalledWith({ baseBranch: 'release/v1' });
expect(addMeta).toHaveBeenCalledWith({ baseBranch: 'release/v2' });
expect(addMeta).toHaveBeenCalledWith({ baseBranch: 'dev' });
expect(addMeta).toHaveBeenCalledWith({ baseBranch: 'some-other' });
});
});
});
21 changes: 20 additions & 1 deletion lib/workers/repository/process/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import type { PackageFile } from '../../../modules/manager/types';
import { platform } from '../../../modules/platform';
import { getCache } from '../../../util/cache/repository';
import { clone } from '../../../util/clone';
import { branchExists } from '../../../util/git';
import { branchExists, getBranchList } from '../../../util/git';
import { configRegexPredicate } from '../../../util/regex';
import { addSplit } from '../../../util/split';
import type { BranchConfig } from '../../types';
import { readDashboardBody } from '../dependency-dashboard';
Expand Down Expand Up @@ -81,6 +82,23 @@ async function getBaseBranchConfig(
return baseBranchConfig;
}

function unfoldBaseBranches(baseBranches: string[]): string[] {
const unfoldedList: string[] = [];

const allBranches = getBranchList();
for (const baseBranch of baseBranches) {
const isAllowedPred = configRegexPredicate(baseBranch);
if (isAllowedPred) {
const matchingBranches = allBranches.filter(isAllowedPred);
unfoldedList.push(...matchingBranches);
} else {
unfoldedList.push(baseBranch);
}
}

return [...new Set(unfoldedList)];
}

export async function extractDependencies(
config: RenovateConfig
): Promise<ExtractResult> {
Expand All @@ -91,6 +109,7 @@ export async function extractDependencies(
packageFiles: null!,
};
if (config.baseBranches?.length) {
config.baseBranches = unfoldBaseBranches(config.baseBranches);
logger.debug({ baseBranches: config.baseBranches }, 'baseBranches');
const extracted: Record<string, Record<string, PackageFile[]>> = {};
for (const baseBranch of config.baseBranches) {
Expand Down

0 comments on commit 27c46cc

Please sign in to comment.