diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java index f32c96288657..bfb69d22dfea 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java @@ -79,17 +79,22 @@ public void injectTransformedArtifacts(RepositorySystemSession session, MavenPro return; } if (Features.consumerPom(session.getConfigProperties())) { - Path buildDir = - project.getBuild() != null ? Paths.get(project.getBuild().getDirectory()) : null; - if (buildDir != null) { - Files.createDirectories(buildDir); - } - Path consumer = buildDir != null - ? Files.createTempFile(buildDir, CONSUMER_POM_CLASSIFIER + "-", ".pom") - : Files.createTempFile(CONSUMER_POM_CLASSIFIER + "-", ".pom"); - deferDeleteFile(consumer); + boolean alreadyAttached = project.getAttachedArtifacts().stream() + .anyMatch(a -> CONSUMER_POM_CLASSIFIER.equals(a.getClassifier()) && "pom".equals(a.getType())); + if (!alreadyAttached) { + Path buildDir = project.getBuild() != null + ? Paths.get(project.getBuild().getDirectory()) + : null; + if (buildDir != null) { + Files.createDirectories(buildDir); + } + Path consumer = buildDir != null + ? Files.createTempFile(buildDir, CONSUMER_POM_CLASSIFIER + "-", ".pom") + : Files.createTempFile(CONSUMER_POM_CLASSIFIER + "-", ".pom"); + deferDeleteFile(consumer); - project.addAttachedArtifact(createConsumerPomArtifact(project, consumer, session)); + project.addAttachedArtifact(createConsumerPomArtifact(project, consumer, session)); + } } else if (project.getModel().getDelegate().isRoot()) { throw new IllegalStateException( "The use of the root attribute on the model requires the buildconsumer feature to be active"); diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java index 81870abd0f3b..5efc02883a3e 100644 --- a/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java +++ b/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java @@ -29,6 +29,7 @@ import org.apache.maven.api.Constants; import org.apache.maven.api.services.Sources; +import org.apache.maven.model.Build; import org.apache.maven.model.Model; import org.apache.maven.model.v4.MavenStaxReader; import org.apache.maven.project.MavenProject; @@ -39,6 +40,7 @@ import org.eclipse.aether.deployment.DeployRequest; import org.eclipse.aether.installation.InstallRequest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.mockito.Mockito; import org.xmlunit.builder.DiffBuilder; import org.xmlunit.diff.Diff; @@ -279,4 +281,45 @@ private DeployRequest createDeployRequestWithoutConsumerPom() { request.setArtifacts(artifacts); return request; } + + @Test + void injectTransformedArtifactsTwiceShouldNotDuplicate(@TempDir Path tempDir) throws IOException { + // Set up a minimal POM file + Path pomFile = tempDir.resolve("pom.xml"); + Files.writeString( + pomFile, + "4.0.0" + + "testtest" + + "1.0"); + + // Create project with the POM file and a build directory + Model model = new Model(); + model.setGroupId("test"); + model.setArtifactId("test"); + model.setVersion("1.0"); + Build build = new Build(); + build.setDirectory(tempDir.resolve("target").toString()); + model.setBuild(build); + MavenProject project = new MavenProject(model); + project.setFile(pomFile.toFile()); + + // Mock session with consumer POM feature enabled (default for Maven 4) + RepositorySystemSession session = Mockito.mock(RepositorySystemSession.class); + SessionData sessionData = Mockito.mock(SessionData.class); + when(session.getData()).thenReturn(sessionData); + when(session.getConfigProperties()).thenReturn(Map.of()); + + ConsumerPomArtifactTransformer transformer = + new ConsumerPomArtifactTransformer((s, p, src) -> p.getModel().getDelegate()); + + // First invocation should attach the consumer POM + transformer.injectTransformedArtifacts(session, project); + assertEquals(1, project.getAttachedArtifacts().size()); + assertEquals("consumer", project.getAttachedArtifacts().get(0).getClassifier()); + assertEquals("pom", project.getAttachedArtifacts().get(0).getType()); + + // Second invocation should be a no-op (not duplicate the artifact) + transformer.injectTransformedArtifacts(session, project); + assertEquals(1, project.getAttachedArtifacts().size()); + } }