feat(nuget): detect feeds in NuGet.confg (#5757)

This commit is contained in:
Florian Greinacher 2020-04-07 07:27:05 +02:00 committed by GitHub
parent 5c334f44af
commit 139e8bb2c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 233 additions and 24 deletions

View file

@ -6,6 +6,10 @@
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"],
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"orta.vscode-jest"
],
"postCreateCommand": "yarn install"
}

View file

@ -26,7 +26,7 @@ To convert your .NET Framework `.csproj`/`.fsproj`/`.vbproj` into an SDK-style p
## Alternate feeds
Renovate by default performs all lookups on `https://api.nuget.org/v3/index.json`, but it also supports alternative nuget feeds. Alternative feeds can be specified in configuration file:
Renovate by default performs all lookups on `https://api.nuget.org/v3/index.json`, but it also supports alternative nuget feeds. Alternative feeds can be specified either [in a `NuGet.config` file](https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file#package-source-sections) within your repository (Renovate will not search outside the repository) or in Renovate configuration options:
```json
"nuget": {

View file

@ -6,6 +6,8 @@ import { GetReleasesConfig, ReleaseResult } from '../common';
export { id } from './common';
export { getDefaultFeed } from './v3';
function parseRegistryUrl(
registryUrl: string
): { feedUrl: string; protocolVersion: number } {

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="Contoso" value="https://contoso.com/packages/" />
</packageSources>
</configuration>

View file

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<Version>0.1.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="4.5.0" />
</ItemGroup>
</Project>

View file

@ -0,0 +1 @@
This is no XML document

View file

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<Version>0.1.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="4.5.0" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<noPackageSources />
</configuration>

View file

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<Version>0.1.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="4.5.0" />
</ItemGroup>
</Project>

View file

@ -1,5 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`lib/manager/nuget/extract extractPackageFile() considers NuGet.config 1`] = `
Object {
"deps": Array [
Object {
"currentValue": "4.5.0",
"datasource": "nuget",
"depName": "Autofac",
"depType": "nuget",
"managerData": Object {
"lineNumber": 8,
},
"registryUrls": Array [
"https://api.nuget.org/v3/index.json#protocolVersion=3",
"https://contoso.com/packages/",
],
},
],
}
`;
exports[`lib/manager/nuget/extract extractPackageFile() extracts all dependencies 1`] = `
Array [
Object {
@ -141,6 +161,38 @@ Array [
]
`;
exports[`lib/manager/nuget/extract extractPackageFile() handles NuGet.config without package sources 1`] = `
Object {
"deps": Array [
Object {
"currentValue": "4.5.0",
"datasource": "nuget",
"depName": "Autofac",
"depType": "nuget",
"managerData": Object {
"lineNumber": 8,
},
},
],
}
`;
exports[`lib/manager/nuget/extract extractPackageFile() handles malformed NuGet.config 1`] = `
Object {
"deps": Array [
Object {
"currentValue": "4.5.0",
"datasource": "nuget",
"depName": "Autofac",
"depType": "nuget",
"managerData": Object {
"lineNumber": 8,
},
},
],
}
`;
exports[`lib/manager/nuget/extract extractPackageFile() returns empty for invalid csproj 1`] = `
Object {
"deps": Array [],

View file

@ -1,23 +1,63 @@
import { readFileSync } from 'fs';
import * as path from 'path';
import { extractPackageFile } from './extract';
const sample = readFileSync(
'lib/manager/nuget/__fixtures__/sample.csproj',
'utf8'
);
describe('lib/manager/nuget/extract', () => {
describe('extractPackageFile()', () => {
let config;
beforeEach(() => {
config = {};
config = {
localDir: path.resolve('lib/manager/nuget/__fixtures__'),
};
});
it('returns empty for invalid csproj', () => {
expect(extractPackageFile('nothing here', config)).toMatchSnapshot();
it('returns empty for invalid csproj', async () => {
expect(
await extractPackageFile('nothing here', 'bogus', config)
).toMatchSnapshot();
});
it('extracts all dependencies', () => {
const res = extractPackageFile(sample, config).deps;
expect(res).toMatchSnapshot();
it('extracts all dependencies', async () => {
const packageFile = 'sample.csproj';
const sample = readFileSync(
path.join(config.localDir, packageFile),
'utf8'
);
const res = await extractPackageFile(sample, packageFile, config);
expect(res.deps).toMatchSnapshot();
});
it('considers NuGet.config', async () => {
const packageFile = 'with-config-file/with-config-file.csproj';
const contents = readFileSync(
path.join(config.localDir, packageFile),
'utf8'
);
expect(
await extractPackageFile(contents, packageFile, config)
).toMatchSnapshot();
});
it('handles malformed NuGet.config', async () => {
const packageFile =
'with-malformed-config-file/with-malformed-config-file.csproj';
const contents = readFileSync(
path.join(config.localDir, packageFile),
'utf8'
);
expect(
await extractPackageFile(contents, packageFile, config)
).toMatchSnapshot();
});
it('handles NuGet.config without package sources', async () => {
const packageFile =
'without-package-sources/without-package-sources.csproj';
const contents = readFileSync(
path.join(config.localDir, packageFile),
'utf8'
);
expect(
await extractPackageFile(contents, packageFile, config)
).toMatchSnapshot();
});
});
});

View file

@ -1,3 +1,7 @@
import findUp from 'find-up';
import * as path from 'path';
import { XmlDocument } from 'xmldoc';
import { readFile } from 'fs-extra';
import { logger } from '../../logger';
import { get } from '../../versioning';
import { PackageDependency, ExtractConfig, PackageFile } from '../common';
@ -5,15 +9,72 @@ import * as semverVersioning from '../../versioning/semver';
import * as datasourceNuget from '../../datasource/nuget';
import { SkipReason } from '../../types';
export function extractPackageFile(
async function readFileAsXmlDocument(file: string): Promise<XmlDocument> {
try {
return new XmlDocument(await readFile(file, 'utf8'));
} catch (err) {
logger.debug({ err }, `failed to parse '${file}' as XML document`);
return undefined;
}
}
async function determineRegistryUrls(
packageFile: string,
localDir: string
): Promise<string[]> {
const nuGetConfigPath = await findUp('NuGet.config', {
cwd: path.dirname(path.join(localDir, packageFile)),
type: 'file',
});
if (nuGetConfigPath?.startsWith(localDir) !== true) {
return undefined;
}
logger.debug({ nuGetConfigPath }, 'found NuGet.config');
const nuGetConfig = await readFileAsXmlDocument(nuGetConfigPath);
if (!nuGetConfig) {
return undefined;
}
const packageSources = nuGetConfig.childNamed('packageSources');
if (!packageSources) {
return undefined;
}
const registryUrls = [datasourceNuget.getDefaultFeed()];
for (const child of packageSources.children) {
if (child.type === 'element') {
if (child.name === 'clear') {
logger.debug(`clearing registry URLs`);
registryUrls.length = 0;
} else if (child.name === 'add') {
let registryUrl = child.attr.value;
if (child.attr.protocolVersion) {
registryUrl += `#protocolVersion=${child.attr.protocolVersion}`;
}
logger.debug({ registryUrl }, 'adding registry URL');
registryUrls.push(registryUrl);
}
}
}
return registryUrls;
}
export async function extractPackageFile(
content: string,
packageFile: string,
config: ExtractConfig = {}
): PackageFile {
logger.trace(`nuget.extractPackageFile(${packageFile})`);
): Promise<PackageFile> {
logger.trace({ packageFile }, 'nuget.extractPackageFile()');
const { isVersion } = get(config.versioning || semverVersioning.id);
const deps: PackageDependency[] = [];
const registryUrls = await determineRegistryUrls(
packageFile,
config.localDir
);
let lineNumber = 0;
for (const line of content.split('\n')) {
/**
@ -41,6 +102,9 @@ export function extractPackageFile(
managerData: { lineNumber },
datasource: datasourceNuget.id,
};
if (registryUrls) {
dep.registryUrls = registryUrls;
}
if (!isVersion(currentValue)) {
dep.skipReason = SkipReason.NotAVersion;
}

View file

@ -129,6 +129,7 @@
"detect-indent": "6.0.0",
"email-addresses": "3.1.0",
"fast-safe-stringify": "2.0.7",
"find-up": "4.1.0",
"fs-extra": "8.1.0",
"get-installed-path": "4.0.8",
"github-url-from-git": "1.5.0",

View file

@ -3995,6 +3995,14 @@ find-npm-prefix@^1.0.2:
resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf"
integrity sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==
find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
dependencies:
locate-path "^5.0.0"
path-exists "^4.0.0"
find-up@^2.0.0, find-up@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
@ -4009,14 +4017,6 @@ find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
find-up@^4.0.0, find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
dependencies:
locate-path "^5.0.0"
path-exists "^4.0.0"
find-versions@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.1.0.tgz#10161f29cf3eb4350dec10a29bdde75bff0df32d"