mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-11 14:36:25 +00:00
feat(nuget): detect feeds in NuGet.confg (#5757)
This commit is contained in:
parent
5c334f44af
commit
139e8bb2c2
14 changed files with 233 additions and 24 deletions
|
@ -6,6 +6,10 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"terminal.integrated.shell.linux": "/bin/bash"
|
"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"
|
"postCreateCommand": "yarn install"
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ To convert your .NET Framework `.csproj`/`.fsproj`/`.vbproj` into an SDK-style p
|
||||||
|
|
||||||
## Alternate feeds
|
## 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
|
```json
|
||||||
"nuget": {
|
"nuget": {
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { GetReleasesConfig, ReleaseResult } from '../common';
|
||||||
|
|
||||||
export { id } from './common';
|
export { id } from './common';
|
||||||
|
|
||||||
|
export { getDefaultFeed } from './v3';
|
||||||
|
|
||||||
function parseRegistryUrl(
|
function parseRegistryUrl(
|
||||||
registryUrl: string
|
registryUrl: string
|
||||||
): { feedUrl: string; protocolVersion: number } {
|
): { feedUrl: string; protocolVersion: number } {
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
||||||
|
This is no XML document
|
|
@ -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>
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<noPackageSources />
|
||||||
|
</configuration>
|
|
@ -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>
|
|
@ -1,5 +1,25 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// 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`] = `
|
exports[`lib/manager/nuget/extract extractPackageFile() extracts all dependencies 1`] = `
|
||||||
Array [
|
Array [
|
||||||
Object {
|
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`] = `
|
exports[`lib/manager/nuget/extract extractPackageFile() returns empty for invalid csproj 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"deps": Array [],
|
"deps": Array [],
|
||||||
|
|
|
@ -1,23 +1,63 @@
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
import { extractPackageFile } from './extract';
|
import { extractPackageFile } from './extract';
|
||||||
|
|
||||||
const sample = readFileSync(
|
|
||||||
'lib/manager/nuget/__fixtures__/sample.csproj',
|
|
||||||
'utf8'
|
|
||||||
);
|
|
||||||
|
|
||||||
describe('lib/manager/nuget/extract', () => {
|
describe('lib/manager/nuget/extract', () => {
|
||||||
describe('extractPackageFile()', () => {
|
describe('extractPackageFile()', () => {
|
||||||
let config;
|
let config;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config = {};
|
config = {
|
||||||
|
localDir: path.resolve('lib/manager/nuget/__fixtures__'),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
it('returns empty for invalid csproj', () => {
|
it('returns empty for invalid csproj', async () => {
|
||||||
expect(extractPackageFile('nothing here', config)).toMatchSnapshot();
|
expect(
|
||||||
|
await extractPackageFile('nothing here', 'bogus', config)
|
||||||
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
it('extracts all dependencies', () => {
|
it('extracts all dependencies', async () => {
|
||||||
const res = extractPackageFile(sample, config).deps;
|
const packageFile = 'sample.csproj';
|
||||||
expect(res).toMatchSnapshot();
|
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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 { logger } from '../../logger';
|
||||||
import { get } from '../../versioning';
|
import { get } from '../../versioning';
|
||||||
import { PackageDependency, ExtractConfig, PackageFile } from '../common';
|
import { PackageDependency, ExtractConfig, PackageFile } from '../common';
|
||||||
|
@ -5,15 +9,72 @@ import * as semverVersioning from '../../versioning/semver';
|
||||||
import * as datasourceNuget from '../../datasource/nuget';
|
import * as datasourceNuget from '../../datasource/nuget';
|
||||||
import { SkipReason } from '../../types';
|
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,
|
content: string,
|
||||||
packageFile: string,
|
packageFile: string,
|
||||||
config: ExtractConfig = {}
|
config: ExtractConfig = {}
|
||||||
): PackageFile {
|
): Promise<PackageFile> {
|
||||||
logger.trace(`nuget.extractPackageFile(${packageFile})`);
|
logger.trace({ packageFile }, 'nuget.extractPackageFile()');
|
||||||
const { isVersion } = get(config.versioning || semverVersioning.id);
|
const { isVersion } = get(config.versioning || semverVersioning.id);
|
||||||
const deps: PackageDependency[] = [];
|
const deps: PackageDependency[] = [];
|
||||||
|
|
||||||
|
const registryUrls = await determineRegistryUrls(
|
||||||
|
packageFile,
|
||||||
|
config.localDir
|
||||||
|
);
|
||||||
|
|
||||||
let lineNumber = 0;
|
let lineNumber = 0;
|
||||||
for (const line of content.split('\n')) {
|
for (const line of content.split('\n')) {
|
||||||
/**
|
/**
|
||||||
|
@ -41,6 +102,9 @@ export function extractPackageFile(
|
||||||
managerData: { lineNumber },
|
managerData: { lineNumber },
|
||||||
datasource: datasourceNuget.id,
|
datasource: datasourceNuget.id,
|
||||||
};
|
};
|
||||||
|
if (registryUrls) {
|
||||||
|
dep.registryUrls = registryUrls;
|
||||||
|
}
|
||||||
if (!isVersion(currentValue)) {
|
if (!isVersion(currentValue)) {
|
||||||
dep.skipReason = SkipReason.NotAVersion;
|
dep.skipReason = SkipReason.NotAVersion;
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,7 @@
|
||||||
"detect-indent": "6.0.0",
|
"detect-indent": "6.0.0",
|
||||||
"email-addresses": "3.1.0",
|
"email-addresses": "3.1.0",
|
||||||
"fast-safe-stringify": "2.0.7",
|
"fast-safe-stringify": "2.0.7",
|
||||||
|
"find-up": "4.1.0",
|
||||||
"fs-extra": "8.1.0",
|
"fs-extra": "8.1.0",
|
||||||
"get-installed-path": "4.0.8",
|
"get-installed-path": "4.0.8",
|
||||||
"github-url-from-git": "1.5.0",
|
"github-url-from-git": "1.5.0",
|
||||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -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"
|
resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf"
|
||||||
integrity sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==
|
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:
|
find-up@^2.0.0, find-up@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
|
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
|
||||||
|
@ -4009,14 +4017,6 @@ find-up@^3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
locate-path "^3.0.0"
|
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:
|
find-versions@^3.0.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.1.0.tgz#10161f29cf3eb4350dec10a29bdde75bff0df32d"
|
resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.1.0.tgz#10161f29cf3eb4350dec10a29bdde75bff0df32d"
|
||||||
|
|
Loading…
Reference in a new issue