From fbb0a01f156a8b60828b24f6b7ae3113b34277ff Mon Sep 17 00:00:00 2001 From: Sergei Zharinov <zharinov@users.noreply.github.com> Date: Wed, 26 Jan 2022 07:52:06 +0300 Subject: [PATCH] refactor(git): Extract error handling to separate file (#13819) --- lib/util/git/error.ts | 143 ++++++++++++++++++++++++++++++++++++++++++ lib/util/git/index.ts | 134 +-------------------------------------- 2 files changed, 145 insertions(+), 132 deletions(-) create mode 100644 lib/util/git/error.ts diff --git a/lib/util/git/error.ts b/lib/util/git/error.ts new file mode 100644 index 0000000000..c564efe762 --- /dev/null +++ b/lib/util/git/error.ts @@ -0,0 +1,143 @@ +import { CONFIG_VALIDATION } from '../../constants/error-messages'; +import { logger } from '../../logger'; +import { ExternalHostError } from '../../types/errors/external-host-error'; +import { FileChange } from './types'; + +// istanbul ignore next +export function checkForPlatformFailure(err: Error): void { + if (process.env.NODE_ENV === 'test') { + return; + } + const externalHostFailureStrings = [ + 'remote: Invalid username or password', + 'gnutls_handshake() failed', + 'The requested URL returned error: 5', + 'The remote end hung up unexpectedly', + 'access denied or repository not exported', + 'Could not write new index file', + 'Failed to connect to', + 'Connection timed out', + 'malformed object name', + 'Could not resolve host', + 'early EOF', + 'fatal: bad config', // .gitmodules problem + 'expected flush after ref listing', + ]; + for (const errorStr of externalHostFailureStrings) { + if (err.message.includes(errorStr)) { + logger.debug({ err }, 'Converting git error to ExternalHostError'); + throw new ExternalHostError(err, 'git'); + } + } + + const configErrorStrings = [ + { + error: 'GitLab: Branch name does not follow the pattern', + message: + "Cannot push because branch name does not follow project's push rules", + }, + { + error: 'GitLab: Commit message does not follow the pattern', + message: + "Cannot push because commit message does not follow project's push rules", + }, + { + error: ' is not a member of team', + message: + 'The `Restrict commits to existing GitLab users` rule is blocking Renovate push. Check the Renovate `gitAuthor` setting', + }, + { + error: 'TF401027:', + message: + 'You need the Git `GenericContribute` permission to perform this action', + }, + { + error: 'matches more than one', + message: + "Renovate cannot push branches if there are tags with names the same as Renovate's branches. Please remove conflicting tag names or change Renovate's branchPrefix to avoid conflicts.", + }, + ]; + for (const { error, message } of configErrorStrings) { + if (err.message.includes(error)) { + logger.debug({ err }, 'Converting git error to CONFIG_VALIDATION error'); + const res = new Error(CONFIG_VALIDATION); + res.validationError = message; + res.validationMessage = err.message; + throw res; + } + } +} + +// istanbul ignore next +export function handleCommitError( + files: FileChange[], + branchName: string, + err: Error +): null { + checkForPlatformFailure(err); + if (err.message.includes(`'refs/heads/renovate' exists`)) { + const error = new Error(CONFIG_VALIDATION); + error.validationSource = 'None'; + error.validationError = 'An existing branch is blocking Renovate'; + error.validationMessage = `Renovate needs to create the branch "${branchName}" but is blocked from doing so because of an existing branch called "renovate". Please remove it so that Renovate can proceed.`; + throw error; + } + if ( + err.message.includes( + 'refusing to allow a GitHub App to create or update workflow' + ) + ) { + logger.warn( + 'App has not been granted permissions to update Workflows - aborting branch.' + ); + return null; + } + if ( + (err.message.includes('remote rejected') || err.message.includes('403')) && + files?.some((file) => file.path?.startsWith('.github/workflows/')) + ) { + logger.debug({ err }, 'commitFiles error'); + logger.info('Workflows update rejection - aborting branch.'); + return null; + } + if (err.message.includes('protected branch hook declined')) { + const error = new Error(CONFIG_VALIDATION); + error.validationSource = branchName; + error.validationError = 'Renovate branch is protected'; + error.validationMessage = `Renovate cannot push to its branch because branch protection has been enabled.`; + throw error; + } + if (err.message.includes('can only push your own commits')) { + const error = new Error(CONFIG_VALIDATION); + error.validationSource = branchName; + error.validationError = 'Bitbucket committer error'; + error.validationMessage = `Renovate has experienced the following error when attempting to push its branch to the server: "${String( + err.message + )}"`; + throw error; + } + if (err.message.includes('remote: error: cannot lock ref')) { + logger.error({ err }, 'Error committing files.'); + return null; + } + if (err.message.includes('[rejected] (stale info)')) { + logger.info( + 'Branch update was rejected because local copy is not up-to-date.' + ); + return null; + } + if ( + err.message.includes('denying non-fast-forward') || + err.message.includes('GH003: Sorry, force-pushing') + ) { + logger.debug({ err }, 'Permission denied to update branch'); + const error = new Error(CONFIG_VALIDATION); + error.validationSource = branchName; + error.validationError = 'Force push denied'; + error.validationMessage = `Renovate is unable to update branch(es) due to force pushes being disallowed.`; + throw error; + } + logger.debug({ err }, 'Unknown error committing files'); + // We don't know why this happened, so this will cause bubble up to a branch error + throw err; +} diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts index 6e6bb7c9e3..983e1f3c3a 100644 --- a/lib/util/git/index.ts +++ b/lib/util/git/index.ts @@ -27,6 +27,7 @@ import { Limit, incLimitedValue } from '../../workers/global/limits'; import { regEx } from '../regex'; import { parseGitAuthor } from './author'; import { getNoVerify, simpleGitConfig } from './config'; +import { checkForPlatformFailure, handleCommitError } from './error'; import { configSigningKey, writePrivateKey } from './private-key'; import type { CommitFilesConfig, @@ -42,71 +43,6 @@ export { setPrivateKey } from './private-key'; // TODO: fix upstream types https://github.com/steveukx/git-js/issues/704 const ResetMode = (simpleGit.default as any).ResetMode as typeof _ResetMode; -// istanbul ignore next -function checkForPlatformFailure(err: Error): void { - if (process.env.NODE_ENV === 'test') { - return; - } - const externalHostFailureStrings = [ - 'remote: Invalid username or password', - 'gnutls_handshake() failed', - 'The requested URL returned error: 5', - 'The remote end hung up unexpectedly', - 'access denied or repository not exported', - 'Could not write new index file', - 'Failed to connect to', - 'Connection timed out', - 'malformed object name', - 'Could not resolve host', - 'early EOF', - 'fatal: bad config', // .gitmodules problem - 'expected flush after ref listing', - ]; - for (const errorStr of externalHostFailureStrings) { - if (err.message.includes(errorStr)) { - logger.debug({ err }, 'Converting git error to ExternalHostError'); - throw new ExternalHostError(err, 'git'); - } - } - - const configErrorStrings = [ - { - error: 'GitLab: Branch name does not follow the pattern', - message: - "Cannot push because branch name does not follow project's push rules", - }, - { - error: 'GitLab: Commit message does not follow the pattern', - message: - "Cannot push because commit message does not follow project's push rules", - }, - { - error: ' is not a member of team', - message: - 'The `Restrict commits to existing GitLab users` rule is blocking Renovate push. Check the Renovate `gitAuthor` setting', - }, - { - error: 'TF401027:', - message: - 'You need the Git `GenericContribute` permission to perform this action', - }, - { - error: 'matches more than one', - message: - "Renovate cannot push branches if there are tags with names the same as Renovate's branches. Please remove conflicting tag names or change Renovate's branchPrefix to avoid conflicts.", - }, - ]; - for (const { error, message } of configErrorStrings) { - if (err.message.includes(error)) { - logger.debug({ err }, 'Converting git error to CONFIG_VALIDATION error'); - const res = new Error(CONFIG_VALIDATION); - res.validationError = message; - res.validationMessage = err.message; - throw res; - } - } -} - function localName(branchName: string): string { return branchName.replace(regEx(/^origin\//), ''); } @@ -881,73 +817,7 @@ export async function commitFiles({ incLimitedValue(Limit.Commits); return commit; } catch (err) /* istanbul ignore next */ { - checkForPlatformFailure(err); - if (err.message.includes(`'refs/heads/renovate' exists`)) { - const error = new Error(CONFIG_VALIDATION); - error.validationSource = 'None'; - error.validationError = 'An existing branch is blocking Renovate'; - error.validationMessage = `Renovate needs to create the branch "${branchName}" but is blocked from doing so because of an existing branch called "renovate". Please remove it so that Renovate can proceed.`; - throw error; - } - if ( - err.message.includes( - 'refusing to allow a GitHub App to create or update workflow' - ) - ) { - logger.warn( - 'App has not been granted permissions to update Workflows - aborting branch.' - ); - return null; - } - if ( - (err.message.includes('remote rejected') || - err.message.includes('403')) && - files?.some((file) => file.path?.startsWith('.github/workflows/')) - ) { - logger.debug({ err }, 'commitFiles error'); - logger.info('Workflows update rejection - aborting branch.'); - return null; - } - if (err.message.includes('protected branch hook declined')) { - const error = new Error(CONFIG_VALIDATION); - error.validationSource = branchName; - error.validationError = 'Renovate branch is protected'; - error.validationMessage = `Renovate cannot push to its branch because branch protection has been enabled.`; - throw error; - } - if (err.message.includes('can only push your own commits')) { - const error = new Error(CONFIG_VALIDATION); - error.validationSource = branchName; - error.validationError = 'Bitbucket committer error'; - error.validationMessage = `Renovate has experienced the following error when attempting to push its branch to the server: "${String( - err.message - )}"`; - throw error; - } - if (err.message.includes('remote: error: cannot lock ref')) { - logger.error({ err }, 'Error committing files.'); - return null; - } - if (err.message.includes('[rejected] (stale info)')) { - logger.info( - 'Branch update was rejected because local copy is not up-to-date.' - ); - return null; - } - if ( - err.message.includes('denying non-fast-forward') || - err.message.includes('GH003: Sorry, force-pushing') - ) { - logger.debug({ err }, 'Permission denied to update branch'); - const error = new Error(CONFIG_VALIDATION); - error.validationSource = branchName; - error.validationError = 'Force push denied'; - error.validationMessage = `Renovate is unable to update branch(es) due to force pushes being disallowed.`; - throw error; - } - logger.debug({ err }, 'Unknown error committing files'); - // We don't know why this happened, so this will cause bubble up to a branch error - throw err; + return handleCommitError(files, branchName, err); } } -- GitLab