From c60737a28592b73be4d3f7614a0fb407b09d7bda Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Thu, 12 Mar 2020 12:48:57 +0100
Subject: [PATCH] fix: revert datasource error handling changes

---
 lib/datasource/cdnjs/index.spec.ts            | 37 +++++++---
 lib/datasource/cdnjs/index.ts                 | 13 +++-
 lib/datasource/crate/index.spec.ts            | 20 +++--
 lib/datasource/crate/index.ts                 |  8 +-
 lib/datasource/dart/index.spec.ts             | 24 ++++--
 lib/datasource/dart/index.ts                  | 12 ++-
 lib/datasource/galaxy/index.spec.ts           | 24 +++++-
 lib/datasource/galaxy/index.ts                |  2 +-
 lib/datasource/git-submodules/index.spec.ts   | 18 +++++
 lib/datasource/git-submodules/index.ts        | 42 ++++++-----
 lib/datasource/git-tags/index.spec.ts         | 18 +++++
 lib/datasource/git-tags/index.ts              | 60 ++++++++-------
 lib/datasource/github-releases/index.ts       | 24 +++---
 lib/datasource/github-tags/index.ts           | 28 ++++---
 .../__snapshots__/index.spec.ts.snap          | 16 ----
 lib/datasource/gitlab-tags/index.spec.ts      |  9 ---
 lib/datasource/gitlab-tags/index.ts           | 27 ++++---
 lib/datasource/gradle-version/index.ts        |  4 +-
 lib/datasource/helm/index.spec.ts             | 31 +++++---
 lib/datasource/helm/index.ts                  | 65 +++++++++++-----
 lib/datasource/hex/index.spec.ts              | 44 +++++++++--
 lib/datasource/hex/index.ts                   | 13 +++-
 lib/datasource/index.spec.ts                  |  2 +-
 lib/datasource/index.ts                       | 43 ++---------
 lib/datasource/metadata.spec.ts               | 13 ----
 lib/datasource/orb/index.spec.ts              | 30 ++++++++
 lib/datasource/orb/index.ts                   | 68 +++++++++--------
 lib/datasource/terraform-module/index.spec.ts | 22 ++++++
 lib/datasource/terraform-module/index.ts      | 74 ++++++++++++-------
 .../terraform-provider/index.spec.ts          | 30 ++++++++
 lib/datasource/terraform-provider/index.ts    | 66 +++++++++++------
 lib/util/clone.ts                             |  2 +-
 .../docker => util/got}/got.spec.ts           |  2 +-
 .../lookup/__snapshots__/index.spec.ts.snap   |  2 +
 .../repository/process/lookup/index.spec.ts   | 15 +++-
 35 files changed, 606 insertions(+), 302 deletions(-)
 rename lib/{datasource/docker => util/got}/got.spec.ts (97%)

diff --git a/lib/datasource/cdnjs/index.spec.ts b/lib/datasource/cdnjs/index.spec.ts
index db88098d92..4a387baa45 100644
--- a/lib/datasource/cdnjs/index.spec.ts
+++ b/lib/datasource/cdnjs/index.spec.ts
@@ -45,8 +45,8 @@ describe('datasource/cdnjs', () => {
       jest.clearAllMocks();
       return global.renovateCache.rmAll();
     });
-    it('throws for 429', async () => {
-      got.mockRejectedValueOnce({ statusCode: 429 });
+    it('throws for empty result', async () => {
+      got.mockResolvedValueOnce(null);
       await expect(
         getPkgReleases({ lookupName: 'foo/bar' })
       ).rejects.toThrowError(DATASOURCE_FAILURE);
@@ -55,13 +55,19 @@ describe('datasource/cdnjs', () => {
       got.mockResolvedValueOnce({});
       expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull();
     });
-    it('throws for 404', async () => {
-      const err = new Error();
-      err.statusCode = 404;
-      got.mockImplementationOnce(() => {
-        throw err;
-      });
-      await expect(getPkgReleases({ lookupName: 'foo/bar' })).rejects.toThrow();
+    it('returns null for 404', async () => {
+      got.mockRejectedValueOnce({ statusCode: 404 });
+      expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull();
+    });
+    it('returns null for 401', async () => {
+      got.mockRejectedValueOnce({ statusCode: 401 });
+      expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull();
+    });
+    it('throws for 429', async () => {
+      got.mockRejectedValueOnce({ statusCode: 429 });
+      await expect(
+        getPkgReleases({ lookupName: 'foo/bar' })
+      ).rejects.toThrowError(DATASOURCE_FAILURE);
     });
     it('throws for 5xx', async () => {
       got.mockRejectedValueOnce({ statusCode: 502 });
@@ -69,6 +75,19 @@ describe('datasource/cdnjs', () => {
         getPkgReleases({ lookupName: 'foo/bar' })
       ).rejects.toThrowError(DATASOURCE_FAILURE);
     });
+    it('returns null for unknown error', async () => {
+      got.mockImplementationOnce(() => {
+        throw new Error();
+      });
+      await expect(
+        getPkgReleases({ lookupName: 'foo/bar' })
+      ).rejects.toThrowError(DATASOURCE_FAILURE);
+    });
+    it('returns null with wrong auth token', async () => {
+      got.mockRejectedValueOnce({ statusCode: 401 });
+      const res = await getPkgReleases({ lookupName: 'foo/bar' });
+      expect(res).toBeNull();
+    });
     it('processes real data', async () => {
       got.mockResolvedValueOnce({ body: res1 });
       const res = await getPkgReleases({ lookupName: 'd3-force/d3-force.js' });
diff --git a/lib/datasource/cdnjs/index.ts b/lib/datasource/cdnjs/index.ts
index b39d73d299..68ed19489d 100644
--- a/lib/datasource/cdnjs/index.ts
+++ b/lib/datasource/cdnjs/index.ts
@@ -88,12 +88,23 @@ export async function getPkgReleases({
 
     return result;
   } catch (err) {
+    const errorData = { library, err };
+
     if (
       err.statusCode === 429 ||
       (err.statusCode >= 500 && err.statusCode < 600)
     ) {
       throw new DatasourceError(err);
     }
-    throw err;
+    if (err.statusCode === 401) {
+      logger.debug(errorData, 'Authorization error');
+    } else if (err.statusCode === 404) {
+      logger.debug(errorData, 'Package lookup error');
+    } else {
+      logger.debug(errorData, 'CDNJS lookup failure: Unknown error');
+      throw new DatasourceError(err);
+    }
   }
+
+  return null;
 }
diff --git a/lib/datasource/crate/index.spec.ts b/lib/datasource/crate/index.spec.ts
index 83725e6e7e..9dd47f443d 100644
--- a/lib/datasource/crate/index.spec.ts
+++ b/lib/datasource/crate/index.spec.ts
@@ -44,13 +44,13 @@ describe('datasource/crate', () => {
         await getPkgReleases({ lookupName: 'non_existent_crate' })
       ).toBeNull();
     });
-    it('throws for 404', async () => {
-      const err = new Error();
-      err.statusCode = 404;
-      got.mockImplementationOnce(() => {
-        throw err;
-      });
-      await expect(getPkgReleases({ lookupName: 'foo/bar' })).rejects.toThrow();
+    it('returns null for 404', async () => {
+      got.mockImplementationOnce(() =>
+        Promise.reject({
+          statusCode: 404,
+        })
+      );
+      expect(await getPkgReleases({ lookupName: 'some_crate' })).toBeNull();
     });
     it('throws for 5xx', async () => {
       got.mockImplementationOnce(() =>
@@ -67,6 +67,12 @@ describe('datasource/crate', () => {
       expect(e).toBeDefined();
       expect(e).toMatchSnapshot();
     });
+    it('returns null for unknown error', async () => {
+      got.mockImplementationOnce(() => {
+        throw new Error();
+      });
+      expect(await getPkgReleases({ lookupName: 'some_crate' })).toBeNull();
+    });
     it('processes real data', async () => {
       got.mockReturnValueOnce({
         body: res1,
diff --git a/lib/datasource/crate/index.ts b/lib/datasource/crate/index.ts
index fc0caadc1a..0640c13f1a 100644
--- a/lib/datasource/crate/index.ts
+++ b/lib/datasource/crate/index.ts
@@ -95,12 +95,18 @@ export async function getPkgReleases({
     await renovateCache.set(cacheNamespace, cacheKey, result, cacheMinutes);
     return result;
   } catch (err) {
+    if (err.statusCode === 404 || err.code === 'ENOTFOUND') {
+      logger.debug({ lookupName }, `Dependency lookup failure: not found`);
+      logger.debug({ err }, 'Crate lookup error');
+      return null;
+    }
     if (
       err.statusCode === 429 ||
       (err.statusCode >= 500 && err.statusCode < 600)
     ) {
       throw new DatasourceError(err);
     }
-    throw err;
+    logger.warn({ err, lookupName }, 'crates.io lookup failure: Unknown error');
+    return null;
   }
 }
diff --git a/lib/datasource/dart/index.spec.ts b/lib/datasource/dart/index.spec.ts
index 1895e46e30..3e63fdb88d 100644
--- a/lib/datasource/dart/index.spec.ts
+++ b/lib/datasource/dart/index.spec.ts
@@ -41,13 +41,15 @@ describe('datasource/dart', () => {
         await getPkgReleases({ lookupName: 'shared_preferences' })
       ).toBeNull();
     });
-    it('throws for 404', async () => {
-      const err = new Error();
-      err.statusCode = 404;
-      got.mockImplementationOnce(() => {
-        throw err;
-      });
-      await expect(getPkgReleases({ lookupName: 'foo/bar' })).rejects.toThrow();
+    it('returns null for 404', async () => {
+      got.mockImplementationOnce(() =>
+        Promise.reject({
+          statusCode: 404,
+        })
+      );
+      expect(
+        await getPkgReleases({ lookupName: 'shared_preferences' })
+      ).toBeNull();
     });
     it('throws for 5xx', async () => {
       got.mockImplementationOnce(() =>
@@ -64,6 +66,14 @@ describe('datasource/dart', () => {
       expect(e).toBeDefined();
       expect(e).toMatchSnapshot();
     });
+    it('returns null for unknown error', async () => {
+      got.mockImplementationOnce(() => {
+        throw new Error();
+      });
+      expect(
+        await getPkgReleases({ lookupName: 'shared_preferences' })
+      ).toBeNull();
+    });
     it('processes real data', async () => {
       got.mockReturnValueOnce({ body });
       const res = await getPkgReleases({
diff --git a/lib/datasource/dart/index.ts b/lib/datasource/dart/index.ts
index 9edbe14dfb..7c571066ad 100644
--- a/lib/datasource/dart/index.ts
+++ b/lib/datasource/dart/index.ts
@@ -1,4 +1,5 @@
 import got from '../../util/got';
+import { logger } from '../../logger';
 import { DatasourceError, ReleaseResult, GetReleasesConfig } from '../common';
 
 export const id = 'dart';
@@ -26,13 +27,22 @@ export async function getPkgReleases({
       json: true,
     });
   } catch (err) {
+    if (err.statusCode === 404 || err.code === 'ENOTFOUND') {
+      logger.debug({ lookupName }, `Dependency lookup failure: not found`);
+      logger.debug({ err }, 'Dart lookup error');
+      return null;
+    }
     if (
       err.statusCode === 429 ||
       (err.statusCode >= 500 && err.statusCode < 600)
     ) {
       throw new DatasourceError(err);
     }
-    throw err;
+    logger.warn(
+      { err, lookupName },
+      'pub.dartlang.org lookup failure: Unknown error'
+    );
+    return null;
   }
 
   const body = raw && raw.body;
diff --git a/lib/datasource/galaxy/index.spec.ts b/lib/datasource/galaxy/index.spec.ts
index 20917e18ad..ba46fcb022 100644
--- a/lib/datasource/galaxy/index.spec.ts
+++ b/lib/datasource/galaxy/index.spec.ts
@@ -35,6 +35,28 @@ describe('datasource/galaxy', () => {
         await getPkgReleases({ lookupName: 'non_existent_crate' })
       ).toBeNull();
     });
+    it('returns null for empty list', async () => {
+      got.mockReturnValueOnce({
+        body: '\n',
+      });
+      expect(
+        await getPkgReleases({ lookupName: 'non_existent_crate' })
+      ).toBeNull();
+    });
+    it('returns null for 404', async () => {
+      got.mockImplementationOnce(() =>
+        Promise.reject({
+          statusCode: 404,
+        })
+      );
+      expect(await getPkgReleases({ lookupName: 'some_crate' })).toBeNull();
+    });
+    it('returns null for unknown error', async () => {
+      got.mockImplementationOnce(() => {
+        throw new Error();
+      });
+      expect(await getPkgReleases({ lookupName: 'some_crate' })).toBeNull();
+    });
     it('processes real data', async () => {
       got.mockReturnValueOnce({
         body: res1,
@@ -72,7 +94,7 @@ describe('datasource/galaxy', () => {
       got.mockImplementationOnce(() => {
         throw err;
       });
-      await expect(getPkgReleases({ lookupName: 'foo.bar' })).rejects.toThrow();
+      expect(await getPkgReleases({ lookupName: 'foo.bar' })).toBeNull();
     });
   });
 });
diff --git a/lib/datasource/galaxy/index.ts b/lib/datasource/galaxy/index.ts
index 5b7d7b2664..f51b9378dd 100644
--- a/lib/datasource/galaxy/index.ts
+++ b/lib/datasource/galaxy/index.ts
@@ -102,6 +102,6 @@ export async function getPkgReleases({
     ) {
       throw new DatasourceError(err);
     }
-    throw err;
+    return null;
   }
 }
diff --git a/lib/datasource/git-submodules/index.spec.ts b/lib/datasource/git-submodules/index.spec.ts
index 5367212669..1c611332e0 100644
--- a/lib/datasource/git-submodules/index.spec.ts
+++ b/lib/datasource/git-submodules/index.spec.ts
@@ -10,6 +10,24 @@ const registryUrls = [lookupName, 'master'];
 describe('datasource/git-submoduless', () => {
   beforeEach(() => global.renovateCache.rmAll());
   describe('getPkgReleases', () => {
+    it('returns null if response is wrong', async () => {
+      simpleGit.mockReturnValue({
+        listRemote() {
+          return Promise.resolve(null);
+        },
+      });
+      const versions = await getPkgReleases({ lookupName, registryUrls });
+      expect(versions).toEqual(null);
+    });
+    it('returns null if remote call throws exception', async () => {
+      simpleGit.mockReturnValue({
+        listRemote() {
+          throw new Error();
+        },
+      });
+      const versions = await getPkgReleases({ lookupName, registryUrls });
+      expect(versions).toEqual(null);
+    });
     it('returns versions filtered from tags', async () => {
       simpleGit.mockReturnValue({
         listRemote() {
diff --git a/lib/datasource/git-submodules/index.ts b/lib/datasource/git-submodules/index.ts
index 72ea923089..95298dfd49 100644
--- a/lib/datasource/git-submodules/index.ts
+++ b/lib/datasource/git-submodules/index.ts
@@ -2,6 +2,7 @@ import Git from 'simple-git/promise';
 import { URL } from 'url';
 
 import { ReleaseResult, GetReleasesConfig, DigestConfig } from '../common';
+import { logger } from '../../logger';
 
 export const id = 'git-submodules';
 
@@ -21,26 +22,31 @@ export async function getPkgReleases({
   }
 
   const git = Git();
-  const newHash = (
-    await git.listRemote(['--refs', registryUrls[0], registryUrls[1]])
-  )
-    .trim()
-    .split(/\t/)[0];
+  try {
+    const newHash = (
+      await git.listRemote(['--refs', registryUrls[0], registryUrls[1]])
+    )
+      .trim()
+      .split(/\t/)[0];
 
-  const sourceUrl = new URL(registryUrls[0]);
-  sourceUrl.username = '';
+    const sourceUrl = new URL(registryUrls[0]);
+    sourceUrl.username = '';
 
-  const result = {
-    sourceUrl: sourceUrl.href,
-    releases: [
-      {
-        version: newHash,
-      },
-    ],
-  };
-  const cacheMinutes = 60;
-  await renovateCache.set(cacheNamespace, cacheKey, result, cacheMinutes);
-  return result;
+    const result = {
+      sourceUrl: sourceUrl.href,
+      releases: [
+        {
+          version: newHash,
+        },
+      ],
+    };
+    const cacheMinutes = 60;
+    await renovateCache.set(cacheNamespace, cacheKey, result, cacheMinutes);
+    return result;
+  } catch (err) {
+    logger.debug(`Error looking up tags in ${lookupName}`);
+  }
+  return null;
 }
 
 export const getDigest = (
diff --git a/lib/datasource/git-tags/index.spec.ts b/lib/datasource/git-tags/index.spec.ts
index 9c8af8973f..ad93a28e55 100644
--- a/lib/datasource/git-tags/index.spec.ts
+++ b/lib/datasource/git-tags/index.spec.ts
@@ -10,6 +10,24 @@ const lookupName = 'https://github.com/example/example.git';
 describe('datasource/git-tags', () => {
   beforeEach(() => global.renovateCache.rmAll());
   describe('getPkgReleases', () => {
+    it('returns nil if response is wrong', async () => {
+      simpleGit.mockReturnValue({
+        listRemote() {
+          return Promise.resolve(null);
+        },
+      });
+      const versions = await getPkgReleases({ lookupName });
+      expect(versions).toEqual(null);
+    });
+    it('returns nil if remote call throws exception', async () => {
+      simpleGit.mockReturnValue({
+        listRemote() {
+          throw new Error();
+        },
+      });
+      const versions = await getPkgReleases({ lookupName });
+      expect(versions).toEqual(null);
+    });
     it('returns versions filtered from tags', async () => {
       simpleGit.mockReturnValue({
         listRemote() {
diff --git a/lib/datasource/git-tags/index.ts b/lib/datasource/git-tags/index.ts
index a27210b034..e2ccd34120 100644
--- a/lib/datasource/git-tags/index.ts
+++ b/lib/datasource/git-tags/index.ts
@@ -1,5 +1,6 @@
 import simpleGit from 'simple-git/promise';
 import * as semver from '../../versioning/semver';
+import { logger } from '../../logger';
 import { ReleaseResult, GetReleasesConfig } from '../common';
 
 export const id = 'git-tags';
@@ -14,33 +15,38 @@ export async function getPkgReleases({
   lookupName,
 }: GetReleasesConfig): Promise<ReleaseResult | null> {
   const git = simpleGit();
-  const cachedResult = await renovateCache.get<ReleaseResult>(
-    cacheNamespace,
-    lookupName
-  );
-  /* istanbul ignore next line */
-  if (cachedResult) return cachedResult;
+  try {
+    const cachedResult = await renovateCache.get<ReleaseResult>(
+      cacheNamespace,
+      lookupName
+    );
+    /* istanbul ignore next line */
+    if (cachedResult) return cachedResult;
 
-  // fetch remote tags
-  const lsRemote = await git.listRemote([
-    '--sort=-v:refname',
-    '--tags',
-    lookupName,
-  ]);
-  // extract valid tags from git ls-remote which looks like 'commithash\trefs/tags/1.2.3
-  const tags = lsRemote
-    .replace(/^.+?refs\/tags\//gm, '')
-    .split('\n')
-    .filter(tag => semver.isVersion(tag));
-  const sourceUrl = lookupName.replace(/\.git$/, '').replace(/\/$/, '');
-  const result: ReleaseResult = {
-    sourceUrl,
-    releases: tags.map(tag => ({
-      version: tag,
-      gitRef: tag,
-    })),
-  };
+    // fetch remote tags
+    const lsRemote = await git.listRemote([
+      '--sort=-v:refname',
+      '--tags',
+      lookupName,
+    ]);
+    // extract valid tags from git ls-remote which looks like 'commithash\trefs/tags/1.2.3
+    const tags = lsRemote
+      .replace(/^.+?refs\/tags\//gm, '')
+      .split('\n')
+      .filter(tag => semver.isVersion(tag));
+    const sourceUrl = lookupName.replace(/\.git$/, '').replace(/\/$/, '');
+    const result: ReleaseResult = {
+      sourceUrl,
+      releases: tags.map(tag => ({
+        version: tag,
+        gitRef: tag,
+      })),
+    };
 
-  await renovateCache.set(cacheNamespace, lookupName, result, cacheMinutes);
-  return result;
+    await renovateCache.set(cacheNamespace, lookupName, result, cacheMinutes);
+    return result;
+  } catch (e) {
+    logger.debug(`Error looking up tags in ${lookupName}`);
+  }
+  return null;
 }
diff --git a/lib/datasource/github-releases/index.ts b/lib/datasource/github-releases/index.ts
index 7965e2a4c4..f57f0434b5 100644
--- a/lib/datasource/github-releases/index.ts
+++ b/lib/datasource/github-releases/index.ts
@@ -1,5 +1,6 @@
 import { api } from '../../platform/github/gh-got-wrapper';
 import { ReleaseResult, GetReleasesConfig } from '../common';
+import { logger } from '../../logger';
 
 const { get: ghGot } = api;
 
@@ -20,6 +21,7 @@ const cacheNamespace = 'datasource-github-releases';
 export async function getPkgReleases({
   lookupName: repo,
 }: GetReleasesConfig): Promise<ReleaseResult | null> {
+  let versions: string[];
   const cachedResult = await renovateCache.get<ReleaseResult>(
     cacheNamespace,
     repo
@@ -28,16 +30,20 @@ export async function getPkgReleases({
   if (cachedResult) {
     return cachedResult;
   }
-  const url = `https://api.github.com/repos/${repo}/releases?per_page=100`;
-  type GitHubRelease = {
-    tag_name: string;
-  }[];
+  try {
+    const url = `https://api.github.com/repos/${repo}/releases?per_page=100`;
+    type GitHubRelease = {
+      tag_name: string;
+    }[];
 
-  const versions = (
-    await ghGot<GitHubRelease>(url, {
-      paginate: true,
-    })
-  ).body.map(o => o.tag_name);
+    versions = (
+      await ghGot<GitHubRelease>(url, {
+        paginate: true,
+      })
+    ).body.map(o => o.tag_name);
+  } catch (err) /* istanbul ignore next */ {
+    logger.debug({ repo, err }, 'Error retrieving from github');
+  }
   // istanbul ignore if
   if (!versions) {
     return null;
diff --git a/lib/datasource/github-tags/index.ts b/lib/datasource/github-tags/index.ts
index f71cfb5f8b..9cfcf22060 100644
--- a/lib/datasource/github-tags/index.ts
+++ b/lib/datasource/github-tags/index.ts
@@ -111,6 +111,7 @@ export async function getDigest(
 export async function getPkgReleases({
   lookupName: repo,
 }: GetReleasesConfig): Promise<ReleaseResult | null> {
+  let versions: string[];
   const cachedResult = await renovateCache.get<ReleaseResult>(
     cacheNamespace,
     getCacheKey(repo, 'tags')
@@ -119,17 +120,24 @@ export async function getPkgReleases({
   if (cachedResult) {
     return cachedResult;
   }
-  // tag
-  const url = `https://api.github.com/repos/${repo}/tags?per_page=100`;
-  type GitHubTag = {
-    name: string;
-  }[];
+  try {
+    // tag
+    const url = `https://api.github.com/repos/${repo}/tags?per_page=100`;
+    type GitHubTag = {
+      name: string;
+    }[];
 
-  const versions = (
-    await ghGot<GitHubTag>(url, {
-      paginate: true,
-    })
-  ).body.map(o => o.name);
+    versions = (
+      await ghGot<GitHubTag>(url, {
+        paginate: true,
+      })
+    ).body.map(o => o.name);
+  } catch (err) {
+    logger.debug({ repo, err }, 'Error retrieving from github');
+  }
+  if (!versions) {
+    return null;
+  }
   const dependency: ReleaseResult = {
     sourceUrl: 'https://github.com/' + repo,
     releases: null,
diff --git a/lib/datasource/gitlab-tags/__snapshots__/index.spec.ts.snap b/lib/datasource/gitlab-tags/__snapshots__/index.spec.ts.snap
index 35f3f358db..73c1a863a9 100644
--- a/lib/datasource/gitlab-tags/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/gitlab-tags/__snapshots__/index.spec.ts.snap
@@ -1,21 +1,5 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`datasource/gitlab-tags getPkgReleases defaults to gitlab.com 1`] = `
-Object {
-  "releases": Array [
-    Object {
-      "gitRef": "v1.0.0",
-      "version": "v1.0.0",
-    },
-    Object {
-      "gitRef": "v1.1.0",
-      "version": "v1.1.0",
-    },
-  ],
-  "sourceUrl": "https://gitlab.com/some/dep2",
-}
-`;
-
 exports[`datasource/gitlab-tags getPkgReleases returns tags 1`] = `
 Object {
   "releases": Array [
diff --git a/lib/datasource/gitlab-tags/index.spec.ts b/lib/datasource/gitlab-tags/index.spec.ts
index 42e90c7b5e..d53087847a 100644
--- a/lib/datasource/gitlab-tags/index.spec.ts
+++ b/lib/datasource/gitlab-tags/index.spec.ts
@@ -23,14 +23,5 @@ describe('datasource/gitlab-tags', () => {
       expect(res).toMatchSnapshot();
       expect(res.releases).toHaveLength(2);
     });
-    it('defaults to gitlab.com', async () => {
-      const body = [{ name: 'v1.0.0' }, { name: 'v1.1.0' }];
-      glGot.mockReturnValueOnce({ headers: {}, body });
-      const res = await gitlab.getPkgReleases({
-        lookupName: 'some/dep2',
-      });
-      expect(res).toMatchSnapshot();
-      expect(res.releases).toHaveLength(2);
-    });
   });
 });
diff --git a/lib/datasource/gitlab-tags/index.ts b/lib/datasource/gitlab-tags/index.ts
index 2df8716909..9a2a46f503 100644
--- a/lib/datasource/gitlab-tags/index.ts
+++ b/lib/datasource/gitlab-tags/index.ts
@@ -1,5 +1,6 @@
 import is from '@sindresorhus/is';
 import { api } from '../../platform/gitlab/gl-got-wrapper';
+import { logger } from '../../logger';
 import { GetReleasesConfig, ReleaseResult } from '../common';
 
 const { get: glGot } = api;
@@ -20,6 +21,7 @@ export async function getPkgReleases({
   const depHost = is.nonEmptyArray(registryUrls)
     ? registryUrls[0].replace(/\/$/, '')
     : 'https://gitlab.com';
+  let versions: string[];
   const cachedResult = await renovateCache.get<ReleaseResult>(
     cacheNamespace,
     getCacheKey(depHost, repo)
@@ -31,17 +33,22 @@ export async function getPkgReleases({
 
   const urlEncodedRepo = encodeURIComponent(repo);
 
-  // tag
-  const url = `${depHost}/api/v4/projects/${urlEncodedRepo}/repository/tags?per_page=100`;
-  type GlTag = {
-    name: string;
-  }[];
+  try {
+    // tag
+    const url = `${depHost}/api/v4/projects/${urlEncodedRepo}/repository/tags?per_page=100`;
+    type GlTag = {
+      name: string;
+    }[];
 
-  const versions = (
-    await glGot<GlTag>(url, {
-      paginate: true,
-    })
-  ).body.map(o => o.name);
+    versions = (
+      await glGot<GlTag>(url, {
+        paginate: true,
+      })
+    ).body.map(o => o.name);
+  } catch (err) {
+    // istanbul ignore next
+    logger.debug({ repo, err }, 'Error retrieving from Gitlab');
+  }
 
   // istanbul ignore if
   if (!versions) {
diff --git a/lib/datasource/gradle-version/index.ts b/lib/datasource/gradle-version/index.ts
index 3fe6b7c39f..b7b212158d 100644
--- a/lib/datasource/gradle-version/index.ts
+++ b/lib/datasource/gradle-version/index.ts
@@ -1,5 +1,6 @@
 import { coerce } from 'semver';
 import is from '@sindresorhus/is';
+import { logger } from '../../logger';
 import got from '../../util/got';
 import {
   DatasourceError,
@@ -55,7 +56,8 @@ export async function getPkgReleases({
         if (err.host === 'services.gradle.org') {
           throw new DatasourceError(err);
         }
-        throw err;
+        logger.debug({ err }, 'gradle-version err');
+        return null;
       }
     })
   );
diff --git a/lib/datasource/helm/index.spec.ts b/lib/datasource/helm/index.spec.ts
index 26301d9d05..3fc7b7cfe7 100644
--- a/lib/datasource/helm/index.spec.ts
+++ b/lib/datasource/helm/index.spec.ts
@@ -55,18 +55,18 @@ describe('datasource/helm', () => {
         })
       ).toBeNull();
     });
-    it('throws for 404', async () => {
-      const err = new Error();
-      err.statusCode = 404;
-      got.mockImplementationOnce(() => {
-        throw err;
-      });
-      await expect(
-        getPkgReleases({
-          lookupName: 'foo/bar',
+    it('returns null for 404', async () => {
+      got.mockImplementationOnce(() =>
+        Promise.reject({
+          statusCode: 404,
+        })
+      );
+      expect(
+        await getPkgReleases({
+          lookupName: 'some_chart',
           registryUrls: ['example-repository.com'],
         })
-      ).rejects.toThrow();
+      ).toBeNull();
     });
     it('throws for 5xx', async () => {
       got.mockImplementationOnce(() =>
@@ -86,6 +86,17 @@ describe('datasource/helm', () => {
       expect(e).toBeDefined();
       expect(e).toMatchSnapshot();
     });
+    it('returns null for unknown error', async () => {
+      got.mockImplementationOnce(() => {
+        throw new Error();
+      });
+      expect(
+        await getPkgReleases({
+          lookupName: 'some_chart',
+          registryUrls: ['example-repository.com'],
+        })
+      ).toBeNull();
+    });
     it('returns null if index.yaml in response is empty', async () => {
       const res = { body: '# A comment' };
       got.mockReturnValueOnce(res);
diff --git a/lib/datasource/helm/index.ts b/lib/datasource/helm/index.ts
index 8ce40dcf62..0494a28306 100644
--- a/lib/datasource/helm/index.ts
+++ b/lib/datasource/helm/index.ts
@@ -23,37 +23,64 @@ export async function getRepositoryData(
       return null;
     }
   } catch (err) {
+    // istanbul ignore if
+    if (err.code === 'ERR_INVALID_URL') {
+      logger.debug(
+        { helmRepository: repository },
+        'helm repository is not a valid URL - skipping'
+      );
+      return null;
+    }
+    // istanbul ignore if
+    if (err.code === 'ENOTFOUND' || err.code === 'EAI_AGAIN') {
+      logger.debug({ err }, 'Could not connect to helm repository');
+      return null;
+    }
+    if (err.statusCode === 404 || err.code === 'ENOTFOUND') {
+      logger.warn({ err }, 'index.yaml lookup error');
+      return null;
+    }
     if (
       err.statusCode === 429 ||
       (err.statusCode >= 500 && err.statusCode < 600)
     ) {
       throw new DatasourceError(err);
     }
-    throw err;
+    // istanbul ignore if
+    if (err.name === 'UnsupportedProtocolError') {
+      logger.debug({ repository }, 'Unsupported protocol');
+      return null;
+    }
+    logger.warn(
+      { err },
+      `helm datasource ${repository} lookup failure: Unknown error`
+    );
+    return null;
   }
-  let doc;
   try {
-    doc = yaml.safeLoad(res.body, { json: true });
+    const doc = yaml.safeLoad(res.body, { json: true });
+    if (!doc) {
+      logger.warn(`Failed to parse index.yaml from ${repository}`);
+      return null;
+    }
+    const result: ReleaseResult[] = Object.entries(doc.entries).map(
+      ([k, v]: [string, any]): ReleaseResult => ({
+        name: k,
+        homepage: v[0].home,
+        sourceUrl: v[0].sources ? v[0].sources[0] : undefined,
+        releases: v.map((x: any) => ({
+          version: x.version,
+        })),
+      })
+    );
+    const cacheMinutes = 20;
+    await renovateCache.set(cacheNamespace, cacheKey, result, cacheMinutes);
+    return result;
   } catch (err) {
-    logger.debug(err);
-  }
-  if (!doc) {
     logger.warn(`Failed to parse index.yaml from ${repository}`);
+    logger.debug(err);
     return null;
   }
-  const result: ReleaseResult[] = Object.entries(doc.entries).map(
-    ([k, v]: [string, any]): ReleaseResult => ({
-      name: k,
-      homepage: v[0].home,
-      sourceUrl: v[0].sources ? v[0].sources[0] : undefined,
-      releases: v.map((x: any) => ({
-        version: x.version,
-      })),
-    })
-  );
-  const cacheMinutes = 20;
-  await renovateCache.set(cacheNamespace, cacheKey, result, cacheMinutes);
-  return result;
 }
 
 export async function getPkgReleases({
diff --git a/lib/datasource/hex/index.spec.ts b/lib/datasource/hex/index.spec.ts
index 8a262049a8..f55cd76087 100644
--- a/lib/datasource/hex/index.spec.ts
+++ b/lib/datasource/hex/index.spec.ts
@@ -21,6 +21,12 @@ describe('datasource/hex', () => {
     beforeEach(() => {
       global.repoCache = {};
     });
+    it('returns null for empty result', async () => {
+      got.mockReturnValueOnce(null);
+      expect(
+        await getPkgReleases({ lookupName: 'non_existent_package' })
+      ).toBeNull();
+    });
     it('returns null for missing fields', async () => {
       got.mockReturnValueOnce({});
       expect(
@@ -32,13 +38,21 @@ describe('datasource/hex', () => {
         await getPkgReleases({ lookupName: 'non_existent_package' })
       ).toBeNull();
     });
-    it('throws for 404', async () => {
-      const err = new Error();
-      err.statusCode = 404;
-      got.mockImplementationOnce(() => {
-        throw err;
-      });
-      await expect(getPkgReleases({ lookupName: 'foo/bar' })).rejects.toThrow();
+    it('returns null for 404', async () => {
+      got.mockImplementationOnce(() =>
+        Promise.reject({
+          statusCode: 404,
+        })
+      );
+      expect(await getPkgReleases({ lookupName: 'some_package' })).toBeNull();
+    });
+    it('returns null for 401', async () => {
+      got.mockImplementationOnce(() =>
+        Promise.reject({
+          statusCode: 401,
+        })
+      );
+      expect(await getPkgReleases({ lookupName: 'some_package' })).toBeNull();
     });
     it('throws for 429', async () => {
       got.mockImplementationOnce(() =>
@@ -60,6 +74,22 @@ describe('datasource/hex', () => {
         getPkgReleases({ lookupName: 'some_crate' })
       ).rejects.toThrowError(DATASOURCE_FAILURE);
     });
+    it('returns null for unknown error', async () => {
+      got.mockImplementationOnce(() => {
+        throw new Error();
+      });
+      expect(await getPkgReleases({ lookupName: 'some_package' })).toBeNull();
+    });
+    it('returns null with wrong auth token', async () => {
+      hostRules.find.mockReturnValueOnce({ token: 'this_simple_token' });
+      got.mockReturnValueOnce(
+        Promise.reject({
+          statusCode: 401,
+        })
+      );
+      const res = await getPkgReleases({ lookupName: 'certifi' });
+      expect(res).toBeNull();
+    });
     it('processes real data', async () => {
       got.mockReturnValueOnce({
         body: res1,
diff --git a/lib/datasource/hex/index.ts b/lib/datasource/hex/index.ts
index a8ee3f866f..3067723be6 100644
--- a/lib/datasource/hex/index.ts
+++ b/lib/datasource/hex/index.ts
@@ -64,12 +64,23 @@ export async function getPkgReleases({
 
     return result;
   } catch (err) {
+    const errorData = { lookupName, err };
+
     if (
       err.statusCode === 429 ||
       (err.statusCode >= 500 && err.statusCode < 600)
     ) {
       throw new DatasourceError(err);
     }
-    throw err;
+
+    if (err.statusCode === 401) {
+      logger.debug(errorData, 'Authorization error');
+    } else if (err.statusCode === 404) {
+      logger.debug(errorData, 'Package lookup error');
+    } else {
+      logger.warn(errorData, 'hex lookup failure: Unknown error');
+    }
   }
+
+  return null;
 }
diff --git a/lib/datasource/index.spec.ts b/lib/datasource/index.spec.ts
index 5d48d9ddf4..7f0a7ea63f 100644
--- a/lib/datasource/index.spec.ts
+++ b/lib/datasource/index.spec.ts
@@ -72,7 +72,7 @@ describe('datasource/index', () => {
   it('trims sourceUrl', async () => {
     npmDatasource.getPkgReleases.mockResolvedValue({
       sourceUrl: ' https://abc.com',
-      releases: [{ version: '1.0.0' }, { version: '1.1.0' }],
+      releases: [],
     });
     const res = await datasource.getPkgReleases({
       datasource: datasourceNpm.id,
diff --git a/lib/datasource/index.ts b/lib/datasource/index.ts
index 6b20b1746d..12ad574501 100644
--- a/lib/datasource/index.ts
+++ b/lib/datasource/index.ts
@@ -77,45 +77,12 @@ export async function getPkgReleases(
       ...config,
       lookupName,
     });
-  } catch (err) /* istanbul ignore next */ {
-    logger.trace({ err }, 'getPkgReleases err');
-    if (err instanceof DatasourceError) {
-      err.datasource = datasource;
-      err.lookupName = lookupName;
-      throw err;
+  } catch (e) /* istanbul ignore next */ {
+    if (e instanceof DatasourceError) {
+      e.datasource = datasource;
+      e.lookupName = lookupName;
     }
-    const { name, url, code, statusCode, statusMessage } = err;
-    const logMeta = {
-      datasource,
-      lookupName,
-      url,
-      code,
-      statusCode,
-      statusMessage,
-    };
-    const log = (reason: string, level = 'debug'): void =>
-      logger[level]({ ...logMeta, reason }, `Datasource Error (ignored)`);
-    if (name === 'UnsupportedProtocolError') {
-      log('Unsupported Protocol');
-    } else if (name === 'SyntaxError') {
-      log('Could not parse response');
-    } else if (code === 'ERR_INVALID_URL') {
-      log('Invalid URL');
-    } else if (code === 'ENOTFOUND' || code === 'EAI_AGAIN') {
-      log('Connection Error');
-    } else if (statusCode === 401 || statusCode === 403) {
-      log('Unauthorized');
-    } else if (statusCode === 404 || code === 'ENOTFOUND') {
-      log('Not Found');
-    } else if (statusCode === 429) {
-      log('Rate Limited');
-    } else if (statusCode >= 500 && statusCode < 600) {
-      log('Server Error');
-    } else {
-      log('Unknown', 'info');
-      logger.debug({ err }, 'Datasource Error err');
-    }
-    return null;
+    throw e;
   }
   if (!res) {
     return res;
diff --git a/lib/datasource/metadata.spec.ts b/lib/datasource/metadata.spec.ts
index a9b53068db..5a70c827b2 100644
--- a/lib/datasource/metadata.spec.ts
+++ b/lib/datasource/metadata.spec.ts
@@ -78,17 +78,4 @@ describe('datasource/metadata', () => {
     addMetaData(dep, datasource, lookupName);
     expect(dep.sourceUrl).toEqual('https://github.com/mockk/mockk');
   });
-  it('Should move github homepage to sourceUrl', () => {
-    const dep = {
-      homepage: 'http://www.github.com/mockk/mockk/',
-      releases: [{ version: '1.9.3' }],
-      sourceUrl: undefined,
-    };
-    const datasource = datasourceMaven.id;
-    const lookupName = 'io.mockk:mockk';
-
-    addMetaData(dep, datasource, lookupName);
-    expect(dep.sourceUrl).toEqual('https://github.com/mockk/mockk');
-    expect(dep.homepage).toBeUndefined();
-  });
 });
diff --git a/lib/datasource/orb/index.spec.ts b/lib/datasource/orb/index.spec.ts
index e82e7d71d6..5ede9da3e9 100644
--- a/lib/datasource/orb/index.spec.ts
+++ b/lib/datasource/orb/index.spec.ts
@@ -33,6 +33,14 @@ describe('datasource/orb', () => {
       global.repoCache = {};
       return global.renovateCache.rmAll();
     });
+    it('returns null for empty result', async () => {
+      got.post.mockReturnValueOnce({ body: {} });
+      expect(
+        await datasource.getPkgReleases({
+          lookupName: 'hyper-expanse/library-release-workflows',
+        })
+      ).toBeNull();
+    });
     it('returns null for missing orb', async () => {
       got.post.mockReturnValueOnce({ body: { data: {} } });
       expect(
@@ -41,6 +49,28 @@ describe('datasource/orb', () => {
         })
       ).toBeNull();
     });
+    it('returns null for 404', async () => {
+      got.mockImplementationOnce(() =>
+        Promise.reject({
+          statusCode: 404,
+        })
+      );
+      expect(
+        await datasource.getPkgReleases({
+          lookupName: 'hyper-expanse/library-release-workflows',
+        })
+      ).toBeNull();
+    });
+    it('returns null for unknown error', async () => {
+      got.mockImplementationOnce(() => {
+        throw new Error();
+      });
+      expect(
+        await datasource.getPkgReleases({
+          lookupName: 'hyper-expanse/library-release-workflows',
+        })
+      ).toBeNull();
+    });
     it('processes real data', async () => {
       got.post.mockReturnValueOnce({
         body: orbData,
diff --git a/lib/datasource/orb/index.ts b/lib/datasource/orb/index.ts
index a300bac9c5..2438509863 100644
--- a/lib/datasource/orb/index.ts
+++ b/lib/datasource/orb/index.ts
@@ -35,35 +35,45 @@ export async function getPkgReleases({
     query: `{orb(name:"${lookupName}"){name, homeUrl, versions {version, createdAt}}}`,
     variables: {},
   };
-  const res: OrbRelease = (
-    await got.post(url, {
-      body,
-      hostType: id,
-      json: true,
-      retry: 5,
-    })
-  ).body.data.orb;
-  if (!res) {
-    logger.debug({ lookupName }, 'Failed to look up orb');
+  try {
+    const res: OrbRelease = (
+      await got.post(url, {
+        body,
+        hostType: id,
+        json: true,
+        retry: 5,
+      })
+    ).body.data.orb;
+    if (!res) {
+      logger.debug({ lookupName }, 'Failed to look up orb');
+      return null;
+    }
+    // Simplify response before caching and returning
+    const dep: ReleaseResult = {
+      name: lookupName,
+      versions: {},
+      releases: null,
+    };
+    if (res.homeUrl && res.homeUrl.length) {
+      dep.homepage = res.homeUrl;
+    }
+    dep.homepage =
+      dep.homepage || `https://circleci.com/orbs/registry/orb/${lookupName}`;
+    const releases = res.versions.map(v => v.version);
+    dep.releases = releases.map(version => ({
+      version,
+    }));
+    logger.trace({ dep }, 'dep');
+    const cacheMinutes = 15;
+    await renovateCache.set(cacheNamespace, cacheKey, dep, cacheMinutes);
+    return dep;
+  } catch (err) /* istanbul ignore next */ {
+    logger.debug({ err }, 'CircleCI Orb lookup error');
+    if (err.statusCode === 404 || err.code === 'ENOTFOUND') {
+      logger.debug({ lookupName }, `CircleCI Orb lookup failure: not found`);
+      return null;
+    }
+    logger.warn({ lookupName }, 'CircleCI Orb lookup failure: Unknown error');
     return null;
   }
-  // Simplify response before caching and returning
-  const dep: ReleaseResult = {
-    name: lookupName,
-    versions: {},
-    releases: null,
-  };
-  if (res.homeUrl && res.homeUrl.length) {
-    dep.homepage = res.homeUrl;
-  }
-  dep.homepage =
-    dep.homepage || `https://circleci.com/orbs/registry/orb/${lookupName}`;
-  const releases = res.versions.map(v => v.version);
-  dep.releases = releases.map(version => ({
-    version,
-  }));
-  logger.trace({ dep }, 'dep');
-  const cacheMinutes = 15;
-  await renovateCache.set(cacheNamespace, cacheKey, dep, cacheMinutes);
-  return dep;
 }
diff --git a/lib/datasource/terraform-module/index.spec.ts b/lib/datasource/terraform-module/index.spec.ts
index 031c0d0da8..a7483e0294 100644
--- a/lib/datasource/terraform-module/index.spec.ts
+++ b/lib/datasource/terraform-module/index.spec.ts
@@ -25,6 +25,28 @@ describe('datasource/terraform-module', () => {
         })
       ).toBeNull();
     });
+    it('returns null for 404', async () => {
+      got.mockImplementationOnce(() =>
+        Promise.reject({
+          statusCode: 404,
+        })
+      );
+      expect(
+        await terraform.getPkgReleases({
+          lookupName: 'hashicorp/consul/aws',
+        })
+      ).toBeNull();
+    });
+    it('returns null for unknown error', async () => {
+      got.mockImplementationOnce(() => {
+        throw new Error();
+      });
+      expect(
+        await terraform.getPkgReleases({
+          lookupName: 'hashicorp/consul/aws',
+        })
+      ).toBeNull();
+    });
     it('processes real data', async () => {
       got.mockReturnValueOnce({
         body: JSON.parse(consulData),
diff --git a/lib/datasource/terraform-module/index.ts b/lib/datasource/terraform-module/index.ts
index 4bdde279b9..a9578d7753 100644
--- a/lib/datasource/terraform-module/index.ts
+++ b/lib/datasource/terraform-module/index.ts
@@ -71,34 +71,52 @@ export async function getPkgReleases({
   if (cachedResult) {
     return cachedResult;
   }
-  const res: TerraformRelease = (
-    await got(pkgUrl, {
-      json: true,
-      hostType: id,
-    })
-  ).body;
-  const returnedName = res.namespace + '/' + res.name + '/' + res.provider;
-  if (returnedName !== repository) {
-    logger.warn({ pkgUrl }, 'Terraform registry result mismatch');
+  try {
+    const res: TerraformRelease = (
+      await got(pkgUrl, {
+        json: true,
+        hostType: id,
+      })
+    ).body;
+    const returnedName = res.namespace + '/' + res.name + '/' + res.provider;
+    if (returnedName !== repository) {
+      logger.warn({ pkgUrl }, 'Terraform registry result mismatch');
+      return null;
+    }
+    // Simplify response before caching and returning
+    const dep: ReleaseResult = {
+      name: repository,
+      versions: {},
+      releases: null,
+    };
+    if (res.source) {
+      dep.sourceUrl = res.source;
+    }
+    dep.releases = res.versions.map(version => ({
+      version,
+    }));
+    if (pkgUrl.startsWith('https://registry.terraform.io/')) {
+      dep.homepage = `https://registry.terraform.io/modules/${repository}`;
+    }
+    logger.trace({ dep }, 'dep');
+    const cacheMinutes = 30;
+    await renovateCache.set(cacheNamespace, pkgUrl, dep, cacheMinutes);
+    return dep;
+  } catch (err) {
+    if (err.statusCode === 404 || err.code === 'ENOTFOUND') {
+      logger.debug(
+        { lookupName },
+        `Terraform registry lookup failure: not found`
+      );
+      logger.debug({
+        err,
+      });
+      return null;
+    }
+    logger.warn(
+      { err, lookupName },
+      'Terraform registry failure: Unknown error'
+    );
     return null;
   }
-  // Simplify response before caching and returning
-  const dep: ReleaseResult = {
-    name: repository,
-    versions: {},
-    releases: null,
-  };
-  if (res.source) {
-    dep.sourceUrl = res.source;
-  }
-  dep.releases = res.versions.map(version => ({
-    version,
-  }));
-  if (pkgUrl.startsWith('https://registry.terraform.io/')) {
-    dep.homepage = `https://registry.terraform.io/modules/${repository}`;
-  }
-  logger.trace({ dep }, 'dep');
-  const cacheMinutes = 30;
-  await renovateCache.set(cacheNamespace, pkgUrl, dep, cacheMinutes);
-  return dep;
 }
diff --git a/lib/datasource/terraform-provider/index.spec.ts b/lib/datasource/terraform-provider/index.spec.ts
index d1112a0c94..1b9155da27 100644
--- a/lib/datasource/terraform-provider/index.spec.ts
+++ b/lib/datasource/terraform-provider/index.spec.ts
@@ -17,6 +17,36 @@ describe('datasource/terraform', () => {
       global.repoCache = {};
       return global.renovateCache.rmAll();
     });
+    it('returns null for empty result', async () => {
+      got.mockReturnValueOnce({ body: {} });
+      expect(
+        await terraformProvider.getPkgReleases({
+          lookupName: 'azurerm',
+        })
+      ).toBeNull();
+    });
+    it('returns null for 404', async () => {
+      got.mockImplementationOnce(() =>
+        Promise.reject({
+          statusCode: 404,
+        })
+      );
+      expect(
+        await terraformProvider.getPkgReleases({
+          lookupName: 'azurerm',
+        })
+      ).toBeNull();
+    });
+    it('returns null for unknown error', async () => {
+      got.mockImplementationOnce(() => {
+        throw new Error();
+      });
+      expect(
+        await terraformProvider.getPkgReleases({
+          lookupName: 'azurerm',
+        })
+      ).toBeNull();
+    });
     it('processes real data', async () => {
       got.mockReturnValueOnce({
         body: JSON.parse(consulData),
diff --git a/lib/datasource/terraform-provider/index.ts b/lib/datasource/terraform-provider/index.ts
index cc1f5c557e..0c3544d3bd 100644
--- a/lib/datasource/terraform-provider/index.ts
+++ b/lib/datasource/terraform-provider/index.ts
@@ -34,29 +34,47 @@ export async function getPkgReleases({
   if (cachedResult) {
     return cachedResult;
   }
-  const res: TerraformProvider = (
-    await got(pkgUrl, {
-      json: true,
-      hostType: id,
-    })
-  ).body;
-  // Simplify response before caching and returning
-  const dep: ReleaseResult = {
-    name: repository,
-    versions: {},
-    releases: null,
-  };
-  if (res.source) {
-    dep.sourceUrl = res.source;
+  try {
+    const res: TerraformProvider = (
+      await got(pkgUrl, {
+        json: true,
+        hostType: id,
+      })
+    ).body;
+    // Simplify response before caching and returning
+    const dep: ReleaseResult = {
+      name: repository,
+      versions: {},
+      releases: null,
+    };
+    if (res.source) {
+      dep.sourceUrl = res.source;
+    }
+    dep.releases = res.versions.map(version => ({
+      version,
+    }));
+    if (pkgUrl.startsWith('https://registry.terraform.io/')) {
+      dep.homepage = `https://registry.terraform.io/providers/${repository}`;
+    }
+    logger.trace({ dep }, 'dep');
+    const cacheMinutes = 30;
+    await renovateCache.set(cacheNamespace, pkgUrl, dep, cacheMinutes);
+    return dep;
+  } catch (err) {
+    if (err.statusCode === 404 || err.code === 'ENOTFOUND') {
+      logger.debug(
+        { lookupName },
+        `Terraform registry lookup failure: not found`
+      );
+      logger.debug({
+        err,
+      });
+      return null;
+    }
+    logger.warn(
+      { err, lookupName },
+      'Terraform registry failure: Unknown error'
+    );
+    return null;
   }
-  dep.releases = res.versions.map(version => ({
-    version,
-  }));
-  if (pkgUrl.startsWith('https://registry.terraform.io/')) {
-    dep.homepage = `https://registry.terraform.io/providers/${repository}`;
-  }
-  logger.trace({ dep }, 'dep');
-  const cacheMinutes = 30;
-  await renovateCache.set(cacheNamespace, pkgUrl, dep, cacheMinutes);
-  return dep;
 }
diff --git a/lib/util/clone.ts b/lib/util/clone.ts
index ea80f56d2e..075e35be3a 100644
--- a/lib/util/clone.ts
+++ b/lib/util/clone.ts
@@ -1,3 +1,3 @@
 export function clone<T>(input: T): T {
-  return input ? JSON.parse(JSON.stringify(input)) : input;
+  return JSON.parse(JSON.stringify(input));
 }
diff --git a/lib/datasource/docker/got.spec.ts b/lib/util/got/got.spec.ts
similarity index 97%
rename from lib/datasource/docker/got.spec.ts
rename to lib/util/got/got.spec.ts
index 2d6180afec..a45da3edde 100644
--- a/lib/datasource/docker/got.spec.ts
+++ b/lib/util/got/got.spec.ts
@@ -1,5 +1,5 @@
 import nock from 'nock';
-import { getConfigResponse } from '.';
+import { getConfigResponse } from '../../datasource/docker';
 
 describe('getConfigResponse', () => {
   beforeEach(() => {
diff --git a/lib/workers/repository/process/lookup/__snapshots__/index.spec.ts.snap b/lib/workers/repository/process/lookup/__snapshots__/index.spec.ts.snap
index 4fa22f3a71..6dfd7d548e 100644
--- a/lib/workers/repository/process/lookup/__snapshots__/index.spec.ts.snap
+++ b/lib/workers/repository/process/lookup/__snapshots__/index.spec.ts.snap
@@ -194,6 +194,8 @@ Object {
 }
 `;
 
+exports[`workers/repository/process/lookup .lookupUpdates() handles github 404 1`] = `Array []`;
+
 exports[`workers/repository/process/lookup .lookupUpdates() handles packagist 1`] = `Array []`;
 
 exports[`workers/repository/process/lookup .lookupUpdates() handles pypi 404 1`] = `Array []`;
diff --git a/lib/workers/repository/process/lookup/index.spec.ts b/lib/workers/repository/process/lookup/index.spec.ts
index 6049a7a2c6..88c87c4d09 100644
--- a/lib/workers/repository/process/lookup/index.spec.ts
+++ b/lib/workers/repository/process/lookup/index.spec.ts
@@ -17,6 +17,7 @@ import * as datasourceNpm from '../../../../datasource/npm';
 import * as datasourcePypi from '../../../../datasource/pypi';
 import * as datasourcePackagist from '../../../../datasource/packagist';
 import * as datasourceDocker from '../../../../datasource/docker';
+import * as datasourceGithubTags from '../../../../datasource/github-tags';
 import * as datasourceGitSubmodules from '../../../../datasource/git-submodules';
 
 jest.mock('../../../../datasource/docker');
@@ -981,13 +982,23 @@ describe('workers/repository/process/lookup', () => {
         .reply(200, qJson);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot();
     });
+    it('handles github 404', async () => {
+      config.depName = 'foo';
+      config.datasource = datasourceGithubTags.id;
+      config.packageFile = 'package.json';
+      config.currentValue = '1.0.0';
+      nock('https://pypi.org')
+        .get('/pypi/foo/json')
+        .reply(404);
+      expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot();
+    });
     it('handles pypi 404', async () => {
       config.depName = 'foo';
       config.datasource = datasourcePypi.id;
       config.packageFile = 'requirements.txt';
       config.currentValue = '1.0.0';
-      nock('https://pypi.org')
-        .get('/pypi/foo/json')
+      nock('https://api.github.com')
+        .get('/repos/some/repo/git/refs/tags?per_page=100')
         .reply(404);
       expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot();
     });
-- 
GitLab