UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

553 lines • 19.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractPackage = extractPackage; exports.extractRegistries = extractRegistries; exports.parseSettings = parseSettings; exports.resolveParents = resolveParents; exports.extractExtensions = extractExtensions; exports.extractAllPackageFiles = extractAllPackageFiles; const tslib_1 = require("tslib"); const is_1 = tslib_1.__importDefault(require("@sindresorhus/is")); 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 regex_1 = require("../../../util/regex"); const maven_1 = require("../../datasource/maven"); const common_1 = require("../../datasource/maven/common"); const extract_1 = require("../buildpacks/extract"); const extract_2 = require("../dockerfile/extract"); const supportedNamespaces = [ 'http://maven.apache.org/SETTINGS/1.0.0', 'http://maven.apache.org/SETTINGS/1.1.0', 'http://maven.apache.org/SETTINGS/1.2.0', ]; const supportedExtensionsNamespaces = [ 'http://maven.apache.org/EXTENSIONS/1.0.0', 'http://maven.apache.org/EXTENSIONS/1.1.0', 'http://maven.apache.org/EXTENSIONS/1.2.0', ]; function parsePom(raw, packageFile) { let project; try { project = new xmldoc_1.XmlDocument(raw); if (raw.includes('\r\n')) { logger_1.logger.warn('Your pom.xml contains windows line endings. This is not supported and may result in parsing issues.'); } } catch { logger_1.logger.debug({ packageFile }, `Failed to parse as XML`); return null; } const { name, attr, children } = project; if (name !== 'project') { return null; } if (attr.xmlns === 'http://maven.apache.org/POM/4.0.0') { return project; } if (is_1.default.nonEmptyArray(children) && children.some((c) => c.name === 'modelVersion' && c.val === '4.0.0')) { return project; } return null; } function parseExtensions(raw, packageFile) { let extensions; try { extensions = new xmldoc_1.XmlDocument(raw); } catch { logger_1.logger.debug({ packageFile }, `Failed to parse as XML`); return null; } const { name, attr, children } = extensions; if (name !== 'extensions') { return null; } if (!supportedExtensionsNamespaces.includes(attr.xmlns)) { return null; } if (!is_1.default.nonEmptyArray(children)) { return null; } return extensions; } function containsPlaceholder(str) { return !!str && (0, regex_1.regEx)(/\${[^}]*?}/).test(str); } function getCNBDependencies(nodes, config) { const deps = []; for (const node of nodes) { const depString = node.val.trim(); if ((0, extract_1.isDockerRef)(depString)) { const dep = (0, extract_2.getDep)(depString.replace(extract_1.DOCKER_PREFIX, ''), true, config.registryAliases); dep.fileReplacePosition = node.position; // TODO: should not be null if (dep.currentValue || dep.currentDigest) { deps.push(dep); } } else if ((0, extract_1.isBuildpackRegistryRef)(depString)) { const dep = (0, extract_1.getDep)(depString.replace(extract_1.BUILDPACK_REGISTRY_PREFIX, '')); if (dep?.currentValue) { dep.fileReplacePosition = node.position; // TODO: should not be null deps.push(dep); } } } return deps; } function getAllCNBDependencies(node, config) { const pluginNodes = node.childNamed('build')?.childNamed('plugins')?.childrenNamed('plugin') ?? []; const pluginNode = pluginNodes.find((pluginNode) => { return (pluginNode.valueWithPath('groupId')?.trim() === 'org.springframework.boot' && pluginNode.valueWithPath('artifactId')?.trim() === 'spring-boot-maven-plugin'); }); if (!pluginNode) { return null; } const deps = []; const imageNode = pluginNode.childNamed('configuration')?.childNamed('image'); if (!imageNode) { return null; } const builder = getCNBDependencies(imageNode.childrenNamed('builder'), config); const runImage = getCNBDependencies(imageNode.childrenNamed('runImage'), config); const buildpacks = getCNBDependencies(imageNode.childNamed('buildpacks')?.childrenNamed('buildpack') ?? [], config); deps.push(...builder, ...runImage, ...buildpacks); return deps.length ? deps : null; } function depFromNode(node, underBuildSettingsElement) { if (!('valueWithPath' in node)) { return null; } let groupId = node.valueWithPath('groupId')?.trim(); const artifactId = node.valueWithPath('artifactId')?.trim(); const currentValue = node.valueWithPath('version')?.trim(); let depType; if (!groupId && node.name === 'plugin') { groupId = 'org.apache.maven.plugins'; } if (groupId && artifactId && currentValue) { const depName = `${groupId}:${artifactId}`; const versionNode = node.descendantWithPath('version'); const fileReplacePosition = versionNode.position; // TODO: should not be null const datasource = maven_1.MavenDatasource.id; const result = { datasource, depName, currentValue, fileReplacePosition, registryUrls: [], }; switch (node.name) { case 'plugin': case 'extension': depType = 'build'; break; case 'parent': depType = 'parent'; break; case 'dependency': if (underBuildSettingsElement) { depType = 'build'; } else if (node.valueWithPath('optional')?.trim() === 'true') { depType = 'optional'; } else { depType = node.valueWithPath('scope')?.trim() ?? 'compile'; // maven default scope is compile } break; } if (depType) { result.depType = depType; } return result; } return null; } function deepExtract(node, result = [], isRoot = true, underBuildSettingsElement = false) { const dep = depFromNode(node, underBuildSettingsElement); if (dep && !isRoot) { result.push(dep); } if (node.children) { for (const child of node.children) { deepExtract(child, result, false, node.name === 'build' || node.name === 'reporting' || underBuildSettingsElement); } } return result; } function applyProps(dep, depPackageFile, props) { let result = dep; let anyChange = false; const alreadySeenProps = new Set(); do { const [returnedResult, returnedAnyChange, fatal] = applyPropsInternal(result, depPackageFile, props, alreadySeenProps); if (fatal) { dep.skipReason = 'recursive-placeholder'; return dep; } result = returnedResult; anyChange = returnedAnyChange; } while (anyChange); if (containsPlaceholder(result.depName)) { result.skipReason = 'name-placeholder'; } else if (containsPlaceholder(result.currentValue)) { result.skipReason = 'version-placeholder'; } return result; } function applyPropsInternal(dep, depPackageFile, props, previouslySeenProps) { let anyChange = false; let fatal = false; const seenProps = new Set(); const replaceAll = (str) => str.replace((0, regex_1.regEx)(/\${[^}]*?}/g), (substr) => { const propKey = substr.slice(2, -1).trim(); // TODO: wrong types here, props is already `MavenProp` const propValue = props[propKey]; if (propValue) { anyChange = true; if (previouslySeenProps.has(propKey)) { fatal = true; } else { seenProps.add(propKey); } return propValue.val; } return substr; }); let depName = dep.depName; if (dep.depName) { depName = replaceAll(dep.depName); } const registryUrls = dep.registryUrls.map((url) => replaceAll(url)); let fileReplacePosition = dep.fileReplacePosition; let propSource = dep.propSource; let sharedVariableName = null; let currentValue = null; if (dep.currentValue) { currentValue = dep.currentValue.replace((0, regex_1.regEx)(/^\${[^}]*?}$/), (substr) => { const propKey = substr.slice(2, -1).trim(); // TODO: wrong types here, props is already `MavenProp` const propValue = props[propKey]; if (propValue) { sharedVariableName ??= propKey; fileReplacePosition = propValue.fileReplacePosition; propSource = propValue.packageFile ?? // istanbul ignore next undefined; anyChange = true; if (previouslySeenProps.has(propKey)) { fatal = true; } else { seenProps.add(propKey); } return propValue.val; } return substr; }); } const result = { ...dep, depName, registryUrls, fileReplacePosition, propSource, currentValue, }; if (sharedVariableName) { result.sharedVariableName = sharedVariableName; } if (propSource && depPackageFile !== propSource) { result.editFile = propSource; } for (const prop of seenProps) { previouslySeenProps.add(prop); } return [result, anyChange, fatal]; } function resolveParentFile(packageFile, parentPath) { let parentFile = 'pom.xml'; let parentDir = parentPath; const parentBasename = upath_1.default.basename(parentPath); if (parentBasename === 'pom.xml' || parentBasename.endsWith('.pom.xml')) { parentFile = parentBasename; parentDir = upath_1.default.dirname(parentPath); } const dir = upath_1.default.dirname(packageFile); return upath_1.default.normalize(upath_1.default.join(dir, parentDir, parentFile)); } function extractPackage(rawContent, packageFile, config) { if (!rawContent) { return null; } const project = parsePom(rawContent, packageFile); if (!project) { return null; } const result = { datasource: maven_1.MavenDatasource.id, packageFile, deps: [], }; result.deps = deepExtract(project); const CNBDependencies = getAllCNBDependencies(project, config); if (CNBDependencies) { result.deps.push(...CNBDependencies); } const propsNode = project.childNamed('properties'); const props = {}; if (propsNode?.children) { for (const propNode of propsNode.children) { const key = propNode.name; const val = propNode?.val?.trim(); if (key && val && propNode.position) { const fileReplacePosition = propNode.position; props[key] = { val, fileReplacePosition, packageFile }; } } } result.mavenProps = props; const repositories = project.childNamed('repositories'); if (repositories?.children) { const repoUrls = []; for (const repo of repositories.childrenNamed('repository')) { const repoUrl = repo.valueWithPath('url')?.trim(); if (repoUrl) { repoUrls.push(repoUrl); } } result.deps.forEach((dep) => { if (is_1.default.array(dep.registryUrls)) { repoUrls.forEach((url) => dep.registryUrls.push(url)); } }); } if (packageFile && project.childNamed('parent')) { const parentPath = project.valueWithPath('parent.relativePath')?.trim() ?? '../pom.xml'; result.parent = resolveParentFile(packageFile, parentPath); } if (project.childNamed('version')) { result.packageFileVersion = project.valueWithPath('version').trim(); } return result; } function extractRegistries(rawContent) { if (!rawContent) { return []; } const settings = parseSettings(rawContent); if (!settings) { return []; } const urls = []; const mirrorUrls = parseUrls(settings, 'mirrors'); urls.push(...mirrorUrls); settings.childNamed('profiles')?.eachChild((profile) => { const repositoryUrls = parseUrls(profile, 'repositories'); urls.push(...repositoryUrls); }); // filter out duplicates return [...new Set(urls)]; } function parseUrls(xmlNode, path) { const children = xmlNode.descendantWithPath(path); const urls = []; if (children?.children) { children.eachChild((child) => { const url = child.valueWithPath('url'); if (url) { urls.push(url); } }); } return urls; } function parseSettings(raw) { let settings; try { settings = new xmldoc_1.XmlDocument(raw); } catch { return null; } const { name, attr } = settings; if (name !== 'settings') { return null; } if (supportedNamespaces.includes(attr.xmlns)) { return settings; } return null; } function resolveParents(packages) { const packageFileNames = []; const extractedPackages = {}; const extractedDeps = {}; const extractedProps = {}; const registryUrls = {}; packages.forEach((pkg) => { const name = pkg.packageFile; packageFileNames.push(name); extractedPackages[name] = pkg; extractedDeps[name] = []; }); // Construct package-specific prop scopes // and merge them in reverse order, // which allows inheritance/overriding. packageFileNames.forEach((name) => { registryUrls[name] = new Set(); const propsHierarchy = []; const visitedPackages = new Set(); let pkg = extractedPackages[name]; while (pkg) { propsHierarchy.unshift(pkg.mavenProps); if (pkg.deps) { pkg.deps.forEach((dep) => { if (dep.registryUrls) { dep.registryUrls.forEach((url) => { registryUrls[name].add(url); }); } }); } if (pkg.parent && !visitedPackages.has(pkg.parent)) { visitedPackages.add(pkg.parent); pkg = extractedPackages[pkg.parent]; } else { pkg = null; } } propsHierarchy.unshift({}); extractedProps[name] = Object.assign.apply(null, propsHierarchy); }); // Resolve registryUrls packageFileNames.forEach((name) => { const pkg = extractedPackages[name]; pkg.deps.forEach((rawDep) => { const urlsSet = new Set([ ...(rawDep.registryUrls ?? []), ...registryUrls[name], ]); rawDep.registryUrls = [...urlsSet]; }); }); const rootDeps = new Set(); // Resolve placeholders packageFileNames.forEach((name) => { const pkg = extractedPackages[name]; pkg.deps.forEach((rawDep) => { const dep = applyProps(rawDep, name, extractedProps[name]); if (dep.depType === 'parent') { const parentPkg = extractedPackages[pkg.parent]; const hasParentWithNoParent = parentPkg && !parentPkg.parent; const hasParentWithExternalParent = parentPkg && !packageFileNames.includes(parentPkg.parent); if (hasParentWithNoParent || hasParentWithExternalParent) { rootDeps.add(dep.depName); } } const sourceName = dep.propSource ?? name; extractedDeps[sourceName].push(dep); }); }); const packageFiles = packageFileNames.map((packageFile) => { const pkg = extractedPackages[packageFile]; const deps = extractedDeps[packageFile]; for (const dep of deps) { if (rootDeps.has(dep.depName)) { dep.depType = 'parent-root'; } } return { ...pkg, deps }; }); return packageFiles; } function cleanResult(packageFiles) { packageFiles.forEach((packageFile) => { delete packageFile.mavenProps; delete packageFile.parent; packageFile.deps.forEach((dep) => { delete dep.propSource; //Add Registry From SuperPom if (dep.datasource === maven_1.MavenDatasource.id) { dep.registryUrls.push(common_1.MAVEN_REPO); } }); }); return packageFiles; } function extractExtensions(rawContent, packageFile) { if (!rawContent) { return null; } const extensions = parseExtensions(rawContent, packageFile); if (!extensions) { return null; } const result = { datasource: maven_1.MavenDatasource.id, packageFile, deps: [], }; result.deps = deepExtract(extensions); return result; } async function extractAllPackageFiles(config, packageFiles) { const packages = []; const additionalRegistryUrls = []; for (const packageFile of packageFiles) { const content = await (0, fs_1.readLocalFile)(packageFile, 'utf8'); if (!content) { logger_1.logger.debug({ packageFile }, 'packageFile has no content'); continue; } if (packageFile.endsWith('settings.xml')) { const registries = extractRegistries(content); if (registries) { logger_1.logger.debug({ registries, packageFile }, 'Found registryUrls in settings.xml'); additionalRegistryUrls.push(...registries); } } else if (packageFile.endsWith('.mvn/extensions.xml')) { const extensions = extractExtensions(content, packageFile); if (extensions) { packages.push(extensions); } else { logger_1.logger.trace({ packageFile }, 'can not read extensions'); } } else { const pkg = extractPackage(content, packageFile, config); if (pkg) { packages.push(pkg); } else { logger_1.logger.trace({ packageFile }, 'can not read dependencies'); } } } if (additionalRegistryUrls) { for (const pkgFile of packages) { for (const dep of pkgFile.deps) { if (dep.registryUrls) { dep.registryUrls.unshift(...additionalRegistryUrls); } } } } return cleanResult(resolveParents(packages)); } //# sourceMappingURL=extract.js.map