From 180a2833834adf8423150bcf07e4db15d07a4182 Mon Sep 17 00:00:00 2001 From: Kamil Patryk Kozakowski Date: Sun, 5 Jul 2026 21:17:25 +0200 Subject: [PATCH] siblings --- .../resolver/SiblingDependencyResolver.java | 20 +++- .../SiblingDependencyResolverTest.java | 102 ++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/resolver/SiblingDependencyResolverTest.java diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/SiblingDependencyResolver.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/SiblingDependencyResolver.java index 32074ae..95aa691 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/SiblingDependencyResolver.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/SiblingDependencyResolver.java @@ -131,7 +131,7 @@ public class SiblingDependencyResolver { if (artifactId != null && targetArtifacts.contains(artifactId)) { Path siblingDir = p.getParent(); if (!siblingDir.equals(projectDir)) { - log.info("Discovered local Maven sibling dependency: {}", siblingDir); + log.info("Discovered local Maven sibling dependency via artifact match: {}", siblingDir); siblings.add(siblingDir); } } @@ -142,6 +142,24 @@ public class SiblingDependencyResolver { } } } + + // Also directly add aggregator directories declared in this pom + Matcher moduleMatcher = Pattern.compile("(.*?)").matcher(content); + while (moduleMatcher.find()) { + String moduleName = moduleMatcher.group(1).trim(); + Path moduleDir = projectDir.resolve(moduleName).normalize(); + if (Files.exists(moduleDir) && Files.exists(moduleDir.resolve("pom.xml")) && !moduleDir.equals(projectDir)) { + log.info("Discovered local Maven sibling dependency via : {}", moduleDir); + siblings.add(moduleDir); + } + } + + // Also add parent dir if it has a pom.xml + Path parentDir = projectDir.getParent(); + if (parentDir != null && Files.exists(parentDir.resolve("pom.xml")) && !parentDir.equals(projectDir)) { + log.info("Discovered local Maven parent sibling: {}", parentDir); + siblings.add(parentDir); + } } return siblings; } diff --git a/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/resolver/SiblingDependencyResolverTest.java b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/resolver/SiblingDependencyResolverTest.java new file mode 100644 index 0000000..9b85d79 --- /dev/null +++ b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/resolver/SiblingDependencyResolverTest.java @@ -0,0 +1,102 @@ +package click.kamil.springstatemachineexporter.analysis.resolver; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class SiblingDependencyResolverTest { + + @Test + void shouldResolveGradleSiblingsTransitively(@TempDir Path tempDir) throws IOException { + // Create root project + Path rootDir = tempDir.resolve("my-root-project"); + Files.createDirectories(rootDir); + Files.writeString(rootDir.resolve("settings.gradle"), "include 'module-a', 'module-b', 'module-c'"); + + // Create module A (depends on B) + Path moduleA = rootDir.resolve("module-a"); + Files.createDirectories(moduleA); + Files.writeString(moduleA.resolve("build.gradle"), "dependencies { implementation project(':module-b') }"); + + // Create module B (depends on C) + Path moduleB = rootDir.resolve("module-b"); + Files.createDirectories(moduleB); + Files.writeString(moduleB.resolve("build.gradle"), "dependencies { implementation project(':module-c') }"); + + // Create module C (no deps) + Path moduleC = rootDir.resolve("module-c"); + Files.createDirectories(moduleC); + Files.writeString(moduleC.resolve("build.gradle"), ""); + + SiblingDependencyResolver resolver = new SiblingDependencyResolver(); + + // When we resolve from A, it should transitively find B and C + Set siblings = resolver.resolveSiblings(moduleA); + + assertThat(siblings).containsExactlyInAnyOrder(moduleB.normalize(), moduleC.normalize()); + } + + @Test + void shouldResolveMavenSiblingsTransitively(@TempDir Path tempDir) throws IOException { + // Create root project + Path rootDir = tempDir.resolve("my-maven-root"); + Files.createDirectories(rootDir); + Files.writeString(rootDir.resolve("pom.xml"), "com.acmeroot"); + // Create a dummy .git so findMavenRoot knows where to stop + Files.createDirectories(rootDir.resolve(".git")); + + // Create module A (depends on B) + Path moduleA = rootDir.resolve("module-a"); + Files.createDirectories(moduleA); + Files.writeString(moduleA.resolve("pom.xml"), "com.acmemodule-acom.acmemodule-b1.0"); + + // Create module B (depends on C) + Path moduleB = rootDir.resolve("module-b"); + Files.createDirectories(moduleB); + Files.writeString(moduleB.resolve("pom.xml"), "com.acmemodule-bcom.acmemodule-c1.0"); + + // Create module C (no deps) + Path moduleC = rootDir.resolve("module-c"); + Files.createDirectories(moduleC); + Files.writeString(moduleC.resolve("pom.xml"), "com.acmemodule-c"); + + SiblingDependencyResolver resolver = new SiblingDependencyResolver(); + + // When we resolve from A, it should transitively find B and C and the parent rootDir + Set siblings = resolver.resolveSiblings(moduleA); + + assertThat(siblings).containsExactlyInAnyOrder(moduleB.normalize(), moduleC.normalize(), rootDir.normalize()); + } + + @Test + void shouldResolveMavenModulesFromAggregator(@TempDir Path tempDir) throws IOException { + // Create root project + Path rootDir = tempDir.resolve("my-aggregator-root"); + Files.createDirectories(rootDir); + Files.writeString(rootDir.resolve("pom.xml"), "com.acmerootservice-xservice-y"); + Files.createDirectories(rootDir.resolve(".git")); + + // Create service X + Path serviceX = rootDir.resolve("service-x"); + Files.createDirectories(serviceX); + Files.writeString(serviceX.resolve("pom.xml"), "service-x"); + + // Create service Y + Path serviceY = rootDir.resolve("service-y"); + Files.createDirectories(serviceY); + Files.writeString(serviceY.resolve("pom.xml"), "service-y"); + + SiblingDependencyResolver resolver = new SiblingDependencyResolver(); + + // Resolving from rootDir should pick up the tags and resolve service-x and service-y + Set siblings = resolver.resolveSiblings(rootDir); + + assertThat(siblings).containsExactlyInAnyOrder(serviceX.normalize(), serviceY.normalize()); + } +}