renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
230 lines (229 loc) • 8.48 kB
JavaScript
import { logger } from "../../../logger/index.js";
import { isValidLocalPath, readLocalFile } from "../../../util/fs/index.js";
import { MavenDatasource } from "../../datasource/maven/index.js";
import { extractRegistries } from "../maven/extract.js";
import { isXmlElement } from "../nuget/util.js";
import { applyProps, containsPlaceholder, findAttrValuePosition, parsePropertiesFile, resolveChainedProps } from "./properties.js";
import upath from "upath";
import { XmlDocument } from "xmldoc";
//#region lib/modules/manager/ant/extract.ts
const scopeNames = new Set([
"compile",
"runtime",
"test",
"provided",
"system"
]);
function getDependencyType(scope) {
if (scope && scopeNames.has(scope)) return scope;
return "compile";
}
function parseCoords(coordsStr) {
const parts = coordsStr.split(":");
if (parts.length < 3) {
logger.trace({ coordsStr }, "ant manager: coords has fewer than 3 parts");
return null;
}
const [groupId, artifactId] = parts;
if (!groupId || !artifactId) {
logger.trace({ coordsStr }, "ant manager: coords has empty groupId or artifactId");
return null;
}
let scope;
let rawVersion;
if (parts.length >= 4 && scopeNames.has(parts.at(-1))) {
scope = parts.at(-1);
rawVersion = parts.at(-2);
} else rawVersion = parts.at(-1);
return {
groupId,
artifactId,
rawVersion,
scope
};
}
async function readSettingsRegistries(settingsFile, baseDir) {
const settingsPath = settingsFile.startsWith("/") ? settingsFile : upath.join(baseDir, settingsFile);
if (!isValidLocalPath(settingsPath)) {
logger.debug(`ant manager: skipping settings file outside repository: ${settingsPath}`);
return [];
}
const settingsContent = await readLocalFile(settingsPath, "utf8");
if (!settingsContent) {
logger.debug(`ant manager: could not read settings file ${settingsPath}`);
return [];
}
return extractRegistries(settingsContent);
}
async function collectRegistryUrls(node, baseDir) {
const urls = [];
const settingsFile = node.attr.settingsFile;
if (settingsFile) urls.push(...await readSettingsRegistries(settingsFile, baseDir));
for (const child of node.children) if (isXmlElement(child) && child.name === "remoteRepository" && child.attr.url) urls.push(child.attr.url);
return [...new Set(urls)];
}
function collectCoordsDependency(node, packageFile, content, registryUrls) {
const coordsStr = node.attr.coords;
const parsed = parseCoords(coordsStr);
if (!parsed) return null;
const dep = {
datasource: MavenDatasource.id,
depName: `${parsed.groupId}:${parsed.artifactId}`,
currentValue: parsed.rawVersion,
depType: getDependencyType(parsed.scope ?? node.attr.scope),
...registryUrls?.length && { registryUrls }
};
dep.fileReplacePosition = findAttrValuePosition(content, node, "coords") + coordsStr.lastIndexOf(parsed.rawVersion);
return {
dep,
depPackageFile: packageFile
};
}
function collectDependency(node, packageFile, content, registryUrls = []) {
if (node.attr.coords) return collectCoordsDependency(node, packageFile, content, registryUrls);
const { groupId, artifactId, version, scope } = node.attr;
if (!version || !groupId || !artifactId) return null;
const dep = {
datasource: MavenDatasource.id,
depName: `${groupId}:${artifactId}`,
currentValue: version,
depType: getDependencyType(scope),
...registryUrls?.length && { registryUrls }
};
dep.fileReplacePosition = findAttrValuePosition(content, node, "version");
return {
dep,
depPackageFile: packageFile
};
}
function walkNode(node, rawDeps, packageFile, content) {
for (const child of node.children) {
if (!isXmlElement(child)) continue;
if (child.name === "dependency") {
const rawDep = collectDependency(child, packageFile, content);
if (rawDep) rawDeps.push(rawDep);
} else walkNode(child, rawDeps, packageFile, content);
}
}
function extractPackageFile(content, packageFile) {
let doc;
try {
doc = new XmlDocument(content);
} catch {
logger.debug(`ant manager: could not parse XML ${packageFile}`);
return null;
}
const rawDeps = [];
walkNode(doc, rawDeps, packageFile, content);
const deps = rawDeps.map((rd) => rd.dep);
if (deps.length === 0) return null;
return { deps };
}
async function loadPropertyFile(file, baseDir, visitedFiles, allProps) {
if (containsPlaceholder(file)) {
logger.debug(`ant manager: skipping properties file with unresolved placeholders in path: ${file}`);
return;
}
const propFilePath = file.startsWith("/") ? file : upath.join(baseDir, file);
if (!isValidLocalPath(propFilePath)) {
logger.debug(`ant manager: skipping properties file outside repository: ${propFilePath}`);
return;
}
if (visitedFiles.has(propFilePath)) return;
visitedFiles.add(propFilePath);
const propContent = await readLocalFile(propFilePath, "utf8");
if (!propContent) {
logger.debug(`ant manager: could not read properties file ${propFilePath}`);
return;
}
parsePropertiesFile(propContent, propFilePath, allProps);
}
async function loadImportedFile(file, baseDir, visitedFiles, allProps, allRawDeps) {
if (containsPlaceholder(file)) {
logger.debug(`ant manager: skipping import file with unresolved placeholders in path: ${file}`);
return;
}
const importedFile = upath.normalize(upath.join(baseDir, file));
if (!isValidLocalPath(importedFile)) {
logger.debug(`ant manager: skipping import file outside repository: ${importedFile}`);
return;
}
await walkXmlFile(importedFile, visitedFiles, allProps, allRawDeps);
}
/**
* Walk an XML node tree in document order, processing properties,
* property file references, and dependencies as they appear.
*/
async function walkNodeInOrder(node, packageFile, content, visitedFiles, allProps, allRawDeps, registryUrls = []) {
const baseDir = upath.dirname(packageFile);
for (const child of node.children) {
if (!isXmlElement(child)) continue;
if (child.name === "property") {
const name = child.attr.name;
const value = child.attr.value;
if (name && value && !(name in allProps)) allProps[name] = {
val: value,
fileReplacePosition: findAttrValuePosition(content, child, "value"),
packageFile
};
if (child.attr.file) await loadPropertyFile(child.attr.file, baseDir, visitedFiles, allProps);
} else if (child.name === "import" && child.attr.file) await loadImportedFile(child.attr.file, baseDir, visitedFiles, allProps, allRawDeps);
else if (child.name === "dependency") {
const rawDep = collectDependency(child, packageFile, content, registryUrls);
if (rawDep) allRawDeps.push(rawDep);
} else {
const childRegistries = await collectRegistryUrls(child, baseDir);
await walkNodeInOrder(child, packageFile, content, visitedFiles, allProps, allRawDeps, childRegistries.length > 0 ? childRegistries : registryUrls);
}
}
}
async function walkXmlFile(packageFile, visitedFiles, allProps, allRawDeps) {
if (visitedFiles.has(packageFile)) return;
visitedFiles.add(packageFile);
const content = await readLocalFile(packageFile, "utf8");
if (!content) {
logger.debug(`ant manager: could not read ${packageFile}`);
return;
}
let doc;
try {
doc = new XmlDocument(content);
} catch {
logger.debug(`ant manager: could not parse XML ${packageFile}`);
return;
}
await walkNodeInOrder(doc, packageFile, content, visitedFiles, allProps, allRawDeps);
}
async function extractAllPackageFiles(_config, packageFiles) {
const results = [];
const seen = /* @__PURE__ */ new Set();
for (const packageFile of packageFiles) {
if (seen.has(packageFile)) continue;
seen.add(packageFile);
const visitedFiles = /* @__PURE__ */ new Set();
const allProps = {};
const allRawDeps = [];
await walkXmlFile(packageFile, visitedFiles, allProps, allRawDeps);
resolveChainedProps(allProps);
const resolvedDeps = allRawDeps.map((rawDep) => applyProps(rawDep.dep, rawDep.depPackageFile, allProps));
if (resolvedDeps.length === 0) continue;
const fileMap = /* @__PURE__ */ new Map();
for (let i = 0; i < resolvedDeps.length; i++) {
const dep = resolvedDeps[i];
const targetFile = dep.propSource ?? allRawDeps[i].depPackageFile;
if (!fileMap.has(targetFile)) fileMap.set(targetFile, []);
fileMap.get(targetFile).push(dep);
}
for (const [pkgFile, deps] of fileMap) {
for (const dep of deps) delete dep.propSource;
results.push({
packageFile: pkgFile,
deps
});
}
}
return results.length > 0 ? results : null;
}
//#endregion
export { extractAllPackageFiles, extractPackageFile };
//# sourceMappingURL=extract.js.map