diff --git a/lib/datasource/github.js b/lib/datasource/github.js index 59448cbf73c40fb1874b548c417bddc6c1ab47dd..287109edac3001cf143c9fb89f6af3ee845d0222 100644 --- a/lib/datasource/github.js +++ b/lib/datasource/github.js @@ -1,11 +1,55 @@ +const cacache = require('cacache/en'); +const os = require('os'); +const { DateTime } = require('luxon'); + const ghGot = require('../platform/github/gh-got-wrapper'); const versioning = require('../versioning'); module.exports = { getPreset, getPkgReleases, + rmAllCache, }; +const datasourceCache = + (process.env.RENOVATE_TMPDIR || os.tmpdir()) + + '/renovate-gh-datasource-cache-v1'; + +async function getCachedResult(repo, type) { + try { + const cacheVal = await cacache.get(datasourceCache, `${repo}-${type}`); + const cachedResult = JSON.parse(cacheVal.data.toString()); + if (cachedResult) { + if (DateTime.local() < DateTime.fromISO(cachedResult.expiry)) { + logger.debug( + { repo, type }, + 'Returning cached github datasource result' + ); + delete cachedResult.expiry; + return cachedResult; + } + // istanbul ignore next + logger.debug('Cache expiry'); + } + } catch (err) { + logger.debug('Cache miss'); + } + return null; +} + +async function setCachedResult(repo, type, res) { + logger.debug({ repo, type }, 'Saving cached github datasource'); + await cacache.put( + datasourceCache, + `${repo}-${type}`, + JSON.stringify({ ...res, expiry: DateTime.local().plus({ minutes: 10 }) }) + ); +} + +async function rmAllCache() { + await cacache.rm.all(datasourceCache); +} + const map = new Map(); async function getPreset(pkgName, presetName = 'default') { @@ -35,6 +79,10 @@ async function getPkgReleases(purl, config) { const { versionScheme } = config || {}; const { fullname: repo, qualifiers: options } = purl; let versions; + const cachedResult = await getCachedResult(repo, options.ref); + if (cachedResult) { + return cachedResult; + } try { if (options.ref === 'release') { const url = `repos/${repo}/releases?per_page=100`; @@ -69,5 +117,6 @@ async function getPkgReleases(purl, config) { version: options.sanitize === 'true' ? isVersion(version) : version, gitRef: version, })); + setCachedResult(repo, options.ref, dependency); return dependency; } diff --git a/test/datasource/__snapshots__/github.spec.js.snap b/test/datasource/__snapshots__/github.spec.js.snap index f3591d40bfb6380e329c0f362fa2d7c76a125136..7520f91335761bc8a0da00054d629493db268125 100644 --- a/test/datasource/__snapshots__/github.spec.js.snap +++ b/test/datasource/__snapshots__/github.spec.js.snap @@ -31,3 +31,19 @@ Object { "repositoryUrl": "https://github.com/some/dep", } `; + +exports[`datasource/github getPkgReleases returns releases from cache 1`] = ` +Object { + "releases": Array [ + Object { + "gitRef": "1.0.0", + "version": "1.0.0", + }, + Object { + "gitRef": "v1.1.0", + "version": "v1.1.0", + }, + ], + "repositoryUrl": "https://github.com/some/dep", +} +`; diff --git a/test/datasource/github.spec.js b/test/datasource/github.spec.js index 86d35bfc6a829eb865d5fbd6ec1c5fc6ee09dc11..597d26bd4ee84736f81f095d34db9c347d8c62c6 100644 --- a/test/datasource/github.spec.js +++ b/test/datasource/github.spec.js @@ -1,3 +1,5 @@ +const delay = require('delay'); + const datasource = require('../../lib/datasource'); const github = require('../../lib/datasource/github'); const ghGot = require('../../lib/platform/github/gh-got-wrapper'); @@ -37,6 +39,7 @@ describe('datasource/github', () => { }); }); describe('getPkgReleases', () => { + beforeAll(() => github.rmAllCache()); it('returns cleaned tags', async () => { const body = [ { name: 'a' }, @@ -71,6 +74,17 @@ describe('datasource/github', () => { res.releases.find(release => release.version === 'v1.1.0') ).toBeDefined(); }); + it('returns releases from cache', async () => { + await delay(1000); + const res = await datasource.getPkgReleases( + 'pkg:github/some/dep?ref=release' + ); + expect(res).toMatchSnapshot(); + expect(res.releases).toHaveLength(2); + expect( + res.releases.find(release => release.version === 'v1.1.0') + ).toBeDefined(); + }); it('returns null for invalid ref', async () => { expect( await datasource.getPkgReleases('pkg:github/some/dep?ref=invalid')