diff --git a/lib/datasource/npm.js b/lib/datasource/npm.js index 81486f3f48bbfec19018dd36de588fc57533d173..f5d724e3aae9bc63ffb9aff322dba9cf32c0c084 100644 --- a/lib/datasource/npm.js +++ b/lib/datasource/npm.js @@ -110,7 +110,14 @@ async function getDependency(name, retries = 5) { headers, })).body; if (!res.versions || !Object.keys(res.versions).length) { - return null; + // Registry returned a 200 OK but with no versions + if (retries <= 0) { + logger.info({ name }, 'No versions returned'); + return null; + } + logger.info('No versions returned, retrying'); + await delay(5000 / retries); + return getDependency(name, 0); } // Determine repository URL let repositoryUrl; @@ -142,18 +149,23 @@ async function getDependency(name, retries = 5) { return dep; } catch (err) { if (err.statusCode === 401) { - logger.info({ err, name }, `Dependency lookup unauthorized`); + logger.info({ err, name }, `Dependency lookup failure: unauthorized`); return null; } if (err.statusCode === 404) { - logger.info({ name }, `Dependency not found`); + logger.info({ name }, `Dependency lookup failure: not found`); logger.debug({ err }); return null; } if (err.name === 'ParseError') { // Registry returned a 200 OK but got failed to parse it - logger.warn({ err }, 'npm registry failure: ParseError'); - throw new Error('registry-failure'); + if (retries <= 0) { + logger.warn({ err }, 'npm registry failure: ParseError'); + throw new Error('registry-failure'); + } + logger.info({ err }, 'npm registry failure: ParseError, retrying'); + await delay(5000 / retries); + return getDependency(name, retries - 1); } if (err.statusCode === 429) { // This is bad if it ever happens, so we should error diff --git a/test/manager/npm/__snapshots__/registry.spec.js.snap b/test/manager/npm/__snapshots__/registry.spec.js.snap index 943cf847f4ddc384ada36ffc7136cb014dc33e97..aeba6372bfc40fcbfa7baf24ffcc888066c33c5f 100644 --- a/test/manager/npm/__snapshots__/registry.spec.js.snap +++ b/test/manager/npm/__snapshots__/registry.spec.js.snap @@ -51,6 +51,8 @@ Object { } `; +exports[`api/npm should retry when 408 or 5xx 1`] = `null`; + exports[`api/npm should send an authorization header if provided 1`] = ` Object { "dist-tags": Object { diff --git a/test/manager/npm/registry.spec.js b/test/manager/npm/registry.spec.js index 857cce9c3873528d7c759bbf160359c4ef33b477..6a4089c2a4d9e74dc64975ad350a1ec8f2a58688 100644 --- a/test/manager/npm/registry.spec.js +++ b/test/manager/npm/registry.spec.js @@ -35,7 +35,10 @@ describe('api/npm', () => { nock('https://registry.npmjs.org') .get('/foobar') .reply(200, missingVersions); - const res = await npm.getDependency('foobar'); + nock('https://registry.npmjs.org') + .get('/foobar') + .reply(200, missingVersions); + const res = await npm.getDependency('foobar', 1); expect(res).toBe(null); }); it('should fetch package info from npm', async () => { @@ -70,12 +73,15 @@ describe('api/npm', () => { expect(res).toBeNull(); }); it('should throw error for unparseable', async () => { + nock('https://registry.npmjs.org') + .get('/foobar') + .reply(200, 'oops'); nock('https://registry.npmjs.org') .get('/foobar') .reply(200, 'oops'); let e; try { - await npm.getDependency('foobar'); + await npm.getDependency('foobar', 1); } catch (err) { e = err; } @@ -127,8 +133,8 @@ describe('api/npm', () => { nock('https://registry.npmjs.org') .get('/foobar') .reply(200); - const res = await npm.getDependency('foobar'); - expect(res).toBe(null); + const res = await npm.getDependency('foobar', 2); + expect(res).toMatchSnapshot(); }); it('should throw error for others', async () => { nock('https://registry.npmjs.org')