mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-11 22:46:27 +00:00
feat(self-hosted): autodiscoverRepoSort
and autodiscoverRepoOrder
(#28738)
Co-authored-by: Rhys Arkins <rhys@arkins.net>
This commit is contained in:
parent
3de9ac7e10
commit
10a4a8bb26
13 changed files with 124 additions and 58 deletions
|
@ -233,6 +233,18 @@ This feature is useful for users who want Renovate to only work on repositories
|
||||||
The `autodiscoverProjects` config option takes an array of minimatch-compatible globs or RE2-compatible regex strings.
|
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).
|
For more details on this syntax see Renovate's [string pattern matching documentation](./string-pattern-matching.md).
|
||||||
|
|
||||||
|
## autodiscoverRepoOrder
|
||||||
|
|
||||||
|
The order method for autodiscover server side repository search.
|
||||||
|
|
||||||
|
> If multiple `autodiscoverTopics` are used resulting order will be per topic not global.
|
||||||
|
|
||||||
|
## autodiscoverRepoSort
|
||||||
|
|
||||||
|
The sort method for autodiscover server side repository search.
|
||||||
|
|
||||||
|
> If multiple `autodiscoverTopics` are used resulting order will be per topic not global.
|
||||||
|
|
||||||
## autodiscoverTopics
|
## autodiscoverTopics
|
||||||
|
|
||||||
Some platforms allow you to add tags, or topics, to repositories and retrieve repository lists by specifying those
|
Some platforms allow you to add tags, or topics, to repositories and retrieve repository lists by specifying those
|
||||||
|
|
|
@ -32,43 +32,6 @@ Skipping the check will speed things up, but may result in versions being return
|
||||||
|
|
||||||
If set to any value, Renovate will always paginate requests to GitHub fully, instead of stopping after 10 pages.
|
If set to any value, Renovate will always paginate requests to GitHub fully, instead of stopping after 10 pages.
|
||||||
|
|
||||||
## `RENOVATE_X_AUTODISCOVER_REPO_ORDER`
|
|
||||||
|
|
||||||
<!-- prettier-ignore -->
|
|
||||||
!!! note
|
|
||||||
For the Forgejo and Gitea platform only.
|
|
||||||
|
|
||||||
The order method for autodiscover server side repository search.
|
|
||||||
|
|
||||||
> If multiple `autodiscoverTopics` are used resulting order will be per topic not global.
|
|
||||||
|
|
||||||
Allowed values:
|
|
||||||
|
|
||||||
- `asc`
|
|
||||||
- `desc`
|
|
||||||
|
|
||||||
Default value: `asc`.
|
|
||||||
|
|
||||||
## `RENOVATE_X_AUTODISCOVER_REPO_SORT`
|
|
||||||
|
|
||||||
<!-- prettier-ignore -->
|
|
||||||
!!! note
|
|
||||||
For the Forgejo and Gitea platform only.
|
|
||||||
|
|
||||||
The sort method for autodiscover server side repository search.
|
|
||||||
|
|
||||||
> If multiple `autodiscoverTopics` are used resulting order will be per topic not global.
|
|
||||||
|
|
||||||
Allowed values:
|
|
||||||
|
|
||||||
- `alpha`
|
|
||||||
- `created`
|
|
||||||
- `updated`
|
|
||||||
- `size`
|
|
||||||
- `id`
|
|
||||||
|
|
||||||
Default value: `alpha`.
|
|
||||||
|
|
||||||
## `RENOVATE_X_DELETE_CONFIG_FILE`
|
## `RENOVATE_X_DELETE_CONFIG_FILE`
|
||||||
|
|
||||||
If `true` Renovate tries to delete the self-hosted config file after reading it.
|
If `true` Renovate tries to delete the self-hosted config file after reading it.
|
||||||
|
|
|
@ -33,6 +33,8 @@ export class GlobalConfig {
|
||||||
'platform',
|
'platform',
|
||||||
'endpoint',
|
'endpoint',
|
||||||
'httpCacheTtlDays',
|
'httpCacheTtlDays',
|
||||||
|
'autodiscoverRepoSort',
|
||||||
|
'autodiscoverRepoOrder',
|
||||||
'userAgent',
|
'userAgent',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,26 @@ const options: RenovateOptions[] = [
|
||||||
globalOnly: true,
|
globalOnly: true,
|
||||||
patternMatch: true,
|
patternMatch: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'autodiscoverRepoOrder',
|
||||||
|
description:
|
||||||
|
'The order method for autodiscover server side repository search.',
|
||||||
|
type: 'string',
|
||||||
|
default: null,
|
||||||
|
globalOnly: true,
|
||||||
|
allowedValues: ['asc', 'desc'],
|
||||||
|
supportedPlatforms: ['gitea'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'autodiscoverRepoSort',
|
||||||
|
description:
|
||||||
|
'The sort method for autodiscover server side repository search.',
|
||||||
|
type: 'string',
|
||||||
|
default: null,
|
||||||
|
globalOnly: true,
|
||||||
|
allowedValues: ['alpha', 'created', 'updated', 'size', 'id'],
|
||||||
|
supportedPlatforms: ['gitea'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'allowedEnv',
|
name: 'allowedEnv',
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -2,6 +2,7 @@ import type { LogLevel } from 'bunyan';
|
||||||
import type { PlatformId } from '../constants';
|
import type { PlatformId } from '../constants';
|
||||||
import type { LogLevelRemap } from '../logger/types';
|
import type { LogLevelRemap } from '../logger/types';
|
||||||
import type { CustomManager } from '../modules/manager/custom/types';
|
import type { CustomManager } from '../modules/manager/custom/types';
|
||||||
|
import type { RepoSortMethod, SortMethod } from '../modules/platform/types';
|
||||||
import type { HostRule } from '../types';
|
import type { HostRule } from '../types';
|
||||||
import type { GitNoVerifyOption } from '../util/git/types';
|
import type { GitNoVerifyOption } from '../util/git/types';
|
||||||
import type { MergeConfidence } from '../util/merge-confidence/types';
|
import type { MergeConfidence } from '../util/merge-confidence/types';
|
||||||
|
@ -159,6 +160,8 @@ export interface RepoGlobalConfig {
|
||||||
privateKey?: string;
|
privateKey?: string;
|
||||||
privateKeyOld?: string;
|
privateKeyOld?: string;
|
||||||
httpCacheTtlDays?: number;
|
httpCacheTtlDays?: number;
|
||||||
|
autodiscoverRepoSort?: RepoSortMethod;
|
||||||
|
autodiscoverRepoOrder?: SortMethod;
|
||||||
userAgent?: string;
|
userAgent?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,9 +224,6 @@ describe('modules/platform/gitea/index', () => {
|
||||||
hostRules.clear();
|
hostRules.clear();
|
||||||
|
|
||||||
setBaseUrl('https://gitea.renovatebot.com/');
|
setBaseUrl('https://gitea.renovatebot.com/');
|
||||||
|
|
||||||
delete process.env.RENOVATE_X_AUTODISCOVER_REPO_SORT;
|
|
||||||
delete process.env.RENOVATE_X_AUTODISCOVER_REPO_ORDER;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function initFakePlatform(
|
async function initFakePlatform(
|
||||||
|
@ -421,8 +418,6 @@ describe('modules/platform/gitea/index', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Sorts repos', async () => {
|
it('Sorts repos', async () => {
|
||||||
process.env.RENOVATE_X_AUTODISCOVER_REPO_SORT = 'updated';
|
|
||||||
process.env.RENOVATE_X_AUTODISCOVER_REPO_ORDER = 'desc';
|
|
||||||
const scope = httpMock
|
const scope = httpMock
|
||||||
.scope('https://gitea.com/api/v1')
|
.scope('https://gitea.com/api/v1')
|
||||||
.get('/repos/search')
|
.get('/repos/search')
|
||||||
|
@ -438,7 +433,10 @@ describe('modules/platform/gitea/index', () => {
|
||||||
});
|
});
|
||||||
await initFakePlatform(scope);
|
await initFakePlatform(scope);
|
||||||
|
|
||||||
const repos = await gitea.getRepos();
|
const repos = await gitea.getRepos({
|
||||||
|
sort: 'updated',
|
||||||
|
order: 'desc',
|
||||||
|
});
|
||||||
expect(repos).toEqual(['a/b', 'c/d']);
|
expect(repos).toEqual(['a/b', 'c/d']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,6 +34,8 @@ import type {
|
||||||
Pr,
|
Pr,
|
||||||
RepoParams,
|
RepoParams,
|
||||||
RepoResult,
|
RepoResult,
|
||||||
|
RepoSortMethod,
|
||||||
|
SortMethod,
|
||||||
UpdatePrConfig,
|
UpdatePrConfig,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { repoFingerprint } from '../util';
|
import { repoFingerprint } from '../util';
|
||||||
|
@ -49,8 +51,6 @@ import type {
|
||||||
PRMergeMethod,
|
PRMergeMethod,
|
||||||
PRUpdateParams,
|
PRUpdateParams,
|
||||||
Repo,
|
Repo,
|
||||||
RepoSortMethod,
|
|
||||||
SortMethod,
|
|
||||||
} from './types';
|
} from './types';
|
||||||
import {
|
import {
|
||||||
DRAFT_PREFIX,
|
DRAFT_PREFIX,
|
||||||
|
@ -159,7 +159,17 @@ async function lookupLabelByName(name: string): Promise<number | null> {
|
||||||
return labelList.find((l) => l.name === name)?.id ?? null;
|
return labelList.find((l) => l.name === name)?.id ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchRepositories(topic?: string): Promise<string[]> {
|
interface FetchRepositoriesArgs {
|
||||||
|
topic?: string;
|
||||||
|
sort?: RepoSortMethod;
|
||||||
|
order?: SortMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchRepositories({
|
||||||
|
topic,
|
||||||
|
sort,
|
||||||
|
order,
|
||||||
|
}: FetchRepositoriesArgs): Promise<string[]> {
|
||||||
const repos = await helper.searchRepos({
|
const repos = await helper.searchRepos({
|
||||||
uid: botUserID,
|
uid: botUserID,
|
||||||
archived: false,
|
archived: false,
|
||||||
|
@ -167,11 +177,11 @@ async function fetchRepositories(topic?: string): Promise<string[]> {
|
||||||
topic: true,
|
topic: true,
|
||||||
q: topic,
|
q: topic,
|
||||||
}),
|
}),
|
||||||
...(process.env.RENOVATE_X_AUTODISCOVER_REPO_SORT && {
|
...(sort && {
|
||||||
sort: process.env.RENOVATE_X_AUTODISCOVER_REPO_SORT as RepoSortMethod,
|
sort,
|
||||||
}),
|
}),
|
||||||
...(process.env.RENOVATE_X_AUTODISCOVER_REPO_ORDER && {
|
...(order && {
|
||||||
order: process.env.RENOVATE_X_AUTODISCOVER_REPO_ORDER as SortMethod,
|
order,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
return repos.filter(usableRepo).map((r) => r.full_name);
|
return repos.filter(usableRepo).map((r) => r.full_name);
|
||||||
|
@ -330,7 +340,16 @@ const platform: Platform = {
|
||||||
try {
|
try {
|
||||||
if (config?.topics) {
|
if (config?.topics) {
|
||||||
logger.debug({ topics: config.topics }, 'Auto-discovering by topics');
|
logger.debug({ topics: config.topics }, 'Auto-discovering by topics');
|
||||||
const repos = await map(config.topics, fetchRepositories);
|
const fetchRepoArgs: FetchRepositoriesArgs[] = config.topics.map(
|
||||||
|
(topic) => {
|
||||||
|
return {
|
||||||
|
topic,
|
||||||
|
sort: config.sort,
|
||||||
|
order: config.order,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const repos = await map(fetchRepoArgs, fetchRepositories);
|
||||||
return deduplicateArray(repos.flat());
|
return deduplicateArray(repos.flat());
|
||||||
} else if (config?.namespaces) {
|
} else if (config?.namespaces) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
@ -348,7 +367,10 @@ const platform: Platform = {
|
||||||
);
|
);
|
||||||
return deduplicateArray(repos.flat());
|
return deduplicateArray(repos.flat());
|
||||||
} else {
|
} else {
|
||||||
return await fetchRepositories();
|
return await fetchRepositories({
|
||||||
|
sort: config?.sort,
|
||||||
|
order: config?.order,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error({ err }, 'Gitea getRepos() error');
|
logger.error({ err }, 'Gitea getRepos() error');
|
||||||
|
|
|
@ -48,5 +48,5 @@ Repositories are ignored when one of the following conditions is met:
|
||||||
- Pull requests are disabled for that repository
|
- Pull requests are disabled for that repository
|
||||||
|
|
||||||
You can change the default server-side sort method and order for autodiscover API.
|
You can change the default server-side sort method and order for autodiscover API.
|
||||||
Set those via [`RENOVATE_X_AUTODISCOVER_REPO_SORT`](../../../self-hosted-experimental.md#renovate_x_autodiscover_repo_sort) and [`RENOVATE_X_AUTODISCOVER_REPO_ORDER`](../../../self-hosted-experimental.md#renovate_x_autodiscover_repo_order).
|
Set those via [`autodiscoverRepoSort`](../../../self-hosted-configuration.md#autodiscoverRepoSort) and [`autodiscoverRepoOrder`](../../../self-hosted-configuration.md#autodiscoverRepoOrder).
|
||||||
Read the [Gitea swagger docs](https://try.gitea.io/api/swagger#/repository/repoSearch) for more details.
|
Read the [Gitea swagger docs](https://try.gitea.io/api/swagger#/repository/repoSearch) for more details.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { LongCommitSha } from '../../../util/git/types';
|
import type { LongCommitSha } from '../../../util/git/types';
|
||||||
import type { Pr } from '../types';
|
import type { Pr, RepoSortMethod, SortMethod } from '../types';
|
||||||
|
|
||||||
export interface PrReviewersParams {
|
export interface PrReviewersParams {
|
||||||
reviewers?: string[];
|
reviewers?: string[];
|
||||||
|
@ -147,10 +147,6 @@ export interface CombinedCommitStatus {
|
||||||
statuses: CommitStatus[];
|
statuses: CommitStatus[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RepoSortMethod = 'alpha' | 'created' | 'updated' | 'size' | 'id';
|
|
||||||
|
|
||||||
export type SortMethod = 'asc' | 'desc';
|
|
||||||
|
|
||||||
export interface RepoSearchParams {
|
export interface RepoSearchParams {
|
||||||
uid?: number;
|
uid?: number;
|
||||||
archived?: boolean;
|
archived?: boolean;
|
||||||
|
|
|
@ -201,8 +201,19 @@ export type EnsureCommentRemovalConfig =
|
||||||
|
|
||||||
export type EnsureIssueResult = 'updated' | 'created';
|
export type EnsureIssueResult = 'updated' | 'created';
|
||||||
|
|
||||||
|
export type RepoSortMethod =
|
||||||
|
| 'alpha'
|
||||||
|
| 'created'
|
||||||
|
| 'updated'
|
||||||
|
| 'size'
|
||||||
|
| 'id'
|
||||||
|
| null;
|
||||||
|
|
||||||
|
export type SortMethod = 'asc' | 'desc' | null;
|
||||||
export interface AutodiscoverConfig {
|
export interface AutodiscoverConfig {
|
||||||
topics?: string[];
|
topics?: string[];
|
||||||
|
sort?: RepoSortMethod;
|
||||||
|
order?: SortMethod;
|
||||||
includeMirrors?: boolean;
|
includeMirrors?: boolean;
|
||||||
namespaces?: string[];
|
namespaces?: string[];
|
||||||
projects?: string[];
|
projects?: string[];
|
||||||
|
|
|
@ -38,6 +38,8 @@ export async function autodiscoverRepositories(
|
||||||
// Autodiscover list of repositories
|
// Autodiscover list of repositories
|
||||||
let discovered = await platform.getRepos({
|
let discovered = await platform.getRepos({
|
||||||
topics: config.autodiscoverTopics,
|
topics: config.autodiscoverTopics,
|
||||||
|
sort: config.autodiscoverRepoSort,
|
||||||
|
order: config.autodiscoverRepoOrder,
|
||||||
includeMirrors: config.includeMirrors,
|
includeMirrors: config.includeMirrors,
|
||||||
namespaces: config.autodiscoverNamespaces,
|
namespaces: config.autodiscoverNamespaces,
|
||||||
projects: config.autodiscoverProjects,
|
projects: config.autodiscoverProjects,
|
||||||
|
|
|
@ -267,6 +267,18 @@ describe('workers/global/config/parse/env', () => {
|
||||||
expect(config.token).toBe('a');
|
expect(config.token).toBe('a');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('massages converted experimental env vars', async () => {
|
||||||
|
const envParam: NodeJS.ProcessEnv = {
|
||||||
|
RENOVATE_X_AUTODISCOVER_REPO_SORT: 'alpha',
|
||||||
|
RENOVATE_X_DOCKER_MAX_PAGES: '10',
|
||||||
|
RENOVATE_AUTODISCOVER_REPO_ORDER: 'desc',
|
||||||
|
};
|
||||||
|
const config = await env.getConfig(envParam);
|
||||||
|
expect(config.autodiscoverRepoSort).toBe('alpha');
|
||||||
|
expect(config.autodiscoverRepoOrder).toBe('desc');
|
||||||
|
expect(config.dockerMaxPages).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
describe('RENOVATE_CONFIG tests', () => {
|
describe('RENOVATE_CONFIG tests', () => {
|
||||||
let processExit: jest.SpyInstance<never, [code?: number]>;
|
let processExit: jest.SpyInstance<never, [code?: number]>;
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,30 @@ function massageEnvKeyValues(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const convertedExperimentalEnvVars = [
|
||||||
|
'RENOVATE_X_AUTODISCOVER_REPO_SORT',
|
||||||
|
'RENOVATE_X_AUTODISCOVER_REPO_ORDER',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Massages the experimental env vars which have been converted to config options
|
||||||
|
*
|
||||||
|
* e.g. RENOVATE_X_AUTODISCOVER_REPO_SORT -> RENOVATE_AUTODISCOVER_REPO_SORT
|
||||||
|
*/
|
||||||
|
function massageConvertedExperimentalVars(
|
||||||
|
env: NodeJS.ProcessEnv,
|
||||||
|
): NodeJS.ProcessEnv {
|
||||||
|
const result = { ...env };
|
||||||
|
for (const key of convertedExperimentalEnvVars) {
|
||||||
|
if (env[key] !== undefined) {
|
||||||
|
const newKey = key.replace('RENOVATE_X_', 'RENOVATE_');
|
||||||
|
result[newKey] = env[key];
|
||||||
|
delete result[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getConfig(
|
export async function getConfig(
|
||||||
inputEnv: NodeJS.ProcessEnv,
|
inputEnv: NodeJS.ProcessEnv,
|
||||||
): Promise<AllConfig> {
|
): Promise<AllConfig> {
|
||||||
|
@ -91,6 +115,7 @@ export async function getConfig(
|
||||||
env = renameEnvKeys(env);
|
env = renameEnvKeys(env);
|
||||||
// massage the values of migrated configuration keys
|
// massage the values of migrated configuration keys
|
||||||
env = massageEnvKeyValues(env);
|
env = massageEnvKeyValues(env);
|
||||||
|
env = massageConvertedExperimentalVars(env);
|
||||||
|
|
||||||
const options = getOptions();
|
const options = getOptions();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue