diff --git a/lib/modules/datasource/rubygems/__snapshots__/index.spec.ts.snap b/lib/modules/datasource/rubygems/__snapshots__/index.spec.ts.snap index 523f011b8492076ddd5ef2ca1def923ad842c9bd..7405c450feeec860a423f1d4147b4c9d6fdf9537 100644 --- a/lib/modules/datasource/rubygems/__snapshots__/index.spec.ts.snap +++ b/lib/modules/datasource/rubygems/__snapshots__/index.spec.ts.snap @@ -1025,20 +1025,6 @@ exports[`modules/datasource/rubygems/index getReleases returns a dep for GitHub } `; -exports[`modules/datasource/rubygems/index getReleases returns a dep for rubygems.org package hit 1`] = ` -{ - "registryUrl": "https://rubygems.org", - "releases": [ - { - "version": "0.1.0", - }, - { - "version": "0.1.1", - }, - ], -} -`; - exports[`modules/datasource/rubygems/index getReleases uses multiple source urls 1`] = ` { "changelogUrl": "https://www.railschangelog.com/", @@ -2406,20 +2392,6 @@ exports[`modules/datasource/rubygems/index getReleases uses multiple source urls } `; -exports[`modules/datasource/rubygems/index getReleases uses rubygems.org if no registry urls were provided 1`] = ` -{ - "registryUrl": "https://rubygems.org", - "releases": [ - { - "version": "0.1.0", - }, - { - "version": "0.1.1", - }, - ], -} -`; - exports[`modules/datasource/rubygems/index getReleases works with real data 1`] = ` { "changelogUrl": "https://www.railschangelog.com/", diff --git a/lib/modules/datasource/rubygems/index.spec.ts b/lib/modules/datasource/rubygems/index.spec.ts index 475e55ab7143f94cedde0e4dcacd9f8b07cb494a..81fd7de90a94c6bab9761f9d59f95bed457c16d5 100644 --- a/lib/modules/datasource/rubygems/index.spec.ts +++ b/lib/modules/datasource/rubygems/index.spec.ts @@ -68,66 +68,39 @@ describe('modules/datasource/rubygems/index', () => { httpMock .scope('https://rubygems.org') .get('/versions') - .reply(200, rubygemsOrgVersions); + .reply(200, rubygemsOrgVersions) + .get('/api/v1/gems/1pass.json') + .reply(200, { name: '1pass' }) + .get('/api/v1/versions/1pass.json') + .reply(200, [ + { number: '0.1.0', created_at: '2020-01-01' }, + { number: '0.1.1', created_at: '2021-01-01' }, + ]); + const res = await getPkgReleases({ versioning: rubyVersioning.id, datasource: RubyGemsDatasource.id, packageName: '1pass', registryUrls: [], }); - expect(res).not.toBeNull(); - expect(res?.releases).toHaveLength(2); - expect(res).toMatchSnapshot(); - expect( - res?.releases.find((release) => release.version === '0.1.1') - ).toBeDefined(); - expect( - res?.releases.find((release) => release.version === '0.1.2') - ).toBeUndefined(); - }); - it('returns a dep for a package hit on an arbitrary registry that only supports old format endpoints', async () => { - const contribsysComVersions = ` - created_at: 2022-06-15T17:10:25+00:00 - --- - sidekiq-ent 0.7.10,1.0.0,1.0.1,1.2.4,2.0.0,2.1.2 4c0f62a49b15b4775b7fb6824ec34d45 - `; - httpMock - .scope('https://enterprise.contribsys.com') - .get('/versions') - .reply(200, contribsysComVersions); - const res = await getPkgReleases({ - versioning: rubyVersioning.id, - datasource: RubyGemsDatasource.id, - packageName: 'sidekiq-ent', - registryUrls: ['https://enterprise.contribsys.com'], - }); - expect(res).not.toBeNull(); - expect(res?.releases).toHaveLength(6); expect(res).toMatchObject({ - registryUrl: 'https://enterprise.contribsys.com', - releases: expect.arrayContaining([ - { - version: '0.7.10', - }, - { - version: '1.0.0', - }, - ]), + releases: [{ version: '0.1.0' }, { version: '0.1.1' }], }); - expect( - res?.releases.find((release) => release.version === '2.1.2') - ).toBeDefined(); - expect( - res?.releases.find((release) => release.version === '2.1.3') - ).toBeUndefined(); }); it('uses rubygems.org if no registry urls were provided', async () => { httpMock .scope('https://rubygems.org') .get('/versions') - .reply(200, rubygemsOrgVersions); + .reply(200, rubygemsOrgVersions) + .get('/api/v1/gems/1pass.json') + .reply(200, { name: '1pass' }) + .get('/api/v1/versions/1pass.json') + .reply(200, [ + { number: '0.1.0', created_at: '2020-01-01' }, + { number: '0.1.1', created_at: '2021-01-01' }, + ]); expect( await getPkgReleases({ @@ -144,9 +117,10 @@ describe('modules/datasource/rubygems/index', () => { packageName: '1pass', registryUrls: [], }); - expect(res).not.toBeNull(); - expect(res?.releases).toHaveLength(2); - expect(res).toMatchSnapshot(); + + expect(res).toMatchObject({ + releases: [{ version: '0.1.0' }, { version: '0.1.1' }], + }); }); it('works with real data', async () => { diff --git a/lib/modules/datasource/rubygems/index.ts b/lib/modules/datasource/rubygems/index.ts index 8efb93ddb6847116be8c229884768a55b057d835..580f487b59d80ac2207e72248cc95aa5796521c9 100644 --- a/lib/modules/datasource/rubygems/index.ts +++ b/lib/modules/datasource/rubygems/index.ts @@ -7,16 +7,20 @@ import * as rubyVersioning from '../../versioning/ruby'; import { Datasource } from '../datasource'; import type { GetReleasesConfig, Release, ReleaseResult } from '../types'; import { RubygemsHttp } from './http'; +import { MetadataCache } from './metadata-cache'; import { GemMetadata, GemVersions, MarshalledVersionInfo } from './schema'; import { VersionsEndpointCache } from './versions-endpoint-cache'; export class RubyGemsDatasource extends Datasource { static readonly id = 'rubygems'; + private metadataCache: MetadataCache; + constructor() { super(RubyGemsDatasource.id); this.http = new RubygemsHttp(RubyGemsDatasource.id); this.versionsEndpointCache = new VersionsEndpointCache(this.http); + this.metadataCache = new MetadataCache(this.http); } override readonly defaultRegistryUrls = ['https://rubygems.org']; @@ -27,13 +31,6 @@ export class RubyGemsDatasource extends Datasource { private readonly versionsEndpointCache: VersionsEndpointCache; - @cache({ - namespace: `datasource-${RubyGemsDatasource.id}`, - key: ({ registryUrl, packageName }: GetReleasesConfig) => - // TODO: types (#7154) - /* eslint-disable @typescript-eslint/restrict-template-expressions */ - `${registryUrl}/${packageName}`, - }) async getReleases({ packageName, registryUrl, @@ -51,7 +48,12 @@ export class RubyGemsDatasource extends Datasource { if (cachedVersions.type === 'success') { const { versions } = cachedVersions; - return { releases: versions.map((version) => ({ version })) }; + const result = await this.metadataCache.getRelease( + registryUrl, + packageName, + versions + ); + return result; } const registryHostname = parseUrl(registryUrl)?.hostname; @@ -87,6 +89,13 @@ export class RubyGemsDatasource extends Datasource { } } + @cache({ + namespace: `datasource-${RubyGemsDatasource.id}`, + key: ({ registryUrl, packageName }: GetReleasesConfig) => + // TODO: types (#7154) + /* eslint-disable @typescript-eslint/restrict-template-expressions */ + `metadata:${registryUrl}/${packageName}`, + }) async fetchGemMetadata( registryUrl: string, packageName: string @@ -106,6 +115,13 @@ export class RubyGemsDatasource extends Datasource { } } + @cache({ + namespace: `datasource-${RubyGemsDatasource.id}`, + key: ({ registryUrl, packageName }: GetReleasesConfig) => + // TODO: types (#7154) + /* eslint-disable @typescript-eslint/restrict-template-expressions */ + `versions:${registryUrl}/${packageName}`, + }) async fetchGemVersions( registryUrl: string, packageName: string @@ -162,6 +178,13 @@ export class RubyGemsDatasource extends Datasource { return result; } + @cache({ + namespace: `datasource-${RubyGemsDatasource.id}`, + key: ({ registryUrl, packageName }: GetReleasesConfig) => + // TODO: types (#7154) + /* eslint-disable @typescript-eslint/restrict-template-expressions */ + `dependencies:${registryUrl}/${packageName}`, + }) async getReleasesViaFallbackAPI( registryUrl: string, packageName: string