diff --git a/lib/workers/repository/error-config.spec.ts b/lib/workers/repository/error-config.spec.ts index 5076984e35673d..109bc7aa7d374f 100644 --- a/lib/workers/repository/error-config.spec.ts +++ b/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'; @@ -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 () => { @@ -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(), + const pr = partial({ + 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(), + const pr = partial({ 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(), number: 1, state: '!open', }); + const res = await raiseConfigWarningIssue(config, error); + expect(res).toBeUndefined(); + expect(logger.info).toHaveBeenCalledWith( + { notificationName }, + 'Configuration failure, issues will be suppressed' + ); }); }); }); diff --git a/lib/workers/repository/error-config.ts b/lib/workers/repository/error-config.ts index 0fba9c00eefbbf..54a6d659e00f5c 100644 --- a/lib/workers/repository/error-config.ts +++ b/lib/workers/repository/error-config.ts @@ -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 { 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 { + 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 { + 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'); } }