diff --git a/src/main/java/org/apache/maven/plugins/deploy/DeployMojo.java b/src/main/java/org/apache/maven/plugins/deploy/DeployMojo.java index 3bd0627..e330535 100644 --- a/src/main/java/org/apache/maven/plugins/deploy/DeployMojo.java +++ b/src/main/java/org/apache/maven/plugins/deploy/DeployMojo.java @@ -33,6 +33,7 @@ import org.apache.maven.api.ProducedArtifact; import org.apache.maven.api.Project; import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.SourceRoot; import org.apache.maven.api.di.Inject; import org.apache.maven.api.model.DistributionManagement; import org.apache.maven.api.model.Plugin; @@ -436,6 +437,7 @@ private ArtifactDeployerRequest createDeployerRequest() { + ":" + project.getVersion() + " are not valid: they use invalid characters."); } + List toDeploy = new ArrayList<>(deployables); for (Artifact deployable : deployables) { if (!isValidClassifier(deployable.getClassifier())) { throw new MojoException("The classifier of attached artifact " + deployable @@ -446,19 +448,19 @@ private ArtifactDeployerRequest createDeployerRequest() { if (attachedArtifacts.isEmpty()) { throw new MojoException( "The packaging for this project did not assign a file to the build artifact"); + } else if (usesModuleSourceHierarchy(project, projectManager)) { + getLog().info("Module source hierarchy: no main artifact produced;" + + " deploying POM and per-module artifacts only."); + toDeploy.remove(deployable); + } else if (allowIncompleteProjects) { + getLog().warn("The packaging plugin for this project did not assign" + + " a main file to the project but it has attachments." + + " Change packaging to 'pom'." + + " Incomplete projects like this will fail in future Maven versions!"); + toDeploy.remove(deployable); } else { - if (allowIncompleteProjects) { - getLog().warn(""); - getLog().warn("The packaging plugin for this project did not assign"); - getLog().warn( - "a main file to the project but it has attachments. Change packaging to 'pom'."); - getLog().warn(""); - getLog().warn("Incomplete projects like this will fail in future Maven versions!"); - getLog().warn(""); - } else { - throw new MojoException("The packaging plugin for this project did not assign " - + "a main file to the project but it has attachments. Change packaging to 'pom'."); - } + throw new MojoException("The packaging plugin for this project did not assign " + + "a main file to the project but it has attachments. Change packaging to 'pom'."); } } else { throw new MojoException("The packaging for this project did not assign " @@ -470,7 +472,7 @@ private ArtifactDeployerRequest createDeployerRequest() { ArtifactDeployerRequest request = ArtifactDeployerRequest.builder() .session(session) .repository(getDeploymentRepository(session.isVersionSnapshot(project.getVersion()))) - .artifacts(deployables) + .artifacts(toDeploy) .retryFailedDeploymentCount(Math.max(1, Math.min(10, getRetryFailedDeploymentCount()))) .build(); @@ -681,6 +683,12 @@ private void warnIfPolicyMismatch(org.apache.maven.api.model.DeploymentRepositor } } + private static boolean usesModuleSourceHierarchy(Project project, ProjectManager projectManager) { + return projectManager.getSourceRoots(project).stream() + .filter(SourceRoot::enabled) + .anyMatch(sr -> sr.module().isPresent()); + } + private boolean isValidPath(Artifact a) { return getArtifactManager().getPath(a).filter(Files::isRegularFile).isPresent(); } diff --git a/src/test/java/org/apache/maven/plugins/deploy/DeployMojoTest.java b/src/test/java/org/apache/maven/plugins/deploy/DeployMojoTest.java index 58175e6..725ffc4 100644 --- a/src/test/java/org/apache/maven/plugins/deploy/DeployMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/deploy/DeployMojoTest.java @@ -25,12 +25,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import org.apache.maven.api.Artifact; import org.apache.maven.api.ProducedArtifact; import org.apache.maven.api.Project; import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.SourceRoot; import org.apache.maven.api.di.Inject; import org.apache.maven.api.di.Priority; import org.apache.maven.api.di.Provides; @@ -68,6 +70,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** @@ -569,6 +572,45 @@ void ambiguousLegacyAltDeploymentRepositoryRefused(DeployMojo mojo) throws Excep assertEquals("Ambiguous syntax for alternative repository.", e.getMessage()); } + @Test + @InjectMojo(goal = "deploy") + @MojoParameter(name = "deployAtEnd", value = "false") + void moduleSourceHierarchySkipsMainArtifactAtDeploy(DeployMojo mojo) throws Exception { + assertNotNull(mojo); + Project project = (Project) getVariableValueFromObject(mojo, "project"); + + // Simulate module source hierarchy: main artifact path is a directory (the build output dir) + artifactManager.setPath(project.getMainArtifact().get(), Paths.get(getBasedir(), "target/test-classes")); + + // Attach a per-module JAR as the real deployable artifact + ProducedArtifactStub moduleJar = new ProducedArtifactStub( + "org.apache.maven.test", "maven-deploy-test", "module.one", "1.0-SNAPSHOT", "jar"); + projectManager.attachArtifact( + project, + moduleJar, + Paths.get(getBasedir(), "target/test-classes/unit/maven-deploy-test-1.0-SNAPSHOT.jar")); + + // Stub a module source root on the projectManager + SourceRoot moduleRoot = mock(SourceRoot.class); + when(moduleRoot.enabled()).thenReturn(true); + when(moduleRoot.module()).thenReturn(Optional.of("org.example.module")); + when(projectManager.getSourceRoots(project)).thenReturn(List.of(moduleRoot)); + + ArtifactDeployerRequest request = execute(mojo); + + assertNotNull(request); + Collection artifacts = request.getArtifacts(); + // Main artifact (directory) must be excluded; POM and per-module JAR must be deployed + List keys = artifacts.stream().map(Artifact::key).collect(Collectors.toList()); + assertFalse( + keys.contains("org.apache.maven.test:maven-deploy-test:jar:1.0-SNAPSHOT"), + "directory main artifact must be excluded from deploy request"); + assertTrue(keys.contains("org.apache.maven.test:maven-deploy-test:pom:1.0-SNAPSHOT"), "POM must be deployed"); + assertTrue( + keys.contains("org.apache.maven.test:maven-deploy-test:jar:module.one:1.0-SNAPSHOT"), + "per-module JAR must be deployed"); + } + private ArtifactDeployerRequest execute(DeployMojo mojo) { ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ArtifactDeployerRequest.class); doNothing().when(artifactDeployer).deploy(requestCaptor.capture());