Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
refactor(config-error): generalize error issue creation for future re…
…use (#22289)
  • Loading branch information
Gabriel-Ladzaretti committed May 17, 2023
1 parent 71d64d2 commit 98d9851
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 42 deletions.
49 changes: 43 additions & 6 deletions lib/workers/repository/error-config.spec.ts
@@ -1,7 +1,13 @@
import { mock } from 'jest-mock-extended';
import { RenovateConfig, getConfig, platform } from '../../../test/util';
import {
RenovateConfig,
getConfig,
partial,
platform,
} from '../../../test/util';
import { GlobalConfig } from '../../config/global';
import { CONFIG_VALIDATION } from '../../constants/error-messages';
import { logger } from '../../logger';
import type { Pr } from '../../modules/platform';
import { raiseConfigWarningIssue } from './error-config';

Expand All @@ -24,9 +30,16 @@ describe('workers/repository/error-config', () => {
const error = new Error(CONFIG_VALIDATION);
error.validationSource = 'package.json';
error.validationMessage = 'some-message';
error.validationError = 'some-error';
platform.ensureIssue.mockResolvedValueOnce('created');

const res = await raiseConfigWarningIssue(config, error);

expect(res).toBeUndefined();
expect(logger.warn).toHaveBeenCalledWith(
{ configError: error, res: 'created' },
'Configuration Warning'
);
});

it('creates issues (dryRun)', async () => {
Expand All @@ -35,50 +48,74 @@ describe('workers/repository/error-config', () => {
error.validationMessage = 'some-message';
platform.ensureIssue.mockResolvedValueOnce('created');
GlobalConfig.set({ dryRun: 'full' });

const res = await raiseConfigWarningIssue(config, error);

expect(res).toBeUndefined();
expect(logger.info).toHaveBeenCalledWith(
{ configError: error },
'DRY-RUN: Would ensure configuration error issue'
);
});

it('handles onboarding', async () => {
const error = new Error(CONFIG_VALIDATION);
error.validationSource = 'package.json';
error.validationMessage = 'some-message';
platform.getBranchPr.mockResolvedValue({
...mock<Pr>(),
const pr = partial<Pr>({
title: 'onboarding',
number: 1,
state: 'open',
});
platform.getBranchPr.mockResolvedValue(pr);

const res = await raiseConfigWarningIssue(config, error);

expect(res).toBeUndefined();
expect(platform.updatePr).toHaveBeenCalledWith(
expect.objectContaining({ prTitle: pr.title, number: pr.number })
);
});

it('handles onboarding (dryRun)', async () => {
const error = new Error(CONFIG_VALIDATION);
error.validationSource = 'package.json';
error.validationMessage = 'some-message';
platform.getBranchPr.mockResolvedValue({
...mock<Pr>(),
const pr = partial<Pr>({
number: 1,
state: 'open',
});
platform.getBranchPr.mockResolvedValue(pr);
GlobalConfig.set({ dryRun: 'full' });

const res = await raiseConfigWarningIssue(config, error);

expect(res).toBeUndefined();
expect(logger.info).toHaveBeenCalledWith(
`DRY-RUN: Would update PR #${pr.number}`
);
});

it('disable issue creation on config failure', async () => {
const notificationName = 'configErrorIssue';
const error = new Error(CONFIG_VALIDATION);
error.validationSource = 'package.json';
error.validationMessage = 'some-message';
// config.suppressNotifications = ['deprecationWarningIssues']
config.suppressNotifications = ['configErrorIssue'];
config.suppressNotifications = [notificationName];
platform.getBranchPr.mockResolvedValueOnce({
...mock<Pr>(),
number: 1,
state: '!open',
});

const res = await raiseConfigWarningIssue(config, error);

expect(res).toBeUndefined();
expect(logger.info).toHaveBeenCalledWith(
{ notificationName },
'Configuration failure, issues will be suppressed'
);
});
});
});
103 changes: 67 additions & 36 deletions lib/workers/repository/error-config.ts
Expand Up @@ -2,61 +2,92 @@
import { GlobalConfig } from '../../config/global';
import type { RenovateConfig } from '../../config/types';
import { logger } from '../../logger';
import { platform } from '../../modules/platform';
import { Pr, platform } from '../../modules/platform';
import { regEx } from '../../util/regex';

export async function raiseConfigWarningIssue(
export function raiseConfigWarningIssue(
config: RenovateConfig,
error: Error
): Promise<void> {
logger.debug('raiseConfigWarningIssue()');
let body = `There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.\n\n`;
const title = `Action Required: Fix Renovate Configuration`;
const body = `There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.\n\n`;
const notificationName = 'configErrorIssue';
return raiseWarningIssue(config, notificationName, title, body, error);
}

async function raiseWarningIssue(
config: RenovateConfig,
notificationName: string,
title: string,
initialBody: string,
error: Error
): Promise<void> {
let body = initialBody;
if (error.validationSource) {
body += `Location: \`${error.validationSource}\`\n`;
}
body += `Error type: ${error.validationError!}\n`;
if (error.validationError) {
body += `Error type: ${error.validationError}\n`;
}
if (error.validationMessage) {
body += `Message: \`${error.validationMessage.replace(
regEx(/`/g),
"'"
)}\`\n`;
}

const pr = await platform.getBranchPr(config.onboardingBranch!);
if (pr?.state === 'open') {
logger.debug('Updating onboarding PR with config error notice');
body = `## Action Required: Fix Renovate Configuration\n\n${body}`;
body += `\n\nOnce you have resolved this problem (in this onboarding branch), Renovate will return to providing you with a preview of your repository's configuration.`;
if (GlobalConfig.get('dryRun')) {
logger.info(`DRY-RUN: Would update PR #${pr.number}`);
} else {
try {
await platform.updatePr({
number: pr.number,
prTitle: config.onboardingPrTitle!,
prBody: body,
});
} catch (err) /* istanbul ignore next */ {
logger.warn({ err }, 'Error updating onboarding PR');
}
}
} else if (GlobalConfig.get('dryRun')) {
logger.info('DRY-RUN: Would ensure config error issue');
} else if (config.suppressNotifications?.includes('configErrorIssue')) {
await handleOnboardingPr(pr, body);
return;
}

if (GlobalConfig.get('dryRun')) {
logger.info(
'configErrorIssue - configuration failure, issues will be suppressed'
{ configError: error },
'DRY-RUN: Would ensure configuration error issue'
);
} else {
const once = false;
const shouldReopen = config.configWarningReuseIssue;
const res = await platform.ensureIssue({
title: `Action Required: Fix Renovate Configuration`,
body,
once,
shouldReOpen: shouldReopen,
confidential: config.confidential,
return;
}

if (config.suppressNotifications?.includes(notificationName)) {
logger.info(
{ notificationName },
'Configuration failure, issues will be suppressed'
);
return;
}

const res = await platform.ensureIssue({
title,
body,
once: false,
shouldReOpen: config.configWarningReuseIssue,
confidential: config.confidential,
});
if (res === 'created') {
logger.warn({ configError: error, res }, 'Configuration Warning');
}
}

async function handleOnboardingPr(pr: Pr, issueMessage: string): Promise<void> {
logger.debug('Updating onboarding PR with config error notice');
if (GlobalConfig.get('dryRun')) {
logger.info(`DRY-RUN: Would update PR #${pr.number}`);
return;
}

let prBody = `## Action Required: Fix Renovate Configuration\n\n${issueMessage}`;
prBody += `\n\nOnce you have resolved this problem (in this onboarding branch), Renovate will return to providing you with a preview of your repository's configuration.`;

try {
await platform.updatePr({
number: pr.number,
prTitle: pr.title,
prBody,
});
if (res === 'created') {
logger.warn({ configError: error, res }, 'Config Warning');
}
} catch (err) /* istanbul ignore next */ {
logger.warn({ err }, 'Error updating onboarding PR');
}
}

0 comments on commit 98d9851

Please sign in to comment.