renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
202 lines • 8.29 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.readFileAsXmlDocument = readFileAsXmlDocument;
exports.getDefaultRegistries = getDefaultRegistries;
exports.getConfiguredRegistries = getConfiguredRegistries;
exports.isXmlElement = isXmlElement;
exports.findVersion = findVersion;
exports.applyRegistries = applyRegistries;
exports.findGlobalJson = findGlobalJson;
const tslib_1 = require("tslib");
const upath_1 = tslib_1.__importDefault(require("upath"));
const xmldoc_1 = require("xmldoc");
const logger_1 = require("../../../logger");
const fs_1 = require("../../../util/fs");
const minimatch_1 = require("../../../util/minimatch");
const regex_1 = require("../../../util/regex");
const nuget_1 = require("../../datasource/nuget");
const schema_1 = require("./schema");
async function readFileAsXmlDocument(file) {
try {
// TODO #22198
const doc = new xmldoc_1.XmlDocument((await (0, fs_1.readLocalFile)(file, 'utf8')));
// don't return empty documents
return doc?.firstChild ? doc : undefined;
}
catch (err) {
logger_1.logger.debug({ err, file }, `failed to parse file as XML document`);
return undefined;
}
}
/**
* The default `nuget.org` named registry.
* @returns the default registry for NuGet
*/
function getDefaultRegistries() {
return [{ url: nuget_1.nugetOrg, name: 'nuget.org' }];
}
async function getConfiguredRegistries(packageFile) {
// 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'];
// normalize paths, otherwise startsWith can fail because of path delimitter mismatch
const nuGetConfigPath = await (0, fs_1.findUpLocal)(nuGetConfigFileNames, upath_1.default.dirname(packageFile));
if (!nuGetConfigPath) {
return undefined;
}
logger_1.logger.debug(`Found NuGet.config at ${nuGetConfigPath}`);
const nuGetConfig = await readFileAsXmlDocument(nuGetConfigPath);
if (!nuGetConfig) {
return undefined;
}
const packageSources = nuGetConfig.childNamed('packageSources');
if (!packageSources) {
// If there are no packageSources, don't even look for any
// disabledPackageSources
// Even if NuGet default source (nuget.org) was among the
// disabledPackageSources, Renovate will default to the default source
// (nuget.org) anyway
return undefined;
}
const packageSourceMapping = nuGetConfig.childNamed('packageSourceMapping');
let registries = getDefaultRegistries();
// Map optional source mapped package patterns to default registries
for (const registry of registries) {
const sourceMappedPackagePatterns = packageSourceMapping
?.childWithAttribute('key', registry.name)
?.childrenNamed('package')
.map((packagePattern) => packagePattern.attr.pattern);
registry.sourceMappedPackagePatterns = sourceMappedPackagePatterns;
}
for (const child of packageSources.children) {
if (isXmlElement(child)) {
if (child.name === 'clear') {
logger_1.logger.debug(`clearing registry URLs`);
registries.length = 0;
}
else if (child.name === 'add') {
const isHttpUrl = (0, regex_1.regEx)(/^https?:\/\//i).test(child.attr.value);
if (isHttpUrl) {
let registryUrl = child.attr.value;
if (child.attr.protocolVersion) {
registryUrl += `#protocolVersion=${child.attr.protocolVersion}`;
}
const sourceMappedPackagePatterns = packageSourceMapping
?.childWithAttribute('key', child.attr.key)
?.childrenNamed('package')
.map((packagePattern) => packagePattern.attr.pattern);
logger_1.logger.debug({
name: child.attr.key,
registryUrl,
sourceMappedPackagePatterns,
}, `Adding registry URL ${registryUrl}`);
registries.push({
name: child.attr.key,
url: registryUrl,
sourceMappedPackagePatterns,
});
}
else {
logger_1.logger.debug({ registryUrl: child.attr.value }, 'ignoring local registry URL');
}
}
// child.name === 'remove' not supported
}
}
const disabledPackageSources = nuGetConfig.childNamed('disabledPackageSources');
if (disabledPackageSources) {
for (const child of disabledPackageSources.children) {
if (isXmlElement(child) &&
child.name === 'add' &&
child.attr.value === 'true') {
const disabledRegistryKey = child.attr.key;
registries = registries.filter((o) => o.name !== disabledRegistryKey);
logger_1.logger.debug(`Disabled registry with key: ${disabledRegistryKey}`);
}
}
}
// Deduplicate registries with #procolVersion=3
// Keep any which include sourceMappedPackagePatterns
const plainRegistryUrls = registries
.filter((r) => !r.sourceMappedPackagePatterns)
.map((r) => r.url);
registries = registries.filter((r) => {
return (r.sourceMappedPackagePatterns ??
!plainRegistryUrls.includes(`${r.url}#protocolVersion=3`));
});
return registries;
}
function isXmlElement(child) {
return child.type === 'element';
}
function findVersion(parsedXml) {
for (const tag of ['Version', 'VersionPrefix']) {
for (const l1Elem of parsedXml.childrenNamed('PropertyGroup')) {
for (const l2Elem of l1Elem.childrenNamed(tag)) {
return l2Elem;
}
}
}
return null;
}
function applyRegistries(dep, registries) {
if (registries) {
if (!registries.some((reg) => reg.sourceMappedPackagePatterns)) {
dep.registryUrls = registries.map((reg) => reg.url);
return dep;
}
const regs = registries.filter((r) => r.sourceMappedPackagePatterns);
const map = new Map(regs.flatMap((r) => r.sourceMappedPackagePatterns.map((p) => [p, []])));
const depName = dep.depName;
for (const reg of regs) {
for (const pattern of reg.sourceMappedPackagePatterns) {
map.get(pattern).push(reg);
}
}
const urls = [];
for (const [pattern, regs] of [...map].sort(sortPatterns)) {
if ((0, minimatch_1.minimatch)(pattern, { nocase: true }).match(depName)) {
urls.push(...regs.map((r) => r.url));
break;
}
}
if (urls.length) {
dep.registryUrls = urls;
}
}
return dep;
}
/*
* Sorts patterns by specificity:
* 1. Exact match patterns
* 2. Wildcard match patterns
* The longest pattern has precedence.
*/
function sortPatterns(a, b) {
if (a[0].endsWith('*') && !b[0].endsWith('*')) {
return 1;
}
if (!a[0].endsWith('*') && b[0].endsWith('*')) {
return -1;
}
const aTrim = a[0].slice(0, -1);
const bTrim = b[0].slice(0, -1);
return aTrim.localeCompare(bTrim) * -1;
}
async function findGlobalJson(packageFile) {
const globalJsonPath = await (0, fs_1.findLocalSiblingOrParent)(packageFile, 'global.json');
if (!globalJsonPath) {
return null;
}
const content = await (0, fs_1.readLocalFile)(globalJsonPath, 'utf8');
if (!content) {
logger_1.logger.debug({ packageFile, globalJsonPath }, 'Failed to read global.json');
return null;
}
const result = await schema_1.GlobalJson.safeParseAsync(content);
if (!result.success) {
logger_1.logger.debug({ packageFile, globalJsonPath, err: result.error }, 'Failed to parse global.json');
return null;
}
return result.data;
}
//# sourceMappingURL=util.js.map