Skip to content

Commit

Permalink
feat(platform/bitbucket): autodiscoverProjects (#27845)
Browse files Browse the repository at this point in the history
Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
  • Loading branch information
RahulGautamSingh and HonkingGoose committed Mar 14, 2024
1 parent 9c06813 commit 11b3c59
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 4 deletions.
15 changes: 15 additions & 0 deletions docs/usage/self-hosted-configuration.md
Expand Up @@ -213,6 +213,21 @@ For example:
}
```

## autodiscoverProjects

You can use this option to filter the list of autodiscovered repositories by project names.
This feature is useful for users who want Renovate to only work on repositories within specific projects or exclude certain repositories from being processed.

```json title="Example for Bitbucket"
{
"platform": "bitbucket",
"autodiscoverProjects": ["a-group", "!another-group/some-subgroup"]
}
```

The `autodiscoverProjects` config option takes an array of minimatch-compatible globs or RE2-compatible regex strings.
For more details on this syntax see Renovate's [string pattern matching documentation](./string-pattern-matching.md).

## autodiscoverTopics

Some platforms allow you to add tags, or topics, to repositories and retrieve repository lists by specifying those
Expand Down
11 changes: 11 additions & 0 deletions lib/config/options/index.ts
Expand Up @@ -882,6 +882,17 @@ const options: RenovateOptions[] = [
globalOnly: true,
supportedPlatforms: ['gitlab'],
},
{
name: 'autodiscoverProjects',
description:
'Filter the list of autodiscovered repositories by project names.',
stage: 'global',
type: 'array',
subType: 'string',
default: null,
globalOnly: true,
supportedPlatforms: ['bitbucket'],
},
{
name: 'autodiscoverTopics',
description: 'Filter the list of autodiscovered repositories by topics.',
Expand Down
1 change: 1 addition & 0 deletions lib/config/types.ts
Expand Up @@ -100,6 +100,7 @@ export interface GlobalOnlyConfig {
autodiscover?: boolean;
autodiscoverFilter?: string[] | string;
autodiscoverNamespaces?: string[];
autodiscoverProjects?: string[];
autodiscoverTopics?: string[];
baseDir?: string;
cacheDir?: string;
Expand Down
30 changes: 29 additions & 1 deletion lib/modules/platform/bitbucket/index.spec.ts
Expand Up @@ -133,9 +133,37 @@ describe('modules/platform/bitbucket/index', () => {
.reply(200, {
values: [{ full_name: 'foo/bar' }, { full_name: 'some/repo' }],
});
const res = await bitbucket.getRepos();
const res = await bitbucket.getRepos({});
expect(res).toEqual(['foo/bar', 'some/repo']);
});

it('filters repos based on autodiscoverProjects patterns', async () => {
httpMock
.scope(baseUrl)
.get('/2.0/repositories?role=contributor&pagelen=100')
.reply(200, {
values: [
{ full_name: 'foo/bar', project: { name: 'ignore' } },
{ full_name: 'some/repo', project: { name: 'allow' } },
],
});
const res = await bitbucket.getRepos({ projects: ['allow'] });
expect(res).toEqual(['some/repo']);
});

it('filters repos based on autodiscoverProjects patterns with negation', async () => {
httpMock
.scope(baseUrl)
.get('/2.0/repositories?role=contributor&pagelen=100')
.reply(200, {
values: [
{ full_name: 'foo/bar', project: { name: 'ignore' } },
{ full_name: 'some/repo', project: { name: 'allow' } },
],
});
const res = await bitbucket.getRepos({ projects: ['!ignore'] });
expect(res).toEqual(['some/repo']);
});
});

describe('initRepo()', () => {
Expand Down
21 changes: 18 additions & 3 deletions lib/modules/platform/bitbucket/index.ts
Expand Up @@ -10,8 +10,9 @@ import { BitbucketHttp, setBaseUrl } from '../../../util/http/bitbucket';
import type { HttpOptions } from '../../../util/http/types';
import { regEx } from '../../../util/regex';
import { sanitize } from '../../../util/sanitize';
import { UUIDRegex } from '../../../util/string-match';
import { UUIDRegex, matchRegexOrGlobList } from '../../../util/string-match';
import type {
AutodiscoverConfig,
BranchStatusConfig,
CreatePRConfig,
EnsureCommentConfig,
Expand Down Expand Up @@ -113,17 +114,31 @@ export async function initPlatform({
}

// Get all repositories that the user has access to
export async function getRepos(): Promise<string[]> {
export async function getRepos(config: AutodiscoverConfig): Promise<string[]> {
logger.debug('Autodiscovering Bitbucket Cloud repositories');
try {
const repos = (
let repos = (
await bitbucketHttp.getJson<PagedResult<RepoInfoBody>>(
`/2.0/repositories/?role=contributor`,
{
paginate: true,
},
)
).body.values;

// if autodiscoverProjects is configured
// filter the repos list
const autodiscoverProjects = config.projects;
if (is.nonEmptyArray(autodiscoverProjects)) {
logger.debug(
{ autodiscoverProjects: config.projects },
'Applying autodiscoverProjects filter',
);
repos = repos.filter((repo) =>
matchRegexOrGlobList(repo.project.name, autodiscoverProjects),
);
}

return repos.map((repo) => repo.full_name);
} catch (err) /* istanbul ignore next */ {
logger.error({ err }, `bitbucket getRepos error`);
Expand Down
3 changes: 3 additions & 0 deletions lib/modules/platform/bitbucket/types.ts
Expand Up @@ -66,6 +66,9 @@ export interface RepoInfoBody {
uuid: string;
full_name: string;
is_private: boolean;
project: {
name: string;
};
}

export interface PrResponse {
Expand Down
1 change: 1 addition & 0 deletions lib/modules/platform/types.ts
Expand Up @@ -178,6 +178,7 @@ export interface AutodiscoverConfig {
topics?: string[];
includeMirrors?: boolean;
namespaces?: string[];
projects?: string[];
}

export interface Platform {
Expand Down
1 change: 1 addition & 0 deletions lib/workers/global/autodiscover.ts
Expand Up @@ -40,6 +40,7 @@ export async function autodiscoverRepositories(
topics: config.autodiscoverTopics,
includeMirrors: config.includeMirrors,
namespaces: config.autodiscoverNamespaces,
projects: config.autodiscoverProjects,
});
if (!discovered?.length) {
// Soft fail (no error thrown) if no accessible repositories
Expand Down

0 comments on commit 11b3c59

Please sign in to comment.