This is an automated email from the ASF dual-hosted git repository. luigidemasi pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-kit.git
commit 79ff4f9411447322b8ed7f6d13192e14a0a8a61e Author: Jinal Sarvaiya <[email protected]> AuthorDate: Wed Nov 11 21:36:37 2026 +0000 Fix Spring Boot version detection to handle parent/import uncertainty --- .../apache/camel/kit/graph/ProjectDiscovery.java | 49 +++++++++++++++------- .../camel/kit/graph/ProjectDiscoveryTest.java | 47 ++++++++++++++++++--- 2 files changed, 75 insertions(+), 21 deletions(-) diff --git a/kit/camel-kit-graph/src/main/java/org/apache/camel/kit/graph/ProjectDiscovery.java b/kit/camel-kit-graph/src/main/java/org/apache/camel/kit/graph/ProjectDiscovery.java index 6b0c4eda..88064a45 100644 --- a/kit/camel-kit-graph/src/main/java/org/apache/camel/kit/graph/ProjectDiscovery.java +++ b/kit/camel-kit-graph/src/main/java/org/apache/camel/kit/graph/ProjectDiscovery.java @@ -186,43 +186,60 @@ public final class ProjectDiscovery { /** * The Spring Boot framework version: an imported {@code spring-boot-dependencies}, otherwise the * {@code spring-boot-starter-parent} parent. This is a different artifact from the Camel BOM and is kept apart so - * the two are never mixed up. Returns null if modules disagree on the version. + * the two are never mixed up. Returns null if modules disagree on the version or if the effective version cannot be + * established from source-only evidence. * <p> - * Import-over-parent precedence is applied per module first, then effective versions are compared. A module that - * imports a version explicitly uses that version even if it also inherits a parent; modules without imports use - * their parent version. If effective versions disagree across modules, the project-wide constraint remains - * unresolved. + * Maven assembles inheritance before importing dependency management, and its importer preserves already-present + * management keys when processing imported BOMs. Therefore, when both a parent and an import are present with + * different versions, the effective framework version cannot be determined from POM source alone without executing + * Maven's model builder. In such cases, the version remains unresolved and discovery records a gap. */ private static String detectSpringBootVersion(ProjectGraph graph) { Map<String, Set<String>> bomVersions = collectManagedVersions(graph, "spring-boot-dependencies"); Map<String, Set<String>> parentVersions = collectParentVersions(graph, "spring-boot-starter-parent"); - // Compute effective version per module: imports override parents + // Compute effective version per module, recognizing when source-only evidence is insufficient Map<String, String> effectiveVersions = new TreeMap<>(); Set<String> allModules = new TreeSet<>(); bomVersions.values().forEach(allModules::addAll); parentVersions.values().forEach(allModules::addAll); for (String module : allModules) { - // Check if this module has an explicit import String importVersion = null; + String parentVersion = null; + + // Check if this module has an import for (Map.Entry<String, Set<String>> entry : bomVersions.entrySet()) { if (entry.getValue().contains(module)) { importVersion = entry.getKey(); break; } } - if (importVersion != null) { - effectiveVersions.put(module, importVersion); - } else { - // No import, use parent if present - for (Map.Entry<String, Set<String>> entry : parentVersions.entrySet()) { - if (entry.getValue().contains(module)) { - effectiveVersions.put(module, entry.getKey()); - break; - } + + // Check if this module has a parent + for (Map.Entry<String, Set<String>> entry : parentVersions.entrySet()) { + if (entry.getValue().contains(module)) { + parentVersion = entry.getKey(); + break; } } + + // Determine effective version for this module + if (importVersion != null && parentVersion != null) { + // Both present: can only establish version if they agree + if (importVersion.equals(parentVersion)) { + effectiveVersions.put(module, importVersion); + } else { + // Different versions: cannot determine effective version from source alone + return null; + } + } else if (importVersion != null) { + // Only import present + effectiveVersions.put(module, importVersion); + } else if (parentVersion != null) { + // Only parent present + effectiveVersions.put(module, parentVersion); + } } // If all modules agree on the effective version, return it; otherwise return null diff --git a/kit/camel-kit-graph/src/test/java/org/apache/camel/kit/graph/ProjectDiscoveryTest.java b/kit/camel-kit-graph/src/test/java/org/apache/camel/kit/graph/ProjectDiscoveryTest.java index ef4966cf..9ad1a52b 100644 --- a/kit/camel-kit-graph/src/test/java/org/apache/camel/kit/graph/ProjectDiscoveryTest.java +++ b/kit/camel-kit-graph/src/test/java/org/apache/camel/kit/graph/ProjectDiscoveryTest.java @@ -829,9 +829,11 @@ class ProjectDiscoveryTest { } @Test - void importOverridesParentInSameModule() { - // Single module inherits spring-boot-starter-parent:3.4.0 but explicitly imports - // spring-boot-dependencies:3.3.4 -> import wins + void cannotDetermineVersionWhenParentAndImportDisagree() { + // Single module inherits spring-boot-starter-parent:3.4.0 and also imports + // spring-boot-dependencies:3.3.4. Maven processes inheritance before imports + // and preserves existing management keys, so the effective version cannot be + // determined from POM source alone without executing Maven's model builder. ProjectGraph graph = new ProjectGraph(); graph.addNode(springBootStarter("pom.xml")); @@ -845,7 +847,42 @@ class ProjectDiscoveryTest { "version", "3.4.0", "file", "pom.xml"))); - // But imports 3.3.4 + // Also imports 3.3.4 + graph.addNode(new GraphNode( + "maven-managed:pom.xml:org.springframework.boot:spring-boot-dependencies", + NodeType.CONFIG_PROPERTY, + Map.of("kind", "managed", + "groupId", "org.springframework.boot", + "artifactId", "spring-boot-dependencies", + "version", "3.3.4", + "type", "pom", + "scope", "import", + "file", "pom.xml"))); + + ProjectDiscovery.DiscoveryResult result = ProjectDiscovery.discover(graph); + + assertNull(result.springBootVersion(), + "Cannot determine effective version when parent and import disagree (source-only evidence insufficient)"); + assertEquals("spring-boot", result.runtime()); + } + + @Test + void resolvesSameVersionWhenParentAndImportAgree() { + // When both parent and import declare the same version, that version can be established + ProjectGraph graph = new ProjectGraph(); + graph.addNode(springBootStarter("pom.xml")); + + // Inherits parent 3.3.4 + graph.addNode(new GraphNode( + "maven-parent:pom.xml:org.springframework.boot:spring-boot-starter-parent", + NodeType.CONFIG_PROPERTY, + Map.of("kind", "parent", + "groupId", "org.springframework.boot", + "artifactId", "spring-boot-starter-parent", + "version", "3.3.4", + "file", "pom.xml"))); + + // Also imports 3.3.4 graph.addNode(new GraphNode( "maven-managed:pom.xml:org.springframework.boot:spring-boot-dependencies", NodeType.CONFIG_PROPERTY, @@ -860,7 +897,7 @@ class ProjectDiscoveryTest { ProjectDiscovery.DiscoveryResult result = ProjectDiscovery.discover(graph); assertEquals("3.3.4", result.springBootVersion(), - "Import should take precedence over parent in the same module"); + "When parent and import agree on the same version, that version is established"); assertEquals("spring-boot", result.runtime()); } }
