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