Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,11 @@ private List<Pom> getAncestryWithinProject(Pom projectPom, Map<Path, Pom> projec
return null;
}
String relativePath = parent.getRelativePath();
if (StringUtils.isBlank(relativePath)) {
// An explicit empty <relativePath/> means "do not look for the parent locally"
if (relativePath != null && relativePath.isEmpty()) {
return null;
}
if (relativePath == null) {
relativePath = "../pom.xml";
}
Path parentPath = projectPom.getSourcePath()
Expand Down Expand Up @@ -516,10 +520,12 @@ public Pom download(GroupArtifactVersion gav,
}
}

if (containingPom != null && containingPom.getRequested().getSourcePath() != null) {
// An explicit empty <relativePath/> means "do not look for the parent locally"
if (containingPom != null && containingPom.getRequested().getSourcePath() != null &&
!(relativePath != null && relativePath.isEmpty())) {
// Maven POM §4.0.0 specifies that <relativePath> defaults to ".." when omitted.
// See DefaultModelBuilder#readParentLocally in maven-model-builder.
String effectiveRelativePath = StringUtils.isBlank(relativePath) ? ".." : relativePath;
String effectiveRelativePath = relativePath == null ? ".." : relativePath;
if (!effectiveRelativePath.contains(":")) {
Path folderContainingPom = containingPom.getRequested().getSourcePath().getParent();
if (folderContainingPom != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,103 @@ void canResolveDifferentVersionOfProjectPom() {

assertDoesNotThrow(() -> downloader.download(gav, Objects.requireNonNull(pom.getParent()).getRelativePath(), resolvedPom, singletonList(nonexistentRepo)));
}

@Issue("https://github.com/moderneinc/customer-requests/issues/1950")
@Test
void emptyRelativePathSkipsLocalParentLookup() {
// A local parent POM exists at ../pom.xml that matches the GAV,
// but the child specifies <relativePath/> (empty string) which means
// "do not look for the parent locally".
var gav = new GroupArtifactVersion("test", "parent", "1.0.0");

Path rootPomXml = Path.of("pom.xml");
Pom parentPom = Pom.builder()
.sourcePath(rootPomXml)
.repository(MAVEN_CENTRAL)
.parent(null)
.gav(new ResolvedGroupArtifactVersion(
MAVEN_CENTRAL.getUri(), "test", "parent", "1.0.0", null))
.build();

Path childPomXml = Path.of("child/pom.xml");
// Empty string for relativePath simulates <relativePath/>
Pom childPom = Pom.builder()
.sourcePath(childPomXml)
.repository(MAVEN_CENTRAL)
.parent(new Parent(gav, ""))
.gav(new ResolvedGroupArtifactVersion(
MAVEN_CENTRAL.getUri(), "test", "child", "1.0.0", null))
.build();

ResolvedPom resolvedPom = ResolvedPom.builder()
.requested(childPom)
.repositories(singletonList(MAVEN_CENTRAL))
.build();

Map<Path, Pom> pomsByPath = new HashMap<>();
pomsByPath.put(rootPomXml, parentPom);
pomsByPath.put(childPomXml, childPom);

MavenPomDownloader downloader = new MavenPomDownloader(pomsByPath, ctx);

// With empty relativePath, the downloader should NOT find the parent locally
// via relative path resolution, and should instead go to remote repos.
// Since the parent IS available on Maven Central, this should succeed
// by downloading from the remote repository, not by local path resolution.
assertDoesNotThrow(() -> downloader.download(gav, "", resolvedPom, singletonList(MAVEN_CENTRAL)));
}

@Issue("https://github.com/moderneinc/customer-requests/issues/1950")
@Test
void emptyRelativePathSkipsPathBasedLookupWithPlaceholderVersion() {
// A local parent POM exists at ../pom.xml, but the child specifies <relativePath/>
// (empty string) and references the parent with a placeholder version ${my.version}.
// Without the fix, the path-based lookup (stage 3) would default relativePath to "..",
// find the parent, and accept it because "${" in the version relaxes the version check.
// With the fix, stage 3 is skipped entirely due to empty relativePath.
var gav = new GroupArtifactVersion("test", "parent", "${my.version}");

Path rootPomXml = Path.of("pom.xml");
Pom parentPom = Pom.builder()
.sourcePath(rootPomXml)
.repository(MAVEN_CENTRAL)
.parent(null)
.gav(new ResolvedGroupArtifactVersion(
MAVEN_CENTRAL.getUri(), "test", "parent", "1.0.0", null))
.build();

Path childPomXml = Path.of("child/pom.xml");
Pom childPom = Pom.builder()
.sourcePath(childPomXml)
.repository(MAVEN_CENTRAL)
.parent(new Parent(gav, ""))
.gav(new ResolvedGroupArtifactVersion(
MAVEN_CENTRAL.getUri(), "test", "child", "1.0.0", null))
.build();

ResolvedPom resolvedPom = ResolvedPom.builder()
.requested(childPom)
.repositories(singletonList(MAVEN_CENTRAL))
.build();

Map<Path, Pom> pomsByPath = new HashMap<>();
pomsByPath.put(rootPomXml, parentPom);
pomsByPath.put(childPomXml, childPom);

String httpUrl = "http://%s.com".formatted(UUID.randomUUID());
MavenRepository nonexistentRepo = new MavenRepository("repo", httpUrl, null, null, false, null, null, null, null);

MavenPomDownloader downloader = new MavenPomDownloader(pomsByPath, ctx);

// With empty relativePath, the path-based lookup (stage 3) should be skipped.
// The placeholder ${my.version} can't be resolved via GAV iteration (stage 2)
// because the parent doesn't define my.version. Without the fix, stage 3 would
// find the parent at ../pom.xml and accept it (placeholder version relaxes the
// version check). With the fix, the download falls through to the remote repo
// and fails (IllegalArgumentException from the unresolved placeholder in the URI).
assertThrows(Exception.class,
() -> downloader.download(gav, "", resolvedPom, singletonList(nonexistentRepo)));
}
}

@Nested
Expand Down