diff --git a/lib/datasource/nuget/__snapshots__/index.spec.ts.snap b/lib/datasource/nuget/__snapshots__/index.spec.ts.snap index b6838ca6640c5177c07a821006b8fad799760ed8..f0f6fba5dcc81d08f022c394e04d81a0f0ec584c 100644 --- a/lib/datasource/nuget/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/nuget/__snapshots__/index.spec.ts.snap @@ -518,6 +518,16 @@ Array [ "method": "GET", "url": "https://api.nuget.org/v3/registration5-gz-semver2/nunit/index.json", }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.nuget.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.nuget.org/v3/index.json", + }, Object { "headers": Object { "accept-encoding": "gzip, deflate", @@ -561,7 +571,6 @@ Object { "version": "2.6.1", }, Object { - "releaseTimestamp": "2012-10-23T15:37:48+00:00", "version": "2.6.2", }, Object { @@ -743,6 +752,25 @@ Array [ "method": "GET", "url": "https://api.nuget.org/v3/registration5-gz-semver2/nunit/index.json", }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "myprivatefeed", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://myprivatefeed/index.json", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "api.nuget.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.nuget.org/v3-flatcontainer/nunit/3.12.0/nunit.nuspec", + }, ] `; @@ -1520,6 +1548,16 @@ Array [ "method": "GET", "url": "https://api.nuget.org/v3/registration5-gz-semver2/nlog/page/4.6.0-rc3/5.0.0-beta11.json", }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.nuget.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.nuget.org/v3/index.json", + }, Object { "headers": Object { "accept-encoding": "gzip, deflate", @@ -1936,6 +1974,16 @@ Array [ "method": "GET", "url": "https://api.nuget.org/v3/registration5-gz-semver2/nunit/index.json", }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "api.nuget.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.nuget.org/v3/index.json", + }, Object { "headers": Object { "accept-encoding": "gzip, deflate", @@ -1965,6 +2013,25 @@ Array [ "method": "GET", "url": "https://api.nuget.org/v3/registration5-gz-semver2/nunit/index.json", }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate", + "host": "myprivatefeed", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://myprivatefeed/index.json", + }, + Object { + "headers": Object { + "accept-encoding": "gzip, deflate", + "host": "api.nuget.org", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "https://api.nuget.org/v3-flatcontainer/nunit/3.12.0/nunit.nuspec", + }, ] `; diff --git a/lib/datasource/nuget/index.spec.ts b/lib/datasource/nuget/index.spec.ts index c7a3ab2025ce40ca3fb18ddad0fd165802d4065e..9e5bcb2a4053fc8b1f6849296e8272e7f4ee101a 100644 --- a/lib/datasource/nuget/index.spec.ts +++ b/lib/datasource/nuget/index.spec.ts @@ -307,8 +307,10 @@ describe('datasource/nuget', () => { httpMock .scope('https://api.nuget.org') .get('/v3/index.json') + .twice() .reply(200, JSON.parse(nugetIndexV3)) .get('/v3-flatcontainer/nunit/3.12.0/nunit.nuspec') + .twice() .reply(200, pkgInfoV3FromNuget) .get('/v3/registration5-gz-semver2/nunit/index.json') .twice() @@ -316,6 +318,7 @@ describe('datasource/nuget', () => { httpMock .scope('https://myprivatefeed') .get('/index.json') + .twice() .reply(200, JSON.parse(nugetIndexV3)); const res = await getPkgReleases({ @@ -370,6 +373,7 @@ describe('datasource/nuget', () => { httpMock .scope('https://api.nuget.org') .get('/v3/index.json') + .twice() .reply(200, JSON.parse(nugetIndexV3)) .get('/v3/registration5-gz-semver2/nunit/index.json') .reply(200, pkgListV3Registration) @@ -387,6 +391,7 @@ describe('datasource/nuget', () => { const scope = httpMock .scope('https://api.nuget.org') .get('/v3/index.json') + .twice() .reply(200, JSON.parse(nugetIndexV3)); nlogMocks.forEach(({ url, result }) => { scope.get(url).reply(200, result); @@ -404,10 +409,21 @@ describe('datasource/nuget', () => { httpMock .scope('https://api.nuget.org') .get('/v3/registration5-gz-semver2/nunit/index.json') - .reply(200, pkgListV3Registration); + .reply( + 200, + pkgListV3Registration + .replace(/"http:\/\/nunit\.org"/g, '""') + .replace('"published": "2012-10-23T15:37:48+00:00",', '') + ) + .get('/v3-flatcontainer/nunit/3.12.0/nunit.nuspec') + .reply( + 200, + pkgInfoV3FromNuget.replace('https://github.com/nunit/nunit', '') + ); httpMock .scope('https://myprivatefeed') .get('/index.json') + .twice() .reply(200, JSON.parse(nugetIndexV3)); const res = await getPkgReleases({ @@ -415,8 +431,8 @@ describe('datasource/nuget', () => { }); expect(res).not.toBeNull(); expect(res).toMatchSnapshot(); - expect(res.sourceUrl).toBeDefined(); expect(httpMock.getTrace()).toMatchSnapshot(); + expect(res.sourceUrl).toBeDefined(); }); it('processes real data (v2)', async () => { httpMock diff --git a/lib/datasource/nuget/v3.ts b/lib/datasource/nuget/v3.ts index 4638dba80637d2bb0d961b73c32719501f94dd00..4d0452bffe08e34d0bbf73c8b6aa160adc35911b 100644 --- a/lib/datasource/nuget/v3.ts +++ b/lib/datasource/nuget/v3.ts @@ -1,9 +1,12 @@ +import is from '@sindresorhus/is'; +import { RequestError } from 'got'; import pAll from 'p-all'; import * as semver from 'semver'; import { XmlDocument } from 'xmldoc'; import { logger } from '../../logger'; import * as packageCache from '../../util/cache/package'; import { Http } from '../../util/http'; +import { ensureTrailingSlash } from '../../util/url'; import { Release, ReleaseResult } from '../common'; import { id } from './common'; @@ -47,6 +50,7 @@ export async function getResourceUrl( cacheNamespace, responseCacheKey ); + // istanbul ignore else: currently not testable if (!servicesIndexRaw) { servicesIndexRaw = (await http.getJson<ServicesIndexRaw>(url)).body; await packageCache.set( @@ -165,27 +169,44 @@ export async function getReleases( releases, }; - if (registryUrl.toLowerCase() === defaultNugetFeed.toLowerCase()) { - try { - const nuspecUrl = `https://api.nuget.org/v3-flatcontainer/${pkgName.toLowerCase()}/${latestStable}/${pkgName.toLowerCase()}.nuspec`; + try { + const packageBaseAddress = await getResourceUrl( + registryUrl, + 'PackageBaseAddress' + ); + // istanbul ignore else: this is a required v3 api + if (is.nonEmptyString(packageBaseAddress)) { + const nuspecUrl = `${ensureTrailingSlash( + packageBaseAddress + )}${pkgName.toLowerCase()}/${latestStable}/${pkgName.toLowerCase()}.nuspec`; const metaresult = await http.get(nuspecUrl); const nuspec = new XmlDocument(metaresult.body); const sourceUrl = nuspec.valueWithPath('metadata.repository@url'); if (sourceUrl) { dep.sourceUrl = sourceUrl; } - } catch (err) /* istanbul ignore next */ { + } + } catch (err) /* istanbul ignore next */ { + // ignore / silence 404. Seen on proget, if remote connector is used and package is not yet cached + if (err instanceof RequestError && err.response?.statusCode === 404) { logger.debug( - `Cannot obtain sourceUrl for ${pkgName} using version ${latestStable}` + { registryUrl, pkgName, pkgVersion: latestStable }, + `package manifest (.nuspec) not found` ); return dep; } - } else if (homepage) { - dep.sourceUrl = homepage; + logger.debug( + { err, registryUrl, pkgName, pkgVersion: latestStable }, + `Cannot obtain sourceUrl` + ); + return dep; } + // istanbul ignore else: not easy testable if (homepage) { - dep.homepage = homepage; + // only assign if not assigned + dep.sourceUrl ??= homepage; + dep.homepage ??= homepage; } return dep;