forked from renovatebot/renovate
/
extract.ts
159 lines (143 loc) · 5.07 KB
/
extract.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import * as path from 'path';
import findUp from 'find-up';
import { XmlDocument } from 'xmldoc';
import * as datasourceNuget from '../../datasource/nuget';
import { logger } from '../../logger';
import { SkipReason } from '../../types';
import { clone } from '../../util/clone';
import { readFile } from '../../util/fs';
import { get } from '../../versioning';
import * as semverVersioning from '../../versioning/semver';
import { ExtractConfig, PackageDependency, PackageFile } from '../common';
import { DotnetToolsManifest } from './types';
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[]> {
// Valid file names taken from https://github.com/NuGet/NuGet.Client/blob/f64621487c0b454eda4b98af853bf4a528bef72a/src/NuGet.Core/NuGet.Configuration/Settings/Settings.cs#L34
const nuGetConfigFileNames = ['nuget.config', 'NuGet.config', 'NuGet.Config'];
const nuGetConfigPath = await findUp(nuGetConfigFileNames, {
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 = clone(datasourceNuget.defaultRegistryUrls);
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') {
const isHttpUrl = /^https?:\/\//i.test(child.attr.value);
if (isHttpUrl) {
let registryUrl = child.attr.value;
if (child.attr.protocolVersion) {
registryUrl += `#protocolVersion=${child.attr.protocolVersion}`;
}
logger.debug({ registryUrl }, 'adding registry URL');
registryUrls.push(registryUrl);
} else {
logger.debug(
{ registryUrl: child.attr.value },
'ignoring local registry URL'
);
}
}
// child.name === 'remove' not supported
}
}
return registryUrls;
}
const packageRe = /<(?:PackageReference|DotNetCliToolReference).*Include\s*=\s*"([^"]+)".*Version\s*=\s*"(?:[[])?(?:([^"(,[\]]+)\s*(?:,\s*[)\]]|])?)"/;
export async function extractPackageFile(
content: string,
packageFile: string,
config: ExtractConfig
): Promise<PackageFile | null> {
logger.trace({ packageFile }, 'nuget.extractPackageFile()');
const { isVersion } = get(config.versioning || semverVersioning.id);
const deps: PackageDependency[] = [];
const registryUrls = await determineRegistryUrls(
packageFile,
config.localDir
);
if (packageFile.endsWith('.config/dotnet-tools.json')) {
let manifest: DotnetToolsManifest;
try {
manifest = JSON.parse(content);
} catch (err) {
logger.debug({ fileName: packageFile }, 'Invalid JSON');
return null;
}
if (manifest.version !== 1) {
logger.debug({ contents: manifest }, 'Unsupported dotnet tools version');
return null;
}
for (const depName of Object.keys(manifest.tools)) {
const tool = manifest.tools[depName];
const currentValue = tool.version;
const dep: PackageDependency = {
depType: 'nuget',
depName,
currentValue,
datasource: datasourceNuget.id,
};
if (registryUrls) {
dep.registryUrls = registryUrls;
}
deps.push(dep);
}
return { deps };
}
for (const line of content.split('\n')) {
/**
* https://docs.microsoft.com/en-us/nuget/concepts/package-versioning
* This article mentions that Nuget 3.x and later tries to restore the lowest possible version
* regarding to given version range.
* 1.3.4 equals [1.3.4,)
* Due to guarantee that an update of package version will result in its usage by the next restore + build operation,
* only following constrained versions make sense
* 1.3.4, [1.3.4], [1.3.4, ], [1.3.4, )
* The update of the right boundary does not make sense regarding to the lowest version restore rule,
* so we don't include it in the extracting regexp
*/
const match = packageRe.exec(line);
if (match) {
const depName = match[1];
const currentValue = match[2];
const dep: PackageDependency = {
depType: 'nuget',
depName,
currentValue,
datasource: datasourceNuget.id,
};
if (registryUrls) {
dep.registryUrls = registryUrls;
}
if (!isVersion(currentValue)) {
dep.skipReason = SkipReason.NotAVersion;
}
deps.push(dep);
}
}
return { deps };
}