Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/usage/self-hosted-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,10 @@ In that case, Renovate searches the repository config file for any of these conf
If Renovate finds any of the above configurations, it continues initializing the repository.
If not, then Renovate skips the repository without cloning it.

A third use case is when `onboarding` is set to `false` and `requireConfig` is not `"optional"` or `"ignored"`.
In this scenario, if the config file is missing, Renovate will skip the repository without cloning it.
This is useful for large organizations with many repositories where only some have Renovate enabled.

## password

## persistRepoData
Expand Down
81 changes: 81 additions & 0 deletions lib/workers/repository/init/apis.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getConfig } from '../../../config/defaults';
import {
REPOSITORY_DISABLED,
REPOSITORY_FORKED,
REPOSITORY_NO_CONFIG,
} from '../../../constants/error-messages';
import { initApis } from './apis';
import { platform } from '~test/util';
Expand Down Expand Up @@ -220,5 +221,85 @@ describe('workers/repository/init/apis', () => {
}),
).rejects.toThrow(REPOSITORY_DISABLED);
});

it('skips without cloning when config is missing and onboarding is disabled', async () => {
platform.initRepo.mockResolvedValueOnce({
defaultBranch: 'master',
isFork: false,
repoFingerprint: '123',
});
platform.getJsonFile.mockResolvedValueOnce(null);
await expect(
initApis({
...config,
optimizeForDisabled: true,
onboarding: false,
}),
).rejects.toThrow(REPOSITORY_NO_CONFIG);
});

it('skips without cloning when config is missing, onboarding is disabled, and requireConfig is required', async () => {
platform.initRepo.mockResolvedValueOnce({
defaultBranch: 'master',
isFork: false,
repoFingerprint: '123',
});
platform.getJsonFile.mockResolvedValueOnce(null);
await expect(
initApis({
...config,
optimizeForDisabled: true,
onboarding: false,
requireConfig: 'required',
}),
).rejects.toThrow(REPOSITORY_NO_CONFIG);
});

it('continues when config is missing, onboarding is disabled, but requireConfig is optional', async () => {
platform.initRepo.mockResolvedValueOnce({
defaultBranch: 'master',
isFork: false,
repoFingerprint: '123',
});
platform.getJsonFile.mockResolvedValueOnce(null);
const workerPlatformConfig = await initApis({
...config,
optimizeForDisabled: true,
onboarding: false,
requireConfig: 'optional',
});
expect(workerPlatformConfig).toBeTruthy();
});

it('continues when config is missing, onboarding is disabled, but requireConfig is ignored', async () => {
platform.initRepo.mockResolvedValueOnce({
defaultBranch: 'master',
isFork: false,
repoFingerprint: '123',
});
platform.getJsonFile.mockResolvedValueOnce(null);
const workerPlatformConfig = await initApis({
...config,
optimizeForDisabled: true,
onboarding: false,
requireConfig: 'ignored',
});
expect(workerPlatformConfig).toBeTruthy();
});

it('continues when config exists even with onboarding disabled', async () => {
platform.initRepo.mockResolvedValueOnce({
defaultBranch: 'master',
isFork: false,
repoFingerprint: '123',
});
platform.getJsonFile.mockResolvedValueOnce({ extends: ['config:base'] });
const workerPlatformConfig = await initApis({
...config,
optimizeForDisabled: true,
onboarding: false,
});
expect(workerPlatformConfig).toBeTruthy();
});
});
});
14 changes: 14 additions & 0 deletions lib/workers/repository/init/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { RenovateConfig } from '../../../config/types';
import {
REPOSITORY_DISABLED_BY_CONFIG,
REPOSITORY_FORKED,
REPOSITORY_NO_CONFIG,
} from '../../../constants/error-messages';
import { logger } from '../../../logger';
import type { RepoParams, RepoResult } from '../../../modules/platform';
Expand Down Expand Up @@ -29,6 +30,19 @@ async function validateOptimizeForDisabled(
if (renovateConfig?.enabled === false) {
throw new Error(REPOSITORY_DISABLED_BY_CONFIG);
}
// If config file is missing, onboarding is disabled, and config is required,
// skip the repo early without cloning
if (
!renovateConfig &&
config.onboarding === false &&
config.requireConfig !== 'optional' &&
config.requireConfig !== 'ignored'
) {
logger.debug(
'Repository has no config and onboarding is disabled - skipping',
);
throw new Error(REPOSITORY_NO_CONFIG);
}
/*
* The following is to support a use case within Mend customers where:
* - Bot admins configure install the bot into every repo
Expand Down