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 818f767e591124fc4e890ccb566ff5d8f355e19e Author: Jinal Sarvaiya <[email protected]> AuthorDate: Wed Nov 11 20:54:56 2026 +0000 fix:review feedback --- docs/modules/ROOT/pages/existing-project.adoc | 8 +-- .../src/main/resources/skills/camel-plan/SKILL.md | 6 +- .../kit/generator/ResourceConsistencyTest.java | 6 +- .../apache/camel/kit/graph/ProjectDiscovery.java | 44 ++++++++++--- .../camel/kit/graph/ProjectDiscoveryTest.java | 74 ++++++++++++++++++++++ 5 files changed, 120 insertions(+), 18 deletions(-) diff --git a/docs/modules/ROOT/pages/existing-project.adoc b/docs/modules/ROOT/pages/existing-project.adoc index 5da2d713..6e234215 100644 --- a/docs/modules/ROOT/pages/existing-project.adoc +++ b/docs/modules/ROOT/pages/existing-project.adoc @@ -118,10 +118,10 @@ hide regressions. There are two ways to get it: If neither happened, `/camel-validate` reports baseline preservation as `UNVERIFIED`. It does not run the tests afterwards and call that the baseline. -`.camel-kit/baseline-test-results.json` stores the outcome of every individual test (`class#method` mapped to `passed`, -`failed`, `error` or `skipped`), the Git revision the tests ran against and whether tracked files had uncommitted -changes. Validation compares tests by identity, so a test that now fails, was skipped, or disappeared is a regression -even when the number of failures is unchanged. +`.camel-kit/baseline-test-results.json` stores the outcome of every individual test (`[module:]class#method` mapped to +`passed`, `failed`, `error` or `skipped`), the Git revision the tests ran against, whether tracked files had uncommitted +changes, and whether all test reports were successfully parsed. Validation compares tests by identity, so a test that +now fails, was skipped, or disappeared is a regression even when the number of failures is unchanged. Camel Kit warns when the project uses an unsupported Camel version (3.x), an unsupported runtime or DSL, or when no routes were found in the project graph. In the last case the route baseline is incomplete, so read the existing route diff --git a/kit/camel-kit-core/src/main/resources/skills/camel-plan/SKILL.md b/kit/camel-kit-core/src/main/resources/skills/camel-plan/SKILL.md index f0e4842e..db9c58ba 100644 --- a/kit/camel-kit-core/src/main/resources/skills/camel-plan/SKILL.md +++ b/kit/camel-kit-core/src/main/resources/skills/camel-plan/SKILL.md @@ -190,9 +190,9 @@ names the evidence. Report it and ask the user to resolve it; do not pick one, a shipped version tables. **The test baseline must exist before execution.** Add a first task to the plan that confirms -`.camel-kit/baseline-test-results.json` is usable (`status` `passed` or `failed`, non-empty `tests`, `dirty` false, -`revision` equal to the current HEAD) and, if not, that `camel-execute` captures it before any edit (see its Step 0.6). -If it cannot be captured, the plan must say that baseline preservation will be UNVERIFIED. +`.camel-kit/baseline-test-results.json` is usable (`status` `passed` or `failed`, non-empty `tests`, `complete` true, +`dirty` false, `revision` equal to the current HEAD) and, if not, that `camel-execute` captures it before any edit (see +its Step 0.6). If it cannot be captured, the plan must say that baseline preservation will be UNVERIFIED. **`project.discoveryGaps` lists what discovery could not determine** (for example no routes found in the graph, or routes in more than one DSL). Inspect the source for those items and ask the user about anything still ambiguous before diff --git a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/ResourceConsistencyTest.java b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/ResourceConsistencyTest.java index 0d80b03f..52a9950f 100644 --- a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/ResourceConsistencyTest.java +++ b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/generator/ResourceConsistencyTest.java @@ -1532,8 +1532,8 @@ class ResourceConsistencyTest { String execute = Files.readString(skills.resolve("camel-execute/SKILL.md")); String plan = Files.readString(skills.resolve("camel-plan/SKILL.md")); - for (String field : List.of("status", "reason", "timestamp", "revision", "dirty", "testCount", "tests", - "nonPassing")) { + for (String field : List.of("status", "reason", "timestamp", "revision", "dirty", "complete", "testCount", + "tests", "nonPassing")) { assertTrue(writer.contains("\"" + field + "\"") || writer.contains("putObject(\"" + field + "\")") || writer.contains("putArray(\"" + field + "\")"), "init no longer writes baseline field " + field); assertTrue(validate.contains("\"" + field + "\""), "camel-validate must document baseline field " + field); @@ -1545,7 +1545,7 @@ class ResourceConsistencyTest { "the dirty flag must ignore init's own untracked output"); // validation compares test identities, never counts, and cannot establish the baseline after the edits - assertTrue(validate.contains("by test identity") && validate.contains("`class#method`"), + assertTrue(validate.contains("by test identity") && validate.contains("`[module:]class#method`"), "camel-validate must compare per-test identities"); assertTrue(validate.contains("**REGRESSION** unless the design spec"), "a baseline test that disappeared must be a regression unless approved"); 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 c408b679..6b0c4eda 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 @@ -187,19 +187,47 @@ 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. + * <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. */ private static String detectSpringBootVersion(ProjectGraph graph) { Map<String, Set<String>> bomVersions = collectManagedVersions(graph, "spring-boot-dependencies"); - if (!bomVersions.isEmpty()) { - // If modules agree on the BOM version, use it; if they conflict, return null - return bomVersions.size() == 1 ? bomVersions.keySet().iterator().next() : null; - } Map<String, Set<String>> parentVersions = collectParentVersions(graph, "spring-boot-starter-parent"); - if (!parentVersions.isEmpty()) { - // If modules agree on the parent version, use it; if they conflict, return null - return parentVersions.size() == 1 ? parentVersions.keySet().iterator().next() : null; + + // Compute effective version per module: imports override parents + 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; + 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; + } + } + } } - return null; + + // If all modules agree on the effective version, return it; otherwise return null + Set<String> distinctVersions = new TreeSet<>(effectiveVersions.values()); + return distinctVersions.size() == 1 ? distinctVersions.iterator().next() : 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 74c674ea..ef4966cf 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 @@ -789,4 +789,78 @@ class ProjectDiscoveryTest { "Conflicting Spring Boot versions across modules should not be resolved"); assertEquals("spring-boot", result.runtime()); } + + @Test + void detectsSpringBootConflictWhenOneModuleImportsAndAnotherInheritsParent() { + // Module A imports spring-boot-dependencies:3.3.4 + // Module B inherits spring-boot-starter-parent:3.4.0 (no import to override) + // Should detect conflict and return null + ProjectGraph graph = new ProjectGraph(); + graph.addNode(springBootStarter("module-a/pom.xml")); + graph.addNode(springBootStarter("module-b/pom.xml")); + + // Module-a imports spring-boot-dependencies:3.3.4 + graph.addNode(new GraphNode( + "maven-managed:module-a/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", "module-a/pom.xml"))); + + // Module-b inherits spring-boot-starter-parent:3.4.0 + graph.addNode(new GraphNode( + "maven-parent:module-b/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.4.0", + "file", "module-b/pom.xml"))); + + ProjectDiscovery.DiscoveryResult result = ProjectDiscovery.discover(graph); + + assertNull(result.springBootVersion(), + "Import in one module and parent in another with different versions should be a conflict"); + assertEquals("spring-boot", result.runtime()); + } + + @Test + void importOverridesParentInSameModule() { + // Single module inherits spring-boot-starter-parent:3.4.0 but explicitly imports + // spring-boot-dependencies:3.3.4 -> import wins + ProjectGraph graph = new ProjectGraph(); + graph.addNode(springBootStarter("pom.xml")); + + // Inherits parent 3.4.0 + 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.4.0", + "file", "pom.xml"))); + + // But 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); + + assertEquals("3.3.4", result.springBootVersion(), + "Import should take precedence over parent in the same module"); + assertEquals("spring-boot", result.runtime()); + } }
