diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseLatestReleasesMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseLatestReleasesMojo.java index 44c5d57c7a..07b39fe2ed 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseLatestReleasesMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseLatestReleasesMojo.java @@ -96,6 +96,16 @@ public class UseLatestReleasesMojo extends UseLatestVersionsMojoBase { @Parameter(property = "allowIncrementalUpdates", defaultValue = "true") protected boolean allowIncrementalUpdates = true; + /** + *

Whether to downgrade a snapshot dependency if allowSnapshots is false + * and there exists a non-snapshot version within the range fulfilling the criteria.

+ *

Only valid if allowSnapshots is false.

+ * + * @since 2.15.0 + */ + @Parameter(property = "allowDowngrade", defaultValue = "false") + protected boolean allowDowngrade; + // ------------------------------ METHODS -------------------------- @Inject @@ -148,7 +158,8 @@ private void useLatestReleases( (dep, versions) -> { try { return getLastFiltered( - versions.getNewerVersions(dep.getVersion(), unchangedSegment, false, false), dep); + versions.getNewerVersions(dep.getVersion(), unchangedSegment, false, allowDowngrade), + dep); } catch (InvalidSegmentException e) { getLog().warn(String.format( "Skipping the processing of %s:%s:%s due to: %s", @@ -157,7 +168,8 @@ private void useLatestReleases( return empty(); }, changeKind, - dep -> !SNAPSHOT_REGEX.matcher(dep.getVersion()).matches()); + dep -> allowDowngrade + || !SNAPSHOT_REGEX.matcher(dep.getVersion()).matches()); } /** diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseNextReleasesMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseNextReleasesMojo.java index a0fdfddcc9..8f614956c3 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseNextReleasesMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseNextReleasesMojo.java @@ -26,18 +26,21 @@ import java.util.Arrays; import java.util.Collection; import java.util.Map; +import java.util.Optional; import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; 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.VersionRetrievalException; import org.codehaus.mojo.versions.api.recording.ChangeRecord; import org.codehaus.mojo.versions.api.recording.ChangeRecorder; +import org.codehaus.mojo.versions.ordering.InvalidSegmentException; import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader; import static java.util.Collections.singletonList; @@ -51,7 +54,15 @@ @Mojo(name = "use-next-releases", threadSafe = true) public class UseNextReleasesMojo extends UseLatestVersionsMojoBase { - // ------------------------------ METHODS -------------------------- + /** + *

Whether to downgrade a snapshot dependency if allowSnapshots is false + * and there exists a non-snapshot version within the range fulfilling the criteria.

+ *

Only valid if allowSnapshots is false.

+ * + * @since 2.15.0 + */ + @Parameter(property = "allowDowngrade", defaultValue = "false") + protected boolean allowDowngrade; @Inject public UseNextReleasesMojo( @@ -99,9 +110,17 @@ private void useNextReleases( useLatestVersions( pom, dependencies, - (dep, versions) -> Arrays.stream(versions.getNewerVersions(dep.getVersion(), false)) - .findFirst(), + (dep, versions) -> { + try { + return Arrays.stream(versions.getNewerVersions( + dep.getVersion(), Optional.empty(), false, allowDowngrade)) + .findFirst(); + } catch (InvalidSegmentException e) { + throw new RuntimeException(e); + } + }, changeKind, - dep -> !SNAPSHOT_REGEX.matcher(dep.getVersion()).matches()); + dep -> allowDowngrade + || !SNAPSHOT_REGEX.matcher(dep.getVersion()).matches()); } } diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseNextVersionsMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseNextVersionsMojo.java index b263c0887c..319005a68e 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseNextVersionsMojo.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseNextVersionsMojo.java @@ -26,18 +26,21 @@ import java.util.Arrays; import java.util.Collection; import java.util.Map; +import java.util.Optional; import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; 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.VersionRetrievalException; import org.codehaus.mojo.versions.api.recording.ChangeRecord; import org.codehaus.mojo.versions.api.recording.ChangeRecorder; +import org.codehaus.mojo.versions.ordering.InvalidSegmentException; import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader; import static java.util.Collections.singletonList; @@ -51,6 +54,16 @@ @Mojo(name = "use-next-versions", threadSafe = true) public class UseNextVersionsMojo extends UseLatestVersionsMojoBase { + /** + *

Whether to downgrade a snapshot dependency if allowSnapshots is false + * and there exists a non-snapshot version within the range fulfilling the criteria.

+ *

Only valid if allowSnapshots is false.

+ * + * @since 2.15.0 + */ + @Parameter(property = "allowDowngrade", defaultValue = "false") + protected boolean allowDowngrade; + // ------------------------------ METHODS -------------------------- @Inject @@ -97,8 +110,15 @@ private void useNextVersions( useLatestVersions( pom, dependencies, - (dep, versions) -> Arrays.stream(versions.getNewerVersions(dep.getVersion(), allowSnapshots)) - .findFirst(), + (dep, versions) -> { + try { + return Arrays.stream(versions.getNewerVersions( + dep.getVersion(), Optional.empty(), allowSnapshots, allowDowngrade)) + .findFirst(); + } catch (InvalidSegmentException e) { + throw new RuntimeException(e); + } + }, changeKind); } } diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseLatestReleasesMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseLatestReleasesMojoTest.java index 2760d8807b..c8777c2b36 100644 --- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseLatestReleasesMojoTest.java +++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseLatestReleasesMojoTest.java @@ -11,6 +11,7 @@ import org.apache.maven.repository.RepositorySystem; import org.codehaus.mojo.versions.api.PomHelper; import org.codehaus.mojo.versions.api.VersionRetrievalException; +import org.codehaus.mojo.versions.change.DefaultVersionChange; import org.codehaus.mojo.versions.utils.DependencyBuilder; import org.codehaus.mojo.versions.utils.TestChangeRecorder; import org.hamcrest.Matchers; @@ -26,7 +27,10 @@ import static org.codehaus.mojo.versions.utils.MockUtils.mockMavenSession; import static org.codehaus.mojo.versions.utils.MockUtils.mockRepositorySystem; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasItem; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mockStatic; public class UseLatestReleasesMojoTest { @@ -98,4 +102,65 @@ public void testDontUpgradeToBeta() } assertThat(changeRecorder.getChanges(), Matchers.empty()); } + + @Test + public void testAllowDowngrade() + throws MojoExecutionException, XMLStreamException, MojoFailureException, VersionRetrievalException { + mojo.aetherRepositorySystem = mockAetherRepositorySystem(new HashMap() { + { + put("artifactA", new String[] {"1.0.0", "1.0.1-SNAPSHOT"}); + } + }); + mojo.getProject() + .setDependencies(singletonList(DependencyBuilder.newBuilder() + .withGroupId("default-group") + .withArtifactId("artifactA") + .withVersion("1.0.1-SNAPSHOT") + .build())); + mojo.allowDowngrade = true; + + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.setDependencyVersion( + any(), anyString(), anyString(), anyString(), anyString(), any(Model.class))) + .thenReturn(true); + pomHelper + .when(() -> PomHelper.getRawModel(any(MavenProject.class))) + .thenReturn(mojo.getProject().getModel()); + mojo.update(null); + } + assertThat( + changeRecorder.getChanges(), + hasItem(new DefaultVersionChange( + "default-group", "artifactA", + "1.0.1-SNAPSHOT", "1.0.0"))); + } + + @Test + public void testDisallowDowngrade() + throws MojoExecutionException, XMLStreamException, MojoFailureException, VersionRetrievalException { + mojo.aetherRepositorySystem = mockAetherRepositorySystem(new HashMap() { + { + put("artifactA", new String[] {"1.0.0", "1.0.1-SNAPSHOT"}); + } + }); + mojo.getProject() + .setDependencies(singletonList(DependencyBuilder.newBuilder() + .withGroupId("default-group") + .withArtifactId("artifactA") + .withVersion("1.0.1-SNAPSHOT") + .build())); + + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.setDependencyVersion( + any(), anyString(), anyString(), anyString(), anyString(), any(Model.class))) + .thenReturn(true); + pomHelper + .when(() -> PomHelper.getRawModel(any(MavenProject.class))) + .thenReturn(mojo.getProject().getModel()); + mojo.update(null); + } + assertThat(changeRecorder.getChanges(), empty()); + } } diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseNextReleasesMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseNextReleasesMojoTest.java index 3a8329f343..8ff57dd70f 100644 --- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseNextReleasesMojoTest.java +++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseNextReleasesMojoTest.java @@ -18,13 +18,18 @@ * under the License. */ +import javax.xml.stream.XMLStreamException; + import java.util.Collections; import java.util.HashMap; import org.apache.maven.model.Model; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import org.apache.maven.repository.RepositorySystem; import org.codehaus.mojo.versions.api.PomHelper; +import org.codehaus.mojo.versions.api.VersionRetrievalException; import org.codehaus.mojo.versions.change.DefaultVersionChange; import org.codehaus.mojo.versions.utils.DependencyBuilder; import org.codehaus.mojo.versions.utils.TestChangeRecorder; @@ -34,14 +39,17 @@ import org.mockito.MockedStatic; import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE; import static org.apache.maven.plugin.testing.ArtifactStubFactory.setVariableValueToObject; import static org.codehaus.mojo.versions.utils.MockUtils.mockAetherRepositorySystem; import static org.codehaus.mojo.versions.utils.MockUtils.mockMavenSession; import static org.codehaus.mojo.versions.utils.MockUtils.mockRepositorySystem; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasItem; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mockStatic; /** @@ -123,4 +131,65 @@ public void testFindANewerRelease() throws IllegalAccessException { changeRecorder.getChanges(), hasItem(new DefaultVersionChange("default-group", "dependency-artifact", "1.1.0", "1.1.1"))); } + + @Test + public void testAllowDowngrade() + throws MojoExecutionException, XMLStreamException, MojoFailureException, VersionRetrievalException { + mojo.aetherRepositorySystem = mockAetherRepositorySystem(new HashMap() { + { + put("artifactA", new String[] {"1.0.0", "1.0.1-SNAPSHOT"}); + } + }); + mojo.getProject() + .setDependencies(singletonList(DependencyBuilder.newBuilder() + .withGroupId("default-group") + .withArtifactId("artifactA") + .withVersion("1.0.1-SNAPSHOT") + .build())); + mojo.allowDowngrade = true; + + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.setDependencyVersion( + any(), anyString(), anyString(), anyString(), anyString(), any(Model.class))) + .thenReturn(true); + pomHelper + .when(() -> PomHelper.getRawModel(any(MavenProject.class))) + .thenReturn(mojo.getProject().getModel()); + mojo.update(null); + } + assertThat( + changeRecorder.getChanges(), + hasItem(new DefaultVersionChange( + "default-group", "artifactA", + "1.0.1-SNAPSHOT", "1.0.0"))); + } + + @Test + public void testDisallowDowngrade() + throws MojoExecutionException, XMLStreamException, MojoFailureException, VersionRetrievalException { + mojo.aetherRepositorySystem = mockAetherRepositorySystem(new HashMap() { + { + put("artifactA", new String[] {"1.0.0", "1.0.1-SNAPSHOT"}); + } + }); + mojo.getProject() + .setDependencies(singletonList(DependencyBuilder.newBuilder() + .withGroupId("default-group") + .withArtifactId("artifactA") + .withVersion("1.0.1-SNAPSHOT") + .build())); + + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.setDependencyVersion( + any(), anyString(), anyString(), anyString(), anyString(), any(Model.class))) + .thenReturn(true); + pomHelper + .when(() -> PomHelper.getRawModel(any(MavenProject.class))) + .thenReturn(mojo.getProject().getModel()); + mojo.update(null); + } + assertThat(changeRecorder.getChanges(), empty()); + } } diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseNextVersionsMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseNextVersionsMojoTest.java index 0dddd3f4b5..7ab0d45cbb 100644 --- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseNextVersionsMojoTest.java +++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseNextVersionsMojoTest.java @@ -18,13 +18,18 @@ * under the License. */ +import javax.xml.stream.XMLStreamException; + import java.util.Collections; import java.util.HashMap; import org.apache.maven.model.Model; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import org.apache.maven.repository.RepositorySystem; import org.codehaus.mojo.versions.api.PomHelper; +import org.codehaus.mojo.versions.api.VersionRetrievalException; import org.codehaus.mojo.versions.change.DefaultVersionChange; import org.codehaus.mojo.versions.utils.DependencyBuilder; import org.codehaus.mojo.versions.utils.TestChangeRecorder; @@ -34,14 +39,17 @@ import org.mockito.MockedStatic; import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE; import static org.apache.maven.plugin.testing.ArtifactStubFactory.setVariableValueToObject; import static org.codehaus.mojo.versions.utils.MockUtils.mockAetherRepositorySystem; import static org.codehaus.mojo.versions.utils.MockUtils.mockMavenSession; import static org.codehaus.mojo.versions.utils.MockUtils.mockRepositorySystem; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasItem; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mockStatic; /** @@ -129,4 +137,65 @@ public void testFindANewerVersion() throws IllegalAccessException { changeRecorder.getChanges(), hasItem(new DefaultVersionChange("default-group", "dependency-artifact", "1.1.0-SNAPSHOT", "1.1.1"))); } + + @Test + public void testAllowDowngrade() + throws MojoExecutionException, XMLStreamException, MojoFailureException, VersionRetrievalException { + mojo.aetherRepositorySystem = mockAetherRepositorySystem(new HashMap() { + { + put("artifactA", new String[] {"1.0.0", "1.0.1-SNAPSHOT"}); + } + }); + mojo.getProject() + .setDependencies(singletonList(DependencyBuilder.newBuilder() + .withGroupId("default-group") + .withArtifactId("artifactA") + .withVersion("1.0.1-SNAPSHOT") + .build())); + mojo.allowDowngrade = true; + + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.setDependencyVersion( + any(), anyString(), anyString(), anyString(), anyString(), any(Model.class))) + .thenReturn(true); + pomHelper + .when(() -> PomHelper.getRawModel(any(MavenProject.class))) + .thenReturn(mojo.getProject().getModel()); + mojo.update(null); + } + assertThat( + changeRecorder.getChanges(), + hasItem(new DefaultVersionChange( + "default-group", "artifactA", + "1.0.1-SNAPSHOT", "1.0.0"))); + } + + @Test + public void testDisallowDowngrade() + throws MojoExecutionException, XMLStreamException, MojoFailureException, VersionRetrievalException { + mojo.aetherRepositorySystem = mockAetherRepositorySystem(new HashMap() { + { + put("artifactA", new String[] {"1.0.0", "1.0.1-SNAPSHOT"}); + } + }); + mojo.getProject() + .setDependencies(singletonList(DependencyBuilder.newBuilder() + .withGroupId("default-group") + .withArtifactId("artifactA") + .withVersion("1.0.1-SNAPSHOT") + .build())); + + try (MockedStatic pomHelper = mockStatic(PomHelper.class)) { + pomHelper + .when(() -> PomHelper.setDependencyVersion( + any(), anyString(), anyString(), anyString(), anyString(), any(Model.class))) + .thenReturn(true); + pomHelper + .when(() -> PomHelper.getRawModel(any(MavenProject.class))) + .thenReturn(mojo.getProject().getModel()); + mojo.update(null); + } + assertThat(changeRecorder.getChanges(), empty()); + } }