From 0a311c8dad9186d038b87d0eaabe45b3b0ac9d55 Mon Sep 17 00:00:00 2001 From: Anthony Roussel <anthony@roussel.dev> Date: Wed, 19 Jul 2023 08:50:34 +0200 Subject: [PATCH] feat(platform/github): autodiscover repos by topic (#23362) --- docs/usage/self-hosted-configuration.md | 8 ++++++ lib/config/options/index.ts | 4 +-- lib/modules/platform/github/index.spec.ts | 27 ++++++++++++++++++++ lib/modules/platform/github/index.ts | 31 +++++++++++++++-------- lib/modules/platform/github/types.ts | 1 + 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md index 4757232ff2..a4dc8516a9 100644 --- a/docs/usage/self-hosted-configuration.md +++ b/docs/usage/self-hosted-configuration.md @@ -130,6 +130,14 @@ If using negations, all repositories except those who match the regex are added Some platforms allow you to add tags, or topics, to repositories and retrieve repository lists by specifying those topics. Set this variable to a list of strings, all of which will be topics for the autodiscovered repositories. +For example: + +```json +{ + "autodiscoverTopics": ["managed-by-renovate"] +} +``` + ## baseDir By default Renovate uses a temporary directory like `/tmp/renovate` to store its data. diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index e2500a1c11..9a14ef74fc 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -800,13 +800,13 @@ const options: RenovateOptions[] = [ }, { name: 'autodiscoverTopics', - description: '', + description: 'Filter the list of autodiscovered repositories by topics.', stage: 'global', type: 'array', subType: 'string', default: null, globalOnly: true, - supportedPlatforms: ['gitlab'], + supportedPlatforms: ['github', 'gitlab'], }, { name: 'prCommitsPerRunLimit', diff --git a/lib/modules/platform/github/index.spec.ts b/lib/modules/platform/github/index.spec.ts index 109c177e7a..23ce1e3f27 100644 --- a/lib/modules/platform/github/index.spec.ts +++ b/lib/modules/platform/github/index.spec.ts @@ -192,6 +192,33 @@ describe('modules/platform/github/index', () => { expect(repos).toMatchSnapshot(); }); + it('should filters repositories by topics', async () => { + httpMock + .scope(githubApiHost) + .get('/user/repos?per_page=100') + .reply(200, [ + { + full_name: 'a/b', + archived: false, + topics: [], + }, + { + full_name: 'c/d', + archived: false, + topics: ['managed-by-renovate'], + }, + { + full_name: 'e/f', + archived: true, + topics: ['managed-by-renovate'], + }, + null, + ]); + + const repos = await github.getRepos({ topics: ['managed-by-renovate'] }); + expect(repos).toEqual(['c/d']); + }); + it('should return an array of repos when using Github App endpoint', async () => { //Use Github App token await github.initPlatform({ diff --git a/lib/modules/platform/github/index.ts b/lib/modules/platform/github/index.ts index 84795983f8..d0b85a6fc5 100644 --- a/lib/modules/platform/github/index.ts +++ b/lib/modules/platform/github/index.ts @@ -42,6 +42,7 @@ import { fromBase64, looseEquals } from '../../../util/string'; import { ensureTrailingSlash } from '../../../util/url'; import type { AggregatedVulnerabilities, + AutodiscoverConfig, BranchStatusConfig, CreatePRConfig, EnsureCommentConfig, @@ -187,9 +188,7 @@ export async function initPlatform({ return platformResult; } -// Get all repositories that the user has access to -export async function getRepos(): Promise<string[]> { - logger.debug('Autodiscovering GitHub repositories'); +async function fetchRepositories(): Promise<GhRestRepo[]> { try { if (platformConfig.isGHApp) { const res = await githubApi.getJson<{ @@ -198,19 +197,13 @@ export async function getRepos(): Promise<string[]> { paginationField: 'repositories', paginate: 'all', }); - return res.body.repositories - .filter(is.nonEmptyObject) - .filter((repo) => !repo.archived) - .map((repo) => repo.full_name); + return res.body.repositories; } else { const res = await githubApi.getJson<GhRestRepo[]>( `user/repos?per_page=100`, { paginate: 'all' } ); - return res.body - .filter(is.nonEmptyObject) - .filter((repo) => !repo.archived) - .map((repo) => repo.full_name); + return res.body; } } catch (err) /* istanbul ignore next */ { logger.error({ err }, `GitHub getRepos error`); @@ -218,6 +211,22 @@ export async function getRepos(): Promise<string[]> { } } +// Get all repositories that the user has access to +export async function getRepos(config?: AutodiscoverConfig): Promise<string[]> { + logger.debug('Autodiscovering GitHub repositories'); + return (await fetchRepositories()) + .filter(is.nonEmptyObject) + .filter((repo) => !repo.archived) + .filter((repo) => { + if (config?.topics) { + const autodiscoverTopics = config.topics; + return repo.topics.some((topic) => autodiscoverTopics.includes(topic)); + } + return true; + }) + .map((repo) => repo.full_name); +} + async function getBranchProtection( branchName: string ): Promise<BranchProtection> { diff --git a/lib/modules/platform/github/types.ts b/lib/modules/platform/github/types.ts index e285ea5a23..b8baebf8fa 100644 --- a/lib/modules/platform/github/types.ts +++ b/lib/modules/platform/github/types.ts @@ -27,6 +27,7 @@ export interface GhRestRepo { login: string; }; archived: boolean; + topics: string[]; } export interface GhRestPr { -- GitLab