mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-12 06:56:24 +00:00
feat(config): custom status checks (#26047)
Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> Co-authored-by: Rhys Arkins <rhys@arkins.net>
This commit is contained in:
parent
376fefd159
commit
3ed295cf94
11 changed files with 350 additions and 46 deletions
|
@ -3584,6 +3584,23 @@ Configure this to `true` if you wish to get one PR for every separate major vers
|
||||||
e.g. if you are on webpack@v1 currently then default behavior is a PR for upgrading to webpack@v3 and not for webpack@v2.
|
e.g. if you are on webpack@v1 currently then default behavior is a PR for upgrading to webpack@v3 and not for webpack@v2.
|
||||||
If this setting is true then you would get one PR for webpack@v2 and one for webpack@v3.
|
If this setting is true then you would get one PR for webpack@v2 and one for webpack@v3.
|
||||||
|
|
||||||
|
## statusCheckNames
|
||||||
|
|
||||||
|
You can customize the name/context of status checks that Renovate adds to commits/branches/PRs.
|
||||||
|
|
||||||
|
This option enables you to modify any existing status checks name/context, but adding new status checks this way is _not_ supported.
|
||||||
|
Setting the value to `null` or an empty string, effectively disables or skips that status check.
|
||||||
|
This option is mergeable, which means you only have to specify the status checks that you want to modify.
|
||||||
|
|
||||||
|
```json title="Example of overriding status check strings"
|
||||||
|
{
|
||||||
|
"statusCheckNames": {
|
||||||
|
"minimumReleaseAge": "custom/stability-days",
|
||||||
|
"mergeConfidence": "custom/merge-confidence-level"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## stopUpdatingLabel
|
## stopUpdatingLabel
|
||||||
|
|
||||||
This feature only works on supported platforms, check the table above.
|
This feature only works on supported platforms, check the table above.
|
||||||
|
|
|
@ -170,6 +170,19 @@ const options: RenovateOptions[] = [
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'statusCheckNames',
|
||||||
|
description: 'Custom strings to use as status check names.',
|
||||||
|
type: 'object',
|
||||||
|
mergeable: true,
|
||||||
|
advancedUse: true,
|
||||||
|
default: {
|
||||||
|
artifactError: 'renovate/artifacts',
|
||||||
|
configValidation: 'renovate/config-validation',
|
||||||
|
mergeConfidence: 'renovate/merge-confidence',
|
||||||
|
minimumReleaseAge: 'renovate/stability-days',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'extends',
|
name: 'extends',
|
||||||
description: 'Configuration presets to use or extend.',
|
description: 'Configuration presets to use or extend.',
|
||||||
|
|
|
@ -190,6 +190,14 @@ export type RenovateRepository =
|
||||||
export type UseBaseBranchConfigType = 'merge' | 'none';
|
export type UseBaseBranchConfigType = 'merge' | 'none';
|
||||||
export type ConstraintsFilter = 'strict' | 'none';
|
export type ConstraintsFilter = 'strict' | 'none';
|
||||||
|
|
||||||
|
export const allowedStatusCheckStrings = [
|
||||||
|
'minimumReleaseAge',
|
||||||
|
'mergeConfidence',
|
||||||
|
'configValidation',
|
||||||
|
'artifactError',
|
||||||
|
] as const;
|
||||||
|
export type StatusCheckKey = (typeof allowedStatusCheckStrings)[number];
|
||||||
|
|
||||||
// TODO: Proper typings
|
// TODO: Proper typings
|
||||||
export interface RenovateConfig
|
export interface RenovateConfig
|
||||||
extends LegacyAdminConfig,
|
extends LegacyAdminConfig,
|
||||||
|
@ -261,6 +269,8 @@ export interface RenovateConfig
|
||||||
|
|
||||||
checkedBranches?: string[];
|
checkedBranches?: string[];
|
||||||
customizeDashboard?: Record<string, string>;
|
customizeDashboard?: Record<string, string>;
|
||||||
|
|
||||||
|
statusCheckNames?: Record<StatusCheckKey, string | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomDatasourceFormats = ['json', 'plain', 'yaml', 'html'] as const;
|
const CustomDatasourceFormats = ['json', 'plain', 'yaml', 'html'] as const;
|
||||||
|
|
|
@ -144,6 +144,30 @@ describe('config/validation', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('validates invalid statusCheckNames', async () => {
|
||||||
|
const config = {
|
||||||
|
statusCheckNames: {
|
||||||
|
randomKey: '',
|
||||||
|
mergeConfidence: 10,
|
||||||
|
configValidation: '',
|
||||||
|
artifactError: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// @ts-expect-error invalid options
|
||||||
|
const { errors } = await configValidation.validateConfig(config);
|
||||||
|
expect(errors).toMatchObject([
|
||||||
|
{
|
||||||
|
message:
|
||||||
|
'Invalid `statusCheckNames.mergeConfidence` configuration: status check is not a string.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message:
|
||||||
|
'Invalid `statusCheckNames.statusCheckNames.randomKey` configuration: key is not allowed.',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(errors).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
it('catches invalid customDatasources record type', async () => {
|
it('catches invalid customDatasources record type', async () => {
|
||||||
const config = {
|
const config = {
|
||||||
customDatasources: {
|
customDatasources: {
|
||||||
|
|
|
@ -16,11 +16,13 @@ import {
|
||||||
import { migrateConfig } from './migration';
|
import { migrateConfig } from './migration';
|
||||||
import { getOptions } from './options';
|
import { getOptions } from './options';
|
||||||
import { resolveConfigPresets } from './presets';
|
import { resolveConfigPresets } from './presets';
|
||||||
import type {
|
import {
|
||||||
RenovateConfig,
|
type RenovateConfig,
|
||||||
RenovateOptions,
|
type RenovateOptions,
|
||||||
ValidationMessage,
|
type StatusCheckKey,
|
||||||
ValidationResult,
|
type ValidationMessage,
|
||||||
|
type ValidationResult,
|
||||||
|
allowedStatusCheckStrings,
|
||||||
} from './types';
|
} from './types';
|
||||||
import * as managerValidator from './validation-helpers/managers';
|
import * as managerValidator from './validation-helpers/managers';
|
||||||
|
|
||||||
|
@ -563,6 +565,30 @@ export async function validateConfig(
|
||||||
message: `Invalid \`${currentPath}.${key}.${res}\` configuration: value is not a string`,
|
message: `Invalid \`${currentPath}.${key}.${res}\` configuration: value is not a string`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (key === 'statusCheckNames') {
|
||||||
|
for (const [statusCheckKey, statusCheckValue] of Object.entries(
|
||||||
|
val,
|
||||||
|
)) {
|
||||||
|
if (
|
||||||
|
!allowedStatusCheckStrings.includes(
|
||||||
|
statusCheckKey as StatusCheckKey,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
errors.push({
|
||||||
|
topic: 'Configuration Error',
|
||||||
|
message: `Invalid \`${currentPath}.${key}.${statusCheckKey}\` configuration: key is not allowed.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!(is.string(statusCheckValue) || is.null_(statusCheckValue))
|
||||||
|
) {
|
||||||
|
errors.push({
|
||||||
|
topic: 'Configuration Error',
|
||||||
|
message: `Invalid \`${currentPath}.${statusCheckKey}\` configuration: status check is not a string.`,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (key === 'customDatasources') {
|
} else if (key === 'customDatasources') {
|
||||||
const allowedKeys = [
|
const allowedKeys = [
|
||||||
'description',
|
'description',
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
fs,
|
fs,
|
||||||
git,
|
git,
|
||||||
mocked,
|
mocked,
|
||||||
|
partial,
|
||||||
platform,
|
platform,
|
||||||
scm,
|
scm,
|
||||||
} from '../../../../test/util';
|
} from '../../../../test/util';
|
||||||
|
@ -26,6 +27,9 @@ describe('workers/repository/reconfigure/index', () => {
|
||||||
const config: RenovateConfig = {
|
const config: RenovateConfig = {
|
||||||
branchPrefix: 'prefix/',
|
branchPrefix: 'prefix/',
|
||||||
baseBranch: 'base',
|
baseBranch: 'base',
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
configValidation: 'renovate/config-validation',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -151,6 +155,46 @@ describe('workers/repository/reconfigure/index', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('skips adding status check if statusCheckNames.configValidation is null', async () => {
|
||||||
|
cache.getCache.mockReturnValueOnce({
|
||||||
|
reconfigureBranchCache: {
|
||||||
|
reconfigureBranchSha: 'new-sha',
|
||||||
|
isConfigValid: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await validateReconfigureBranch({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
configValidation: null,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips adding status check if statusCheckNames.configValidation is empty string', async () => {
|
||||||
|
cache.getCache.mockReturnValueOnce({
|
||||||
|
reconfigureBranchCache: {
|
||||||
|
reconfigureBranchSha: 'new-sha',
|
||||||
|
isConfigValid: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await validateReconfigureBranch({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
configValidation: '',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('skips validation if cache is valid', async () => {
|
it('skips validation if cache is valid', async () => {
|
||||||
cache.getCache.mockReturnValueOnce({
|
cache.getCache.mockReturnValueOnce({
|
||||||
reconfigureBranchCache: {
|
reconfigureBranchCache: {
|
||||||
|
@ -174,7 +218,7 @@ describe('workers/repository/reconfigure/index', () => {
|
||||||
platform.getBranchStatusCheck.mockResolvedValueOnce('green');
|
platform.getBranchStatusCheck.mockResolvedValueOnce('green');
|
||||||
await validateReconfigureBranch(config);
|
await validateReconfigureBranch(config);
|
||||||
expect(logger.debug).toHaveBeenCalledWith(
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
'Skipping validation check as status check already exists',
|
'Skipping validation check because status check already exists.',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { logger } from '../../../logger';
|
||||||
import { platform } from '../../../modules/platform';
|
import { platform } from '../../../modules/platform';
|
||||||
import { ensureComment } from '../../../modules/platform/comment';
|
import { ensureComment } from '../../../modules/platform/comment';
|
||||||
import { scm } from '../../../modules/platform/scm';
|
import { scm } from '../../../modules/platform/scm';
|
||||||
|
import type { BranchStatus } from '../../../types';
|
||||||
import { getCache } from '../../../util/cache/repository';
|
import { getCache } from '../../../util/cache/repository';
|
||||||
import { readLocalFile } from '../../../util/fs';
|
import { readLocalFile } from '../../../util/fs';
|
||||||
import { getBranchCommit } from '../../../util/git';
|
import { getBranchCommit } from '../../../util/git';
|
||||||
|
@ -16,6 +17,25 @@ import {
|
||||||
setReconfigureBranchCache,
|
setReconfigureBranchCache,
|
||||||
} from './reconfigure-cache';
|
} from './reconfigure-cache';
|
||||||
|
|
||||||
|
async function setBranchStatus(
|
||||||
|
branchName: string,
|
||||||
|
description: string,
|
||||||
|
state: BranchStatus,
|
||||||
|
context?: string | null,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!is.nonEmptyString(context)) {
|
||||||
|
// already logged this case when validating the status check
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await platform.setBranchStatus({
|
||||||
|
branchName,
|
||||||
|
context,
|
||||||
|
description,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function getReconfigureBranchName(prefix: string): string {
|
export function getReconfigureBranchName(prefix: string): string {
|
||||||
return `${prefix}reconfigure`;
|
return `${prefix}reconfigure`;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +43,7 @@ export async function validateReconfigureBranch(
|
||||||
config: RenovateConfig,
|
config: RenovateConfig,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
logger.debug('validateReconfigureBranch()');
|
logger.debug('validateReconfigureBranch()');
|
||||||
const context = `renovate/config-validation`;
|
const context = config.statusCheckNames?.configValidation;
|
||||||
|
|
||||||
const branchName = getReconfigureBranchName(config.branchPrefix!);
|
const branchName = getReconfigureBranchName(config.branchPrefix!);
|
||||||
const branchExists = await scm.branchExists(branchName);
|
const branchExists = await scm.branchExists(branchName);
|
||||||
|
@ -48,15 +68,24 @@ export async function validateReconfigureBranch(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context) {
|
||||||
const validationStatus = await platform.getBranchStatusCheck(
|
const validationStatus = await platform.getBranchStatusCheck(
|
||||||
branchName,
|
branchName,
|
||||||
'renovate/config-validation',
|
context,
|
||||||
);
|
);
|
||||||
|
|
||||||
// if old status check is present skip validation
|
// if old status check is present skip validation
|
||||||
if (is.nonEmptyString(validationStatus)) {
|
if (is.nonEmptyString(validationStatus)) {
|
||||||
logger.debug('Skipping validation check as status check already exists');
|
logger.debug(
|
||||||
|
'Skipping validation check because status check already exists.',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
logger.debug(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await scm.checkoutBranch(branchName);
|
await scm.checkoutBranch(branchName);
|
||||||
|
@ -70,12 +99,12 @@ export async function validateReconfigureBranch(
|
||||||
|
|
||||||
if (!is.nonEmptyString(configFileName)) {
|
if (!is.nonEmptyString(configFileName)) {
|
||||||
logger.warn('No config file found in reconfigure branch');
|
logger.warn('No config file found in reconfigure branch');
|
||||||
await platform.setBranchStatus({
|
await setBranchStatus(
|
||||||
branchName,
|
branchName,
|
||||||
|
'Validation Failed - No config file found',
|
||||||
|
'red',
|
||||||
context,
|
context,
|
||||||
description: 'Validation Failed - No config file found',
|
);
|
||||||
state: 'red',
|
|
||||||
});
|
|
||||||
setReconfigureBranchCache(branchSha, false);
|
setReconfigureBranchCache(branchSha, false);
|
||||||
await scm.checkoutBranch(config.defaultBranch!);
|
await scm.checkoutBranch(config.defaultBranch!);
|
||||||
return;
|
return;
|
||||||
|
@ -90,12 +119,12 @@ export async function validateReconfigureBranch(
|
||||||
|
|
||||||
if (!is.nonEmptyString(configFileRaw)) {
|
if (!is.nonEmptyString(configFileRaw)) {
|
||||||
logger.warn('Empty or invalid config file');
|
logger.warn('Empty or invalid config file');
|
||||||
await platform.setBranchStatus({
|
await setBranchStatus(
|
||||||
branchName,
|
branchName,
|
||||||
|
'Validation Failed - Empty/Invalid config file',
|
||||||
|
'red',
|
||||||
context,
|
context,
|
||||||
description: 'Validation Failed - Empty/Invalid config file',
|
);
|
||||||
state: 'red',
|
|
||||||
});
|
|
||||||
setReconfigureBranchCache(branchSha, false);
|
setReconfigureBranchCache(branchSha, false);
|
||||||
await scm.checkoutBranch(config.baseBranch!);
|
await scm.checkoutBranch(config.baseBranch!);
|
||||||
return;
|
return;
|
||||||
|
@ -110,12 +139,12 @@ export async function validateReconfigureBranch(
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error({ err }, 'Error while parsing config file');
|
logger.error({ err }, 'Error while parsing config file');
|
||||||
await platform.setBranchStatus({
|
await setBranchStatus(
|
||||||
branchName,
|
branchName,
|
||||||
|
'Validation Failed - Unparsable config file',
|
||||||
|
'red',
|
||||||
context,
|
context,
|
||||||
description: 'Validation Failed - Unparsable config file',
|
);
|
||||||
state: 'red',
|
|
||||||
});
|
|
||||||
setReconfigureBranchCache(branchSha, false);
|
setReconfigureBranchCache(branchSha, false);
|
||||||
await scm.checkoutBranch(config.baseBranch!);
|
await scm.checkoutBranch(config.baseBranch!);
|
||||||
return;
|
return;
|
||||||
|
@ -150,24 +179,14 @@ export async function validateReconfigureBranch(
|
||||||
content: body,
|
content: body,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await platform.setBranchStatus({
|
await setBranchStatus(branchName, 'Validation Failed', 'red', context);
|
||||||
branchName,
|
|
||||||
context,
|
|
||||||
description: 'Validation Failed',
|
|
||||||
state: 'red',
|
|
||||||
});
|
|
||||||
setReconfigureBranchCache(branchSha, false);
|
setReconfigureBranchCache(branchSha, false);
|
||||||
await scm.checkoutBranch(config.baseBranch!);
|
await scm.checkoutBranch(config.baseBranch!);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// passing check
|
// passing check
|
||||||
await platform.setBranchStatus({
|
await setBranchStatus(branchName, 'Validation Successful', 'green', context);
|
||||||
branchName,
|
|
||||||
context,
|
|
||||||
description: 'Validation Successful',
|
|
||||||
state: 'green',
|
|
||||||
});
|
|
||||||
|
|
||||||
setReconfigureBranchCache(branchSha, true);
|
setReconfigureBranchCache(branchSha, true);
|
||||||
await scm.checkoutBranch(config.baseBranch!);
|
await scm.checkoutBranch(config.baseBranch!);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { platform } from '../../../../../test/util';
|
import { RenovateConfig, partial, platform } from '../../../../../test/util';
|
||||||
import { GlobalConfig } from '../../../../config/global';
|
import { GlobalConfig } from '../../../../config/global';
|
||||||
|
import { logger } from '../../../../logger';
|
||||||
import type { BranchConfig } from '../../../types';
|
import type { BranchConfig } from '../../../types';
|
||||||
import { setArtifactErrorStatus } from './artifacts';
|
import { setArtifactErrorStatus } from './artifacts';
|
||||||
|
|
||||||
|
@ -14,6 +15,9 @@ describe('workers/repository/update/branch/artifacts', () => {
|
||||||
branchName: 'renovate/pin',
|
branchName: 'renovate/pin',
|
||||||
upgrades: [],
|
upgrades: [],
|
||||||
artifactErrors: [{ lockFile: 'some' }],
|
artifactErrors: [{ lockFile: 'some' }],
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
artifactError: 'renovate/artifact',
|
||||||
|
}),
|
||||||
} satisfies BranchConfig;
|
} satisfies BranchConfig;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,15 +34,50 @@ describe('workers/repository/update/branch/artifacts', () => {
|
||||||
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('skips status if statusCheckNames.artifactError is null', async () => {
|
||||||
|
await setArtifactErrorStatus({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
artifactError: null,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips status if statusCheckNames.artifactError is empty string', async () => {
|
||||||
|
await setArtifactErrorStatus({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
artifactError: '',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips status if statusCheckNames is undefined', async () => {
|
||||||
|
await setArtifactErrorStatus({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: undefined,
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('skips status (dry-run)', async () => {
|
it('skips status (dry-run)', async () => {
|
||||||
GlobalConfig.set({ dryRun: 'full' });
|
GlobalConfig.set({ dryRun: 'full' });
|
||||||
platform.getBranchStatusCheck.mockResolvedValueOnce(null);
|
|
||||||
await setArtifactErrorStatus(config);
|
await setArtifactErrorStatus(config);
|
||||||
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('skips status (no errors)', async () => {
|
it('skips status (no errors)', async () => {
|
||||||
platform.getBranchStatusCheck.mockResolvedValueOnce(null);
|
|
||||||
config.artifactErrors = [];
|
config.artifactErrors = [];
|
||||||
await setArtifactErrorStatus(config);
|
await setArtifactErrorStatus(config);
|
||||||
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
|
|
@ -11,7 +11,14 @@ export async function setArtifactErrorStatus(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = `renovate/artifacts`;
|
const context = config.statusCheckNames?.artifactError;
|
||||||
|
if (!context) {
|
||||||
|
logger.debug(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const description = 'Artifact file update failure';
|
const description = 'Artifact file update failure';
|
||||||
const state = 'red';
|
const state = 'red';
|
||||||
const existingState = await platform.getBranchStatusCheck(
|
const existingState = await platform.getBranchStatusCheck(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { partial, platform } from '../../../../../test/util';
|
import { RenovateConfig, partial, platform } from '../../../../../test/util';
|
||||||
|
import { logger } from '../../../../logger';
|
||||||
import {
|
import {
|
||||||
ConfidenceConfig,
|
ConfidenceConfig,
|
||||||
StabilityConfig,
|
StabilityConfig,
|
||||||
|
@ -14,6 +15,9 @@ describe('workers/repository/update/branch/status-checks', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config = partial<StabilityConfig>({
|
config = partial<StabilityConfig>({
|
||||||
branchName: 'renovate/some-branch',
|
branchName: 'renovate/some-branch',
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
minimumReleaseAge: 'renovate/stability-days',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -45,6 +49,46 @@ describe('workers/repository/update/branch/status-checks', () => {
|
||||||
expect(platform.getBranchStatusCheck).toHaveBeenCalledTimes(1);
|
expect(platform.getBranchStatusCheck).toHaveBeenCalledTimes(1);
|
||||||
expect(platform.setBranchStatus).toHaveBeenCalledTimes(0);
|
expect(platform.setBranchStatus).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('skips status if statusCheckNames.minimumReleaseAge is null', async () => {
|
||||||
|
config.stabilityStatus = 'green';
|
||||||
|
await setStability({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
minimumReleaseAge: null,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips status if statusCheckNames.minimumReleaseAge is empty string', async () => {
|
||||||
|
config.stabilityStatus = 'green';
|
||||||
|
await setStability({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
minimumReleaseAge: '',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips status if statusCheckNames is undefined', async () => {
|
||||||
|
config.stabilityStatus = 'green';
|
||||||
|
await setStability({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: undefined as never,
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setConfidence', () => {
|
describe('setConfidence', () => {
|
||||||
|
@ -53,6 +97,9 @@ describe('workers/repository/update/branch/status-checks', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config = {
|
config = {
|
||||||
branchName: 'renovate/some-branch',
|
branchName: 'renovate/some-branch',
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
mergeConfidence: 'renovate/merge-confidence',
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -85,6 +132,49 @@ describe('workers/repository/update/branch/status-checks', () => {
|
||||||
expect(platform.getBranchStatusCheck).toHaveBeenCalledTimes(1);
|
expect(platform.getBranchStatusCheck).toHaveBeenCalledTimes(1);
|
||||||
expect(platform.setBranchStatus).toHaveBeenCalledTimes(0);
|
expect(platform.setBranchStatus).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('skips status if statusCheckNames.mergeConfidence is null', async () => {
|
||||||
|
config.minimumConfidence = 'high';
|
||||||
|
config.confidenceStatus = 'green';
|
||||||
|
await setConfidence({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
mergeConfidence: null,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips status if statusCheckNames.mergeConfidence is empty string', async () => {
|
||||||
|
config.minimumConfidence = 'high';
|
||||||
|
config.confidenceStatus = 'green';
|
||||||
|
await setConfidence({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
|
||||||
|
mergeConfidence: '',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips status if statusCheckNames is undefined', async () => {
|
||||||
|
config.minimumConfidence = 'high';
|
||||||
|
config.confidenceStatus = 'green';
|
||||||
|
await setConfidence({
|
||||||
|
...config,
|
||||||
|
statusCheckNames: undefined as never,
|
||||||
|
});
|
||||||
|
expect(logger.debug).toHaveBeenCalledWith(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
expect(platform.setBranchStatus).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getBranchStatus', () => {
|
describe('getBranchStatus', () => {
|
||||||
|
|
|
@ -62,7 +62,15 @@ export async function setStability(config: StabilityConfig): Promise<void> {
|
||||||
if (!config.stabilityStatus) {
|
if (!config.stabilityStatus) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const context = `renovate/stability-days`;
|
|
||||||
|
const context = config.statusCheckNames?.minimumReleaseAge;
|
||||||
|
if (!context) {
|
||||||
|
logger.debug(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const description =
|
const description =
|
||||||
config.stabilityStatus === 'green'
|
config.stabilityStatus === 'green'
|
||||||
? 'Updates have met minimum release age requirement'
|
? 'Updates have met minimum release age requirement'
|
||||||
|
@ -90,7 +98,14 @@ export async function setConfidence(config: ConfidenceConfig): Promise<void> {
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const context = `renovate/merge-confidence`;
|
const context = config.statusCheckNames?.mergeConfidence;
|
||||||
|
if (!context) {
|
||||||
|
logger.debug(
|
||||||
|
'Status check is null or an empty string, skipping status check addition.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const description =
|
const description =
|
||||||
config.confidenceStatus === 'green'
|
config.confidenceStatus === 'green'
|
||||||
? 'Updates have met Merge Confidence requirement'
|
? 'Updates have met Merge Confidence requirement'
|
||||||
|
|
Loading…
Reference in a new issue