diff --git a/lib/modules/datasource/rubygems/metadata-cache.spec.ts b/lib/modules/datasource/rubygems/metadata-cache.spec.ts index 80f86ae8b428526707294c1ab50e2b9a37b6010f..c1bc38244755dc487af115a5959938f9c0713941 100644 --- a/lib/modules/datasource/rubygems/metadata-cache.spec.ts +++ b/lib/modules/datasource/rubygems/metadata-cache.spec.ts @@ -171,4 +171,39 @@ describe('modules/datasource/rubygems/metadata-cache', () => { }); expect(packageCache.set).toHaveBeenCalledTimes(2); }); + + it('returns fallback results on 404', async () => { + const cache = new MetadataCache(new Http('test')); + + httpMock + .scope('https://rubygems.org') + .get('/api/v1/versions/foobar.json') + .reply(404); + + const versions = ['1', '2', '3']; + const res = await cache.getRelease( + 'https://rubygems.org', + 'foobar', + versions + ); + + expect(res).toEqual({ + releases: [{ version: '1' }, { version: '2' }, { version: '3' }], + }); + }); + + it('throws on unknown error', async () => { + const cache = new MetadataCache(new Http('test')); + + httpMock + .scope('https://rubygems.org') + .get('/api/v1/versions/foobar.json') + .reply(500); + + const versions = ['1', '2', '3']; + + await expect( + cache.getRelease('https://rubygems.org', 'foobar', versions) + ).rejects.toThrow(); + }); }); diff --git a/lib/modules/datasource/rubygems/metadata-cache.ts b/lib/modules/datasource/rubygems/metadata-cache.ts index 76ed3cef727ecdfec93f99b67c31c22e78f6117d..a5f1de6893ce96174591d0bbf5e9a0421566ea24 100644 --- a/lib/modules/datasource/rubygems/metadata-cache.ts +++ b/lib/modules/datasource/rubygems/metadata-cache.ts @@ -1,6 +1,6 @@ import hasha from 'hasha'; import * as packageCache from '../../../util/cache/package'; -import type { Http } from '../../../util/http'; +import { Http, HttpError } from '../../../util/http'; import { joinUrlParts } from '../../../util/url'; import type { ReleaseResult } from '../types'; import { GemMetadata, GemVersions } from './schema'; @@ -26,40 +26,48 @@ export class MetadataCache { return oldCache.data; } - const { body: releases } = await this.http.getJson( - joinUrlParts(registryUrl, '/api/v1/versions', `${packageName}.json`), - GemVersions - ); + try { + const { body: releases } = await this.http.getJson( + joinUrlParts(registryUrl, '/api/v1/versions', `${packageName}.json`), + GemVersions + ); - const { body: metadata } = await this.http.getJson( - joinUrlParts(registryUrl, '/api/v1/gems', `${packageName}.json`), - GemMetadata - ); + const { body: metadata } = await this.http.getJson( + joinUrlParts(registryUrl, '/api/v1/gems', `${packageName}.json`), + GemMetadata + ); - const data: ReleaseResult = { releases }; + const data: ReleaseResult = { releases }; - if (metadata.changelogUrl) { - data.changelogUrl = metadata.changelogUrl; - } + if (metadata.changelogUrl) { + data.changelogUrl = metadata.changelogUrl; + } - if (metadata.sourceUrl) { - data.sourceUrl = metadata.sourceUrl; - } + if (metadata.sourceUrl) { + data.sourceUrl = metadata.sourceUrl; + } - if (metadata.homepage) { - data.homepage = metadata.homepage; - } + if (metadata.homepage) { + data.homepage = metadata.homepage; + } - const newCache: CacheRecord = { hash, data }; - const ttlMinutes = 100 * 24 * 60; - const ttlRandomDelta = Math.floor(Math.random() * 10 * 24 * 60); - await packageCache.set( - cacheNs, - cacheKey, - newCache, - ttlMinutes + ttlRandomDelta - ); + const newCache: CacheRecord = { hash, data }; + const ttlMinutes = 100 * 24 * 60; + const ttlRandomDelta = Math.floor(Math.random() * 10 * 24 * 60); + await packageCache.set( + cacheNs, + cacheKey, + newCache, + ttlMinutes + ttlRandomDelta + ); + return data; + } catch (err) { + if (err instanceof HttpError && err.response?.statusCode === 404) { + const releases = versions.map((version) => ({ version })); + return { releases }; + } - return data; + throw err; + } } }