Skip to content
Snippets Groups Projects
Commit 0a6d97f2 authored by Sebastian Poxhofer's avatar Sebastian Poxhofer Committed by Rhys Arkins
Browse files

feat(config)!: move autodiscovery filter from string to array (#16525)

Change type of autodiscoverFilter from string to array.

Closes #8763

BREAKING CHANGE: autodiscover filters can no longer include commas
parent 9e74ddc5
No related branches found
No related tags found
No related merge requests found
...@@ -85,11 +85,24 @@ You can limit which repositories Renovate can access by using the `autodiscoverF ...@@ -85,11 +85,24 @@ You can limit which repositories Renovate can access by using the `autodiscoverF
You can use this option to filter the list of repositories that the Renovate bot account can access through `autodiscover`. You can use this option to filter the list of repositories that the Renovate bot account can access through `autodiscover`.
It takes a [minimatch](https://www.npmjs.com/package/minimatch) glob-style or regex pattern. It takes a [minimatch](https://www.npmjs.com/package/minimatch) glob-style or regex pattern.
If you set multiple filters, then the matches of each filter are added to the overall result.
If you use an environment variable or the CLI to set the value for `autodiscoverFilter`, then commas `,` within filters are not supported.
Commas will be used as delimiter for a new filter.
```
# DO NOT use commas inside the filter if your are using env or cli variables to configure it.
RENOVATE_AUTODISCOVER_FILTER="/myapp/{readme.md,src/**}"
# in this example you can use regex instead
RENOVATE_AUTODISCOVER_FILTER="/myapp/(readme\.md|src/.*)/"
```
**Minimatch**: **Minimatch**:
```json ```json
{ {
"autodiscoverFilter": "project/*" "autodiscoverFilter": ["project/*"]
} }
``` ```
...@@ -99,15 +112,17 @@ All text inside the start and end `/` will be treated as a regular expression. ...@@ -99,15 +112,17 @@ All text inside the start and end `/` will be treated as a regular expression.
```json ```json
{ {
"autodiscoverFilter": "/project/.*/" "autodiscoverFilter": ["/project/.*/"]
} }
``` ```
You can negate the regex by putting a `!` in front: You can negate the regex by putting a `!` in front.
Only use a single negation and don't mix with other filters because all filters are combined with `or`.
If using negations, all repositories except those who match the regex are added to the result:
```json ```json
{ {
"autodiscoverFilter": "!/project/.*/" "autodiscoverFilter": ["!/project/.*/"]
} }
``` ```
......
...@@ -723,7 +723,9 @@ const options: RenovateOptions[] = [ ...@@ -723,7 +723,9 @@ const options: RenovateOptions[] = [
name: 'autodiscoverFilter', name: 'autodiscoverFilter',
description: 'Filter the list of autodiscovered repositories.', description: 'Filter the list of autodiscovered repositories.',
stage: 'global', stage: 'global',
type: 'string', type: 'array',
subType: 'string',
allowString: true,
default: null, default: null,
globalOnly: true, globalOnly: true,
}, },
......
...@@ -84,7 +84,7 @@ export interface RenovateSharedConfig { ...@@ -84,7 +84,7 @@ export interface RenovateSharedConfig {
// The below should contain config options where stage=global // The below should contain config options where stage=global
export interface GlobalOnlyConfig { export interface GlobalOnlyConfig {
autodiscover?: boolean; autodiscover?: boolean;
autodiscoverFilter?: string; autodiscoverFilter?: string[];
baseDir?: string; baseDir?: string;
cacheDir?: string; cacheDir?: string;
containerbaseDir?: string; containerbaseDir?: string;
......
...@@ -53,7 +53,7 @@ describe('workers/global/autodiscover', () => { ...@@ -53,7 +53,7 @@ describe('workers/global/autodiscover', () => {
it('filters autodiscovered github repos', async () => { it('filters autodiscovered github repos', async () => {
config.autodiscover = true; config.autodiscover = true;
config.autodiscoverFilter = 'project/re*'; config.autodiscoverFilter = ['project/re*'];
config.platform = PlatformId.Github; config.platform = PlatformId.Github;
hostRules.find = jest.fn(() => ({ hostRules.find = jest.fn(() => ({
token: 'abc', token: 'abc',
...@@ -67,7 +67,7 @@ describe('workers/global/autodiscover', () => { ...@@ -67,7 +67,7 @@ describe('workers/global/autodiscover', () => {
it('filters autodiscovered github repos but nothing matches', async () => { it('filters autodiscovered github repos but nothing matches', async () => {
config.autodiscover = true; config.autodiscover = true;
config.autodiscoverFilter = 'project/re*'; config.autodiscoverFilter = ['project/re*'];
config.platform = 'github'; config.platform = 'github';
hostRules.find = jest.fn(() => ({ hostRules.find = jest.fn(() => ({
token: 'abc', token: 'abc',
...@@ -81,7 +81,7 @@ describe('workers/global/autodiscover', () => { ...@@ -81,7 +81,7 @@ describe('workers/global/autodiscover', () => {
it('filters autodiscovered github repos with regex', async () => { it('filters autodiscovered github repos with regex', async () => {
config.autodiscover = true; config.autodiscover = true;
config.autodiscoverFilter = '/project/re*./'; config.autodiscoverFilter = ['/project/re*./'];
config.platform = PlatformId.Github; config.platform = PlatformId.Github;
hostRules.find = jest.fn(() => ({ hostRules.find = jest.fn(() => ({
token: 'abc', token: 'abc',
...@@ -95,7 +95,7 @@ describe('workers/global/autodiscover', () => { ...@@ -95,7 +95,7 @@ describe('workers/global/autodiscover', () => {
it('filters autodiscovered github repos with regex negation', async () => { it('filters autodiscovered github repos with regex negation', async () => {
config.autodiscover = true; config.autodiscover = true;
config.autodiscoverFilter = '!/project/re*./'; config.autodiscoverFilter = ['!/project/re*./'];
config.platform = PlatformId.Github; config.platform = PlatformId.Github;
hostRules.find = jest.fn(() => ({ hostRules.find = jest.fn(() => ({
token: 'abc', token: 'abc',
...@@ -109,7 +109,7 @@ describe('workers/global/autodiscover', () => { ...@@ -109,7 +109,7 @@ describe('workers/global/autodiscover', () => {
it('fail if regex pattern is not valid', async () => { it('fail if regex pattern is not valid', async () => {
config.autodiscover = true; config.autodiscover = true;
config.autodiscoverFilter = '/project/re**./'; config.autodiscoverFilter = ['/project/re**./'];
config.platform = PlatformId.Github; config.platform = PlatformId.Github;
hostRules.find = jest.fn(() => ({ hostRules.find = jest.fn(() => ({
token: 'abc', token: 'abc',
...@@ -119,4 +119,22 @@ describe('workers/global/autodiscover', () => { ...@@ -119,4 +119,22 @@ describe('workers/global/autodiscover', () => {
); );
await expect(autodiscoverRepositories(config)).rejects.toThrow(); await expect(autodiscoverRepositories(config)).rejects.toThrow();
}); });
it('filters autodiscovered github repos with multiple values', async () => {
config.autodiscover = true;
config.autodiscoverFilter = ['another-project/re*', 'department/dev/*'];
config.platform = 'github';
hostRules.find = jest.fn(() => ({
token: 'abc',
}));
const expectedRepositories = [
'another-project/repo',
'department/dev/aProject',
];
ghApi.getRepos = jest.fn(() =>
Promise.resolve(['another-project/another-repo', ...expectedRepositories])
);
const res = await autodiscoverRepositories(config);
expect(res.repositories).toEqual(expectedRepositories);
});
}); });
...@@ -30,20 +30,10 @@ export async function autodiscoverRepositories( ...@@ -30,20 +30,10 @@ export async function autodiscoverRepositories(
); );
return config; return config;
} }
if (config.autodiscoverFilter) { if (config.autodiscoverFilter) {
if (isConfigRegex(config.autodiscoverFilter)) { discovered = applyFilters(discovered, config.autodiscoverFilter);
const autodiscoveryPred = configRegexPredicate(config.autodiscoverFilter);
if (!autodiscoveryPred) {
throw new Error(
`Failed to parse regex pattern "${config.autodiscoverFilter}"`
);
}
discovered = discovered.filter(autodiscoveryPred);
} else {
discovered = discovered.filter(
minimatch.filter(config.autodiscoverFilter)
);
}
if (!discovered.length) { if (!discovered.length) {
// Soft fail (no error thrown) if no accessible repositories match the filter // Soft fail (no error thrown) if no accessible repositories match the filter
logger.debug('None of the discovered repositories matched the filter'); logger.debug('None of the discovered repositories matched the filter');
...@@ -54,6 +44,7 @@ export async function autodiscoverRepositories( ...@@ -54,6 +44,7 @@ export async function autodiscoverRepositories(
{ length: discovered.length, repositories: discovered }, { length: discovered.length, repositories: discovered },
`Autodiscovered repositories` `Autodiscovered repositories`
); );
// istanbul ignore if // istanbul ignore if
if (config.repositories?.length) { if (config.repositories?.length) {
logger.debug( logger.debug(
...@@ -80,3 +71,24 @@ export async function autodiscoverRepositories( ...@@ -80,3 +71,24 @@ export async function autodiscoverRepositories(
} }
return { ...config, repositories: discovered }; return { ...config, repositories: discovered };
} }
export function applyFilters(repos: string[], filters: string[]): string[] {
const matched = new Set<string>();
for (const filter of filters) {
let res: string[];
if (isConfigRegex(filter)) {
const autodiscoveryPred = configRegexPredicate(filter);
if (!autodiscoveryPred) {
throw new Error(`Failed to parse regex pattern "${filter}"`);
}
res = repos.filter(autodiscoveryPred);
} else {
res = repos.filter(minimatch.filter(filter));
}
for (const repository of res) {
matched.add(repository);
}
}
return [...matched];
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment