diff --git a/lib/modules/datasource/index.ts b/lib/modules/datasource/index.ts index 3815c69b6f90fd..8fa5355a125ceb 100644 --- a/lib/modules/datasource/index.ts +++ b/lib/modules/datasource/index.ts @@ -8,6 +8,7 @@ import { coerceArray } from '../../util/array'; import * as memCache from '../../util/cache/memory'; import * as packageCache from '../../util/cache/package'; import { clone } from '../../util/clone'; +import { filterMap } from '../../util/filter-map'; import { AsyncResult, Result } from '../../util/result'; import { DatasourceCacheStats } from '../../util/stats'; import { trimTrailingSlash } from '../../util/url'; @@ -161,53 +162,63 @@ async function mergeRegistries( registryUrls: string[], ): Promise { let combinedRes: ReleaseResult | undefined; - let caughtError: Error | undefined; + let lastErr: Error | undefined; + let commonRegistryUrl = true; for (const registryUrl of registryUrls) { try { const res = await getRegistryReleases(datasource, config, registryUrl); if (!res) { continue; } - if (combinedRes) { - for (const existingRelease of coerceArray(combinedRes.releases)) { - existingRelease.registryUrl ??= combinedRes.registryUrl; - } - for (const additionalRelease of coerceArray(res.releases)) { - additionalRelease.registryUrl = res.registryUrl; - } - combinedRes = { ...res, ...combinedRes }; - delete combinedRes.registryUrl; - combinedRes.releases = [...combinedRes.releases, ...res.releases]; - } else { + + if (!combinedRes) { combinedRes = res; + continue; + } + + if (commonRegistryUrl) { + for (const release of coerceArray(combinedRes.releases)) { + release.registryUrl ??= combinedRes.registryUrl; + } + commonRegistryUrl = false; + } + + const releases = coerceArray(res.releases); + for (const release of releases) { + release.registryUrl ??= res.registryUrl; } + + combinedRes.releases.push(...releases); + combinedRes = { ...res, ...combinedRes }; + delete combinedRes.registryUrl; } catch (err) { if (err instanceof ExternalHostError) { throw err; } - // We'll always save the last-thrown error - caughtError = err; + + lastErr = err; logger.trace({ err }, 'datasource merge failure'); } } - // De-duplicate releases - if (combinedRes?.releases?.length) { - const seenVersions = new Set(); - combinedRes.releases = combinedRes.releases.filter((release) => { - if (seenVersions.has(release.version)) { - return false; - } - seenVersions.add(release.version); - return true; - }); - } - if (combinedRes) { - return combinedRes; - } - if (caughtError) { - throw caughtError; + + if (!combinedRes) { + if (lastErr) { + throw lastErr; + } + + return null; } - return null; + + const seenVersions = new Set(); + combinedRes.releases = filterMap(combinedRes.releases, (release) => { + if (seenVersions.has(release.version)) { + return null; + } + seenVersions.add(release.version); + return release; + }); + + return combinedRes; } function massageRegistryUrls(registryUrls: string[]): string[] {