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