diff --git a/pom.xml b/pom.xml
index 620ed09ab0..5afea400aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -256,7 +256,7 @@
org.mockito
- mockito-core
+ mockito-inline
4.7.0
test
diff --git a/src/main/java/org/codehaus/mojo/versions/UseLatestVersionsMojo.java b/src/main/java/org/codehaus/mojo/versions/UseLatestVersionsMojo.java
index 60301607a0..cbfb7adf9a 100644
--- a/src/main/java/org/codehaus/mojo/versions/UseLatestVersionsMojo.java
+++ b/src/main/java/org/codehaus/mojo/versions/UseLatestVersionsMojo.java
@@ -22,9 +22,8 @@
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.List;
+import java.util.Collections;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
@@ -75,8 +74,33 @@ public class UseLatestVersionsMojo
@Parameter( property = "allowIncrementalUpdates", defaultValue = "true" )
private boolean allowIncrementalUpdates;
+ /**
+ *
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.12.0
+ */
+ @Parameter( property = "allowDowngrade",
+ defaultValue = "false" )
+ private boolean allowDowngrade;
+
// ------------------------------ METHODS --------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException
+ {
+ if ( allowDowngrade && allowSnapshots )
+ {
+ throw new MojoExecutionException( "allowDowngrade is only valid with allowSnapshots equal to false" );
+ }
+ super.execute();
+ }
+
/**
* @param pom the pom to update.
* @throws org.apache.maven.plugin.MojoExecutionException when things go wrong
@@ -109,9 +133,7 @@ protected void update( ModifiedPomXMLEventReader pom )
dependency.setGroupId( getProject().getParent().getGroupId() );
dependency.setVersion( getProject().getParent().getVersion() );
dependency.setType( "pom" );
- List list = new ArrayList();
- list.add( dependency );
- useLatestVersions( pom, list );
+ useLatestVersions( pom, Collections.singletonList( dependency ) );
}
}
catch ( ArtifactMetadataRetrievalException | IOException e )
@@ -154,7 +176,8 @@ private void useLatestVersions( ModifiedPomXMLEventReader pom, Collection 0 )
diff --git a/src/main/java/org/codehaus/mojo/versions/api/AbstractVersionDetails.java b/src/main/java/org/codehaus/mojo/versions/api/AbstractVersionDetails.java
index a06ba2c4fb..ee2a954df0 100644
--- a/src/main/java/org/codehaus/mojo/versions/api/AbstractVersionDetails.java
+++ b/src/main/java/org/codehaus/mojo/versions/api/AbstractVersionDetails.java
@@ -26,6 +26,7 @@
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
+import org.codehaus.mojo.versions.ordering.InvalidSegmentException;
import org.codehaus.mojo.versions.ordering.VersionComparator;
/**
@@ -135,30 +136,6 @@ public final ArtifactVersion[] getVersions( ArtifactVersion currentVersion, Arti
return getVersions( currentVersion, upperBound, includeSnapshots, false, false );
}
- /**
- * Gets newer versions of the specified artifact version.
- *
- * @param version The current version of the artifact.
- * @param upperBoundFixedSegment Indicates the segment in the version number that cannot be changed. For example, a
- * value of 0 indicates that the major version number cannot be changed. A value of -1 indicates any
- * segment value can be changed.
- * @param includeSnapshots Whether to include snapshot versions.
- * @return Returns the newer artifact versions.
- */
- private ArtifactVersion[] getNewerVersions( ArtifactVersion version, int upperBoundFixedSegment,
- boolean includeSnapshots )
- {
- ArtifactVersion lowerBound = version;
- ArtifactVersion upperBound = null;
-
- if ( upperBoundFixedSegment != -1 )
- {
- upperBound = getVersionComparator().incrementSegment( lowerBound, upperBoundFixedSegment );
- }
-
- return getVersions( version, upperBound, includeSnapshots, false, false );
- }
-
private ArtifactVersion[] getNewerVersions( ArtifactVersion version, boolean includeSnapshots )
{
return getVersions( version, null, includeSnapshots, false, true );
@@ -243,9 +220,43 @@ public final ArtifactVersion[] getNewerVersions( String version, boolean include
return getNewerVersions( new DefaultArtifactVersion( version ), includeSnapshots );
}
+ /**
+ * Returns an array of newer versions than the given version, given the upper bound segment and whether snapshots
+ * should be included.
+ *
+ * @param version current version
+ * @param upperBoundSegment the upper bound segment
+ * @param includeSnapshots whether snapshot versions should be included
+ * @deprecated please use {@link AbstractVersionDetails#getNewerVersions(String, int, boolean, boolean)} instead
+ * @return array of newer versions fulfilling the criteria
+ */
+ @Deprecated
public final ArtifactVersion[] getNewerVersions( String version, int upperBoundSegment, boolean includeSnapshots )
{
- return getNewerVersions( new DefaultArtifactVersion( version ), upperBoundSegment, includeSnapshots );
+ return getNewerVersions( version, upperBoundSegment, includeSnapshots, false );
+ }
+
+ /**
+ * Returns an array of newer versions than the given version, given the upper bound segment and whether snapshots
+ * should be included.
+ *
+ * @param versionString current version
+ * @param upperBoundSegment the upper bound segment
+ * @param includeSnapshots whether snapshot versions should be included
+ * @param allowDowngrade whether to allow downgrading if the current version is a snapshots and snapshots
+ * are disallowed
+ * @return array of newer versions fulfilling the criteria
+ */
+ public final ArtifactVersion[] getNewerVersions( String versionString, int upperBoundSegment,
+ boolean includeSnapshots, boolean allowDowngrade )
+ {
+ ArtifactVersion currentVersion = new DefaultArtifactVersion( versionString );
+ ArtifactVersion lowerBound =
+ allowDowngrade ? getLowerBoundArtifactVersion( currentVersion, upperBoundSegment ) : currentVersion;
+ ArtifactVersion upperBound = upperBoundSegment == -1 ? null
+ : getVersionComparator().incrementSegment( lowerBound, upperBoundSegment );
+
+ return getVersions( lowerBound, upperBound, includeSnapshots, allowDowngrade, allowDowngrade );
}
public final ArtifactVersion getOldestVersion( ArtifactVersion lowerBound, ArtifactVersion upperBound )
@@ -483,4 +494,52 @@ public ArtifactVersion[] getAllUpdates( VersionRange versionRange, boolean inclu
{
return getVersions( versionRange, getCurrentVersion(), null, includeSnapshots, false, true );
}
+
+ protected ArtifactVersion getLowerBoundArtifactVersion( ArtifactVersion version, int segment )
+ {
+ String lowerBound = getLowerBound( version, segment );
+ return lowerBound != null ? new DefaultArtifactVersion( lowerBound ) : null;
+ }
+
+ protected String getLowerBound( ArtifactVersion version, int segment )
+ {
+ if ( segment < 0 )
+ {
+ return null;
+ }
+
+ int segmentCount = getVersionComparator().getSegmentCount( version );
+ if ( segment > segmentCount )
+ {
+ throw new InvalidSegmentException( segment, segmentCount,
+ version.toString() );
+ }
+
+ StringBuilder newVersion = new StringBuilder();
+ newVersion.append( version.getMajorVersion() );
+ if ( segmentCount > 0 )
+ {
+ newVersion.append( "." )
+ .append( segment >= 1 ? version.getMinorVersion() : 0 );
+ }
+ if ( segmentCount > 1 )
+ {
+ newVersion.append( "." )
+ .append( segment >= 2 ? version.getIncrementalVersion() : 0 );
+ }
+ if ( segmentCount > 2 )
+ {
+ if ( version.getQualifier() != null )
+ {
+ newVersion.append( "-" )
+ .append( segment >= 3 ? version.getQualifier() : "0" );
+ }
+ else
+ {
+ newVersion.append( "-" )
+ .append( segment >= 3 ? version.getBuildNumber() : "0" );
+ }
+ }
+ return newVersion.toString();
+ }
}
diff --git a/src/main/java/org/codehaus/mojo/versions/api/PropertyVersions.java b/src/main/java/org/codehaus/mojo/versions/api/PropertyVersions.java
index eb363589ae..4ab81204b5 100644
--- a/src/main/java/org/codehaus/mojo/versions/api/PropertyVersions.java
+++ b/src/main/java/org/codehaus/mojo/versions/api/PropertyVersions.java
@@ -23,7 +23,6 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -39,7 +38,6 @@
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.plugin.MojoExecutionException;
import org.codehaus.mojo.versions.Property;
-import org.codehaus.mojo.versions.ordering.InvalidSegmentException;
import org.codehaus.mojo.versions.ordering.VersionComparator;
/**
@@ -145,12 +143,8 @@ public ArtifactAssociation[] getAssociations()
private VersionComparator[] lookupComparators()
{
- Set result = new HashSet();
- for ( ArtifactAssociation association : associations )
- {
- result.add( helper.getVersionComparator( association.getArtifact() ) );
- }
- return result.toArray( new VersionComparator[0] );
+ return associations.stream().map( association -> helper.getVersionComparator( association.getArtifact() ) )
+ .distinct().toArray( VersionComparator[]::new );
}
/**
@@ -346,19 +340,15 @@ public ArtifactVersion getNewestVersion( String currentVersion, Property propert
throw new MojoExecutionException( e.getMessage(), e );
}
- ArtifactVersion lowerBoundArtifactVersion = null;
+ ArtifactVersion lowerBoundArtifactVersion = helper.createArtifactVersion( currentVersion );
if ( allowDowngrade )
{
- if ( segment != -1 )
- {
- lowerBoundArtifactVersion = getLowerBound( helper, currentVersion, segment );
- }
- helper.getLog().debug( "lowerBoundArtifactVersion is null based on allowDowngrade:" + allowDowngrade );
+ String updatedVersion = getLowerBound( lowerBoundArtifactVersion, segment );
+ lowerBoundArtifactVersion = updatedVersion != null ? helper.createArtifactVersion( updatedVersion ) : null;
}
- else
+ if ( helper.getLog().isDebugEnabled() )
{
- lowerBoundArtifactVersion = helper.createArtifactVersion( currentVersion );
- helper.getLog().debug( "lowerBoundArtifactVersion: " + lowerBoundArtifactVersion.toString() );
+ helper.getLog().debug( "lowerBoundArtifactVersion: " + lowerBoundArtifactVersion );
}
ArtifactVersion upperBound = null;
@@ -375,7 +365,7 @@ public ArtifactVersion getNewestVersion( String currentVersion, Property propert
if ( property.isSearchReactor() )
{
helper.getLog().debug( "Property ${" + property.getName() + "}: Searching reactor for a valid version..." );
- Collection reactorArtifacts = helper.extractArtifacts( reactorProjects );
+ Set reactorArtifacts = helper.extractArtifacts( reactorProjects );
ArtifactVersion[] reactorVersions = getVersions( reactorArtifacts );
helper.getLog().debug( "Property ${" + property.getName()
+ "}: Set of valid available versions from the reactor is " + Arrays.asList(
@@ -527,45 +517,4 @@ public ArtifactVersion incrementSegment( ArtifactVersion v, int segment )
}
}
-
-
- private ArtifactVersion getLowerBound( VersionsHelper helper,
- String currentVersion, int segment )
- {
- ArtifactVersion version = helper.createArtifactVersion( currentVersion );
- int segmentCount = getVersionComparator().getSegmentCount( version );
- if ( segment < 0 || segment > segmentCount )
- {
- throw new InvalidSegmentException( segment, segmentCount,
- currentVersion );
- }
-
- StringBuilder newVersion = new StringBuilder();
- newVersion.append( segment >= 0 ? version.getMajorVersion() : 0 );
- if ( segmentCount > 0 )
- {
- newVersion.append( "." )
- .append( segment >= 1 ? version.getMinorVersion() : 0 );
- }
- if ( segmentCount > 1 )
- {
- newVersion.append( "." )
- .append( segment >= 2 ? version.getIncrementalVersion() : 0 );
- }
- if ( segmentCount > 2 )
- {
- if ( version.getQualifier() != null )
- {
- newVersion.append( "-" )
- .append( segment >= 3 ? version.getQualifier() : "0" );
- }
- else
- {
- newVersion.append( "-" )
- .append( segment >= 3 ? version.getBuildNumber() : "0" );
- }
- }
- return helper.createArtifactVersion( newVersion.toString() );
- }
-
}
diff --git a/src/test/java/org/codehaus/mojo/versions/UseLatestVersionsMojoTest.java b/src/test/java/org/codehaus/mojo/versions/UseLatestVersionsMojoTest.java
new file mode 100644
index 0000000000..4d64826b32
--- /dev/null
+++ b/src/test/java/org/codehaus/mojo/versions/UseLatestVersionsMojoTest.java
@@ -0,0 +1,244 @@
+package org.codehaus.mojo.versions;
+
+import javax.xml.stream.XMLStreamException;
+
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.DependencyManagement;
+import org.apache.maven.model.Model;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.testing.AbstractMojoTestCase;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.repository.RepositorySystem;
+import org.codehaus.mojo.versions.api.PomHelper;
+import org.codehaus.mojo.versions.change.VersionChange;
+import org.codehaus.mojo.versions.recording.ChangeRecorder;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockedStatic;
+
+import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasItem;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+@SuppressWarnings( "deprecation" )
+public class UseLatestVersionsMojoTest extends AbstractMojoTestCase
+{
+ private UseLatestVersionsMojo mojo;
+ private TestChangeRecorder changeRecorder;
+
+ @Before
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ RepositorySystem repositorySystemMock = mock( RepositorySystem.class );
+ when( repositorySystemMock.createDependencyArtifact( any( Dependency.class ) ) ).thenAnswer( invocation ->
+ {
+ Dependency dependency = invocation.getArgument( 0 );
+ return new DefaultArtifact( dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(),
+ dependency.getScope(), dependency.getType(), dependency.getClassifier() != null
+ ? dependency.getClassifier() : "default", null );
+ } );
+
+ ArtifactMetadataSource artifactMetadataSourceMock = mock( ArtifactMetadataSource.class );
+ when( artifactMetadataSourceMock.retrieveAvailableVersions( any( Artifact.class ), any(), any() ) ).then(
+ invocation ->
+ {
+ Artifact artifact = invocation.getArgument( 0 );
+ if ( "dependency-artifact".equals( artifact.getArtifactId() ) )
+ {
+ return Arrays.asList( new DefaultArtifactVersion( "1.1.1-SNAPSHOT" ),
+ new DefaultArtifactVersion( "1.1.0" ), new DefaultArtifactVersion( "1.1.0-SNAPSHOT" ),
+ new DefaultArtifactVersion( "1.0.0" ), new DefaultArtifactVersion( "1.0.0-SNAPSHOT" ),
+ new DefaultArtifactVersion( "0.9.0" ) );
+ }
+ fail();
+ return null;
+ } );
+
+ changeRecorder = new TestChangeRecorder();
+
+ mojo = new UseLatestVersionsMojo()
+ {{
+ MavenProject project = new MavenProject()
+ {{
+ setModel( new Model()
+ {{
+ setGroupId( "default-group" );
+ setArtifactId( "project-artifact" );
+ setVersion( "1.0.0-SNAPSHOT" );
+
+ setDependencies( Collections.singletonList(
+ DependencyBuilder.dependencyWith( "default-group", "dependency-artifact", "1.1.1-SNAPSHOT",
+ "default", "pom", SCOPE_COMPILE ) ) );
+
+ setDependencyManagement( new DependencyManagement() );
+ getDependencyManagement().setDependencies( Collections.singletonList(
+ DependencyBuilder.dependencyWith( "default-group", "dependency-artifact", "1.1.1-SNAPSHOT",
+ "default", "pom", SCOPE_COMPILE ) ) );
+ }} );
+ }};
+ setProject( project );
+ repositorySystem = repositorySystemMock;
+ artifactMetadataSource = artifactMetadataSourceMock;
+ setVariableValueToObject( this, "changeRecorder", changeRecorder );
+ }};
+ }
+
+ private static class TestChangeRecorder implements ChangeRecorder
+ {
+ private final List changes = new LinkedList<>();
+
+ @Override
+ public void recordUpdate( String kind, String groupId, String artifactId, String oldVersion, String newVersion )
+ {
+ changes.add( new VersionChange( groupId, artifactId, oldVersion, newVersion ) );
+ }
+
+ @Override
+ public void serialize( OutputStream outputStream )
+ {
+ }
+
+ public List getChanges()
+ {
+ return changes;
+ }
+ }
+
+ @Test
+ public void testDependenciesDowngradeIncremental()
+ throws MojoExecutionException, XMLStreamException, MojoFailureException, IllegalAccessException
+ {
+ setVariableValueToObject( mojo, "processDependencies", true );
+ setVariableValueToObject( mojo, "allowSnapshots", false );
+ setVariableValueToObject( mojo, "allowMajorUpdates", false );
+ setVariableValueToObject( mojo, "allowMinorUpdates", true );
+ setVariableValueToObject( mojo, "allowIncrementalUpdates", true );
+ setVariableValueToObject( mojo, "allowDowngrade", true );
+
+ try ( MockedStatic pomHelper = mockStatic( PomHelper.class ) )
+ {
+ pomHelper.when( () -> PomHelper.setDependencyVersion( any(), any(), any(), any(), any(), any() ) )
+ .thenReturn( true );
+ mojo.update( null );
+ }
+ assertThat( changeRecorder.getChanges(),
+ hasItem( new VersionChange( "default-group", "dependency-artifact", "1.1.1-SNAPSHOT",
+ "1.1.0" ) ) );
+ }
+
+ @Test
+ public void testDependenciesDowngradeMinor()
+ throws MojoExecutionException, XMLStreamException, MojoFailureException, IllegalAccessException
+ {
+ setVariableValueToObject( mojo, "processDependencies", true );
+ setVariableValueToObject( mojo, "allowSnapshots", false );
+ setVariableValueToObject( mojo, "allowMajorUpdates", false );
+ setVariableValueToObject( mojo, "allowMinorUpdates", true );
+ setVariableValueToObject( mojo, "allowIncrementalUpdates", false );
+ setVariableValueToObject( mojo, "allowDowngrade", true );
+
+ try ( MockedStatic pomHelper = mockStatic( PomHelper.class ) )
+ {
+ pomHelper.when( () -> PomHelper.setDependencyVersion( any(), any(), any(), any(), any(), any() ) )
+ .thenReturn( true );
+ mojo.update( null );
+ }
+ assertThat( changeRecorder.getChanges(),
+ hasItem( new VersionChange( "default-group", "dependency-artifact", "1.1.1-SNAPSHOT",
+ "1.0.0" ) ) );
+ }
+
+ @Test
+ public void testDependenciesDowngradeMajor()
+ throws MojoExecutionException, XMLStreamException, MojoFailureException, IllegalAccessException
+ {
+ setVariableValueToObject( mojo, "processDependencies", true );
+ setVariableValueToObject( mojo, "allowSnapshots", false );
+ setVariableValueToObject( mojo, "allowMajorUpdates", true );
+ setVariableValueToObject( mojo, "allowMinorUpdates", false );
+ setVariableValueToObject( mojo, "allowIncrementalUpdates", false );
+ setVariableValueToObject( mojo, "allowDowngrade", true );
+
+ try ( MockedStatic pomHelper = mockStatic( PomHelper.class ) )
+ {
+ pomHelper.when( () -> PomHelper.setDependencyVersion( any(), any(), any(), any(), any(), any() ) )
+ .thenReturn( true );
+ mojo.update( null );
+ }
+ assertThat( changeRecorder.getChanges(),
+ hasItem( new VersionChange( "default-group", "dependency-artifact", "1.1.1-SNAPSHOT",
+ "0.9.0" ) ) );
+ }
+
+ @Test
+ public void testDependencyManagementDowngrade()
+ throws MojoExecutionException, XMLStreamException, MojoFailureException, IllegalAccessException
+ {
+ setVariableValueToObject( mojo, "processDependencyManagement", true );
+ setVariableValueToObject( mojo, "allowSnapshots", false );
+ setVariableValueToObject( mojo, "allowMajorUpdates", false );
+ setVariableValueToObject( mojo, "allowMinorUpdates", true );
+ setVariableValueToObject( mojo, "allowIncrementalUpdates", true );
+ setVariableValueToObject( mojo, "allowDowngrade", true );
+
+ try ( MockedStatic pomHelper = mockStatic( PomHelper.class ) )
+ {
+ pomHelper.when( () -> PomHelper.getRawModel( any( MavenProject.class ) ) )
+ .thenReturn( mojo.getProject().getModel() );
+ pomHelper.when( () -> PomHelper.setDependencyVersion( any(), any(), any(), any(), any(), any() ) )
+ .thenReturn( true );
+ mojo.update( null );
+ }
+ assertThat( changeRecorder.getChanges(),
+ hasItem( new VersionChange( "default-group", "dependency-artifact", "1.1.1-SNAPSHOT",
+ "1.1.0" ) ) );
+ }
+
+ @Test
+ public void testParentDowngrade()
+ throws MojoExecutionException, XMLStreamException, MojoFailureException, IllegalAccessException
+ {
+ setVariableValueToObject( mojo, "processParent", true );
+ setVariableValueToObject( mojo, "allowSnapshots", false );
+ setVariableValueToObject( mojo, "allowMajorUpdates", false );
+ setVariableValueToObject( mojo, "allowMinorUpdates", true );
+ setVariableValueToObject( mojo, "allowIncrementalUpdates", true );
+ setVariableValueToObject( mojo, "allowDowngrade", true );
+
+ mojo.getProject().setParentArtifact( new DefaultArtifact( "default-group", "dependency-artifact",
+ "1.1.1-SNAPSHOT", "compile", "pom", "default", null ) );
+ mojo.getProject().setParent( new MavenProject()
+ {{
+ setGroupId( mojo.getProject().getParentArtifact().getGroupId() );
+ setArtifactId( mojo.getProject().getParentArtifact().getArtifactId() );
+ setVersion( mojo.getProject().getParentArtifact().getVersion() );
+ }} );
+
+
+ try ( MockedStatic pomHelper = mockStatic( PomHelper.class ) )
+ {
+ pomHelper.when( () -> PomHelper.setDependencyVersion( any(), any(), any(), any(), any(), any() ) )
+ .thenReturn( true );
+ mojo.update( null );
+ }
+ assertThat( changeRecorder.getChanges(),
+ hasItem( new VersionChange( "default-group", "dependency-artifact", "1.1.1-SNAPSHOT",
+ "1.1.0" ) ) );
+ }
+}
\ No newline at end of file