From bd400387cb2883fd2059699984f8daebad822c52 Mon Sep 17 00:00:00 2001 From: Andrzej Jarmoniuk Date: Fri, 6 Jan 2023 18:28:18 +0100 Subject: [PATCH] Resolves #899: Corrected the invocation of Resolver to retrieve the timestamped snapshot version; added unit tests for LockSnapshotsMojo for the case when no timestamped versions are found --- versions-maven-plugin/pom.xml | 8 + .../invoker.properties | 1 + .../src/it/it-lock-snapshots-junit5/pom.xml | 20 ++ .../it/it-lock-snapshots-junit5/verify.groovy | 3 + .../mojo/versions/LockSnapshotsMojo.java | 95 ++++------ .../mojo/versions/LockSnapshotsMojoTest.java | 177 ++++++++++++++++++ 6 files changed, 250 insertions(+), 54 deletions(-) create mode 100644 versions-maven-plugin/src/it/it-lock-snapshots-junit5/invoker.properties create mode 100644 versions-maven-plugin/src/it/it-lock-snapshots-junit5/pom.xml create mode 100644 versions-maven-plugin/src/it/it-lock-snapshots-junit5/verify.groovy create mode 100644 versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/LockSnapshotsMojoTest.java diff --git a/versions-maven-plugin/pom.xml b/versions-maven-plugin/pom.xml index c27ddd124..78bfb61f1 100644 --- a/versions-maven-plugin/pom.xml +++ b/versions-maven-plugin/pom.xml @@ -190,6 +190,14 @@ ! Not deleting as it might be helpful with assessing performance measurements. --> it-property-updates-report-002-slow/* + + it-lock-snapshots-junit/* verify diff --git a/versions-maven-plugin/src/it/it-lock-snapshots-junit5/invoker.properties b/versions-maven-plugin/src/it/it-lock-snapshots-junit5/invoker.properties new file mode 100644 index 000000000..115fc0b6c --- /dev/null +++ b/versions-maven-plugin/src/it/it-lock-snapshots-junit5/invoker.properties @@ -0,0 +1 @@ +invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:lock-snapshots diff --git a/versions-maven-plugin/src/it/it-lock-snapshots-junit5/pom.xml b/versions-maven-plugin/src/it/it-lock-snapshots-junit5/pom.xml new file mode 100644 index 000000000..14d1c58fc --- /dev/null +++ b/versions-maven-plugin/src/it/it-lock-snapshots-junit5/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + localhost + it-lock-snapshots-001 + 1.0 + pom + + + + + junit + junit + 5.0-SNAPSHOT + pom + + + + diff --git a/versions-maven-plugin/src/it/it-lock-snapshots-junit5/verify.groovy b/versions-maven-plugin/src/it/it-lock-snapshots-junit5/verify.groovy new file mode 100644 index 000000000..ee5b13808 --- /dev/null +++ b/versions-maven-plugin/src/it/it-lock-snapshots-junit5/verify.groovy @@ -0,0 +1,3 @@ +def project = new XmlSlurper().parse( new File( basedir, 'pom.xml' ) ) + +assert !( project.dependencies.dependency.version =~ /-SNAPSHOT/ ) \ No newline at end of file diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/LockSnapshotsMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/LockSnapshotsMojo.java index 4cb287f3a..d790340a2 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/LockSnapshotsMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/LockSnapshotsMojo.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.util.Collection; import java.util.Map; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -39,9 +40,12 @@ import org.apache.maven.repository.RepositorySystem; import org.apache.maven.wagon.Wagon; import org.codehaus.mojo.versions.api.PomHelper; +import org.codehaus.mojo.versions.api.VersionsHelper; import org.codehaus.mojo.versions.api.recording.ChangeRecorder; import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader; -import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.VersionRequest; +import org.eclipse.aether.resolution.VersionResolutionException; +import org.eclipse.aether.resolution.VersionResult; /** * Attempts to resolve unlocked snapshot dependency versions to the locked timestamp versions used in the build. For @@ -96,13 +100,13 @@ protected void update(ModifiedPomXMLEventReader pom) if (getProject().getParent() != null && isProcessingParent()) { lockParentSnapshot(pom, getProject().getParent()); } - } catch (IOException e) { + } catch (IOException | VersionResolutionException e) { throw new MojoExecutionException(e.getMessage(), e); } } - private void lockSnapshots(ModifiedPomXMLEventReader pom, Collection dependencies) - throws XMLStreamException, MojoExecutionException { + protected void lockSnapshots(ModifiedPomXMLEventReader pom, Collection dependencies) + throws XMLStreamException, MojoExecutionException, VersionResolutionException { for (Dependency dep : dependencies) { if (isExcludeReactor() && isProducedByReactor(dep)) { getLog().info("Ignoring reactor dependency: " + toString(dep)); @@ -121,24 +125,26 @@ private void lockSnapshots(ModifiedPomXMLEventReader pom, Collection String version = dep.getVersion(); Matcher versionMatcher = TIMESTAMPED_SNAPSHOT_REGEX.matcher(version); if (versionMatcher.find() && versionMatcher.end() == version.length()) { - String lockedVersion = resolveSnapshotVersion(dep); - if (!version.equals(lockedVersion)) { + Optional lockedVersion = resolveSnapshotVersion(dep); + if (lockedVersion.isPresent()) { if (PomHelper.setDependencyVersion( pom, dep.getGroupId(), dep.getArtifactId(), version, - lockedVersion, + lockedVersion.get(), getProject().getModel())) { - getLog().info("Locked " + toString(dep) + " to version " + lockedVersion); + getLog().info("Locked " + toString(dep) + " to version " + lockedVersion.get()); } + } else { + getLog().info("No timestamped version for " + toString(dep) + " found."); } } } } - private void lockParentSnapshot(ModifiedPomXMLEventReader pom, MavenProject parent) - throws XMLStreamException, MojoExecutionException { + protected void lockParentSnapshot(ModifiedPomXMLEventReader pom, MavenProject parent) + throws XMLStreamException, VersionResolutionException { if (parent == null) { getLog().info("Project does not have a parent"); return; @@ -154,11 +160,13 @@ private void lockParentSnapshot(ModifiedPomXMLEventReader pom, MavenProject pare Matcher versionMatcher = TIMESTAMPED_SNAPSHOT_REGEX.matcher(parentVersion); if (versionMatcher.find() && versionMatcher.end() == parentVersion.length()) { - String lockedParentVersion = resolveSnapshotVersion(parentArtifact); - if (!parentVersion.equals(lockedParentVersion)) { - if (PomHelper.setProjectParentVersion(pom, lockedParentVersion)) { - getLog().info("Locked parent " + parentArtifact + " to version " + lockedParentVersion); + Optional lockedParentVersion = resolveSnapshotVersion(parentArtifact); + if (lockedParentVersion.isPresent()) { + if (PomHelper.setProjectParentVersion(pom, lockedParentVersion.get())) { + getLog().info("Locked parent " + parentArtifact + " to version " + lockedParentVersion.get()); } + } else { + getLog().info("No timestamped version for " + parentArtifact + " found."); } } } @@ -166,53 +174,32 @@ private void lockParentSnapshot(ModifiedPomXMLEventReader pom, MavenProject pare /** * Determine the timestamp version of the snapshot artifact used in the build. * - * @param artifact - * @return The timestamp version if exists, otherwise the original snapshot artifact version is returned. + * @param artifact artifact for which to retrieve the locked version + * @return The timestamp version if exists, otherwise {@link Optional#empty()} + * @throws VersionResolutionException thrown if version resolution fails */ - private String resolveSnapshotVersion(Artifact artifact) { + private Optional resolveSnapshotVersion(Artifact artifact) throws VersionResolutionException { getLog().debug("Resolving snapshot version for artifact: " + artifact); - - String lockedVersion = artifact.getVersion(); - - try { - aetherRepositorySystem.resolveArtifact( - session.getRepositorySession(), - new ArtifactRequest( - RepositoryUtils.toArtifact(artifact), - getProject().getRemoteProjectRepositories(), - getClass().getName())); - - lockedVersion = artifact.getVersion(); - } catch (Exception e) { - getLog().error(e); - } - return lockedVersion; + VersionResult versionResult = aetherRepositorySystem.resolveVersion( + session.getRepositorySession(), + new VersionRequest( + RepositoryUtils.toArtifact(artifact), + getProject().getRemoteProjectRepositories(), + getClass().getSimpleName())); + return Optional.ofNullable(versionResult.getVersion()) + .filter(v -> !String.valueOf(artifact.getVersion()).equals(v)); } /** * Determine the timestamp version of the snapshot dependency used in the build. * - * @param dep - * @return The timestamp version if exists, otherwise the original snapshot dependency version is returned. + * @param dep dependency for which to retrieve the locked version + * @return The timestamp version if exists, otherwise {@link Optional#empty()} + * @throws MojoExecutionException thrown if retrieval of {@link VersionsHelper} fails + * @throws VersionResolutionException thrown if version resolution fails */ - private String resolveSnapshotVersion(Dependency dep) { - getLog().debug("Resolving snapshot version for dependency: " + dep); - - String lockedVersion = dep.getVersion(); - - try { - Artifact depArtifact = getHelper().createDependencyArtifact(dep); - aetherRepositorySystem.resolveArtifact( - session.getRepositorySession(), - new ArtifactRequest( - RepositoryUtils.toArtifact(depArtifact), - getProject().getRemoteProjectRepositories(), - getClass().getName())); - - lockedVersion = depArtifact.getVersion(); - } catch (Exception e) { - getLog().error(e); - } - return lockedVersion; + private Optional resolveSnapshotVersion(Dependency dep) + throws MojoExecutionException, VersionResolutionException { + return resolveSnapshotVersion(getHelper().createDependencyArtifact(dep)); } } diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/LockSnapshotsMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/LockSnapshotsMojoTest.java new file mode 100644 index 000000000..1f7f8fdd8 --- /dev/null +++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/LockSnapshotsMojoTest.java @@ -0,0 +1,177 @@ +package org.codehaus.mojo.versions; +/* + * Copyright MojoHaus and Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import javax.xml.stream.XMLStreamException; + +import java.util.List; +import java.util.function.UnaryOperator; + +import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.testing.stubs.DefaultArtifactHandlerStub; +import org.apache.maven.project.MavenProject; +import org.codehaus.mojo.versions.api.PomHelper; +import org.codehaus.mojo.versions.utils.DependencyBuilder; +import org.codehaus.mojo.versions.utils.MockUtils; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.resolution.VersionRequest; +import org.eclipse.aether.resolution.VersionResolutionException; +import org.eclipse.aether.resolution.VersionResult; +import org.junit.Test; +import org.mockito.MockedStatic; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +/** + * Unit tests for {@link LockSnapshotsMojo} + */ +public class LockSnapshotsMojoTest { + + private LockSnapshotsMojo createMojo(RepositorySystem repositorySystem) { + return new LockSnapshotsMojo(null, repositorySystem, null, null) { + { + reactorProjects = emptyList(); + project = new MavenProject(new Model() { + { + setGroupId("default-group"); + setArtifactId("default-project"); + setVersion("1.0-SNAPSHOT"); + } + + @Override + public void setDependencies(List dependencies) { + super.setDependencies(singletonList( + DependencyBuilder.dependencyWith("default-group", "default-artifact", "1.0-SNAPSHOT"))); + } + }); + session = MockUtils.mockMavenSession(); + } + }; + } + + private RepositorySystem mockRepositorySystem(UnaryOperator versionProducer) + throws VersionResolutionException { + RepositorySystem repositorySystem = mock(RepositorySystem.class); + when(repositorySystem.resolveVersion(any(), any())).then(i -> { + VersionRequest request = i.getArgument(1); + return new VersionResult(request) + .setVersion(versionProducer.apply(request.getArtifact().getVersion())); + }); + return repositorySystem; + } + + @Test + public void testNoTimestampedDependencyFoundNull() + throws XMLStreamException, MojoExecutionException, VersionResolutionException { + RepositorySystem repositorySystem = mockRepositorySystem(v -> null); + + LockSnapshotsMojo mojo = createMojo(repositorySystem); + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.setDependencyVersion(any(), any(), any(), any(), any(), any())) + .thenThrow(new RuntimeException("Not supposed to modify the dependency")); + mojo.lockSnapshots(null, mojo.project.getDependencies()); + } + } + + @Test + public void testNoTimestampedDependencyFoundSameVersion() + throws XMLStreamException, MojoExecutionException, VersionResolutionException { + RepositorySystem repositorySystem = mockRepositorySystem(UnaryOperator.identity()); + + LockSnapshotsMojo mojo = createMojo(repositorySystem); + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.setDependencyVersion(any(), any(), any(), any(), any(), any())) + .thenThrow(new RuntimeException("Not supposed to modify the dependency")); + mojo.lockSnapshots(null, mojo.project.getDependencies()); + } + } + + @Test + public void testNoTimestampedParentFoundNull() + throws XMLStreamException, MojoExecutionException, VersionResolutionException { + RepositorySystem repositorySystem = mockRepositorySystem(v -> null); + + LockSnapshotsMojo mojo = createMojo(repositorySystem); + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.setProjectParentVersion(any(), any())) + .thenThrow(new RuntimeException("Not supposed to modify the parent")); + mojo.lockParentSnapshot( + null, + new MavenProject(new Model() { + { + setGroupId("default-group"); + setArtifactId("default-parent"); + setVersion("1.0-SNAPSHOT"); + } + }) { + { + setArtifact(new DefaultArtifact( + "default-group", + "default-parent", + "1.0-SNAPSHOT", + "compile", + "pom", + null, + new DefaultArtifactHandlerStub("jar"))); + } + }); + } + } + + @Test + public void testNoTimestampedParentFoundSameVersion() + throws XMLStreamException, MojoExecutionException, VersionResolutionException { + RepositorySystem repositorySystem = mockRepositorySystem(UnaryOperator.identity()); + + LockSnapshotsMojo mojo = createMojo(repositorySystem); + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.setProjectParentVersion(any(), any())) + .thenThrow(new RuntimeException("Not supposed to modify the parent")); + mojo.lockParentSnapshot( + null, + new MavenProject(new Model() { + { + setGroupId("default-group"); + setArtifactId("default-parent"); + setVersion("1.0-SNAPSHOT"); + } + }) { + { + setArtifact(new DefaultArtifact( + "default-group", + "default-parent", + "1.0-SNAPSHOT", + "compile", + "pom", + null, + new DefaultArtifactHandlerStub("jar"))); + } + }); + } + } +}