diff --git a/src/main/java/org/codehaus/mojo/versions/SetMojo.java b/src/main/java/org/codehaus/mojo/versions/SetMojo.java index a268fb4a2..1938aacdc 100644 --- a/src/main/java/org/codehaus/mojo/versions/SetMojo.java +++ b/src/main/java/org/codehaus/mojo/versions/SetMojo.java @@ -19,12 +19,14 @@ * under the License. */ +import javax.xml.stream.XMLStreamException; + import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; import java.util.Date; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -36,8 +38,6 @@ import java.util.TreeMap; import java.util.regex.Pattern; -import javax.xml.stream.XMLStreamException; - import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.model.Model; import org.apache.maven.model.Parent; @@ -68,8 +68,7 @@ * @since 1.0-beta-1 */ @Mojo( name = "set", requiresProject = true, requiresDirectInvocation = true, aggregator = true, threadSafe = true ) -public class SetMojo - extends AbstractVersionsUpdaterMojo +public class SetMojo extends AbstractVersionsUpdaterMojo { private static final String SNAPSHOT = "-SNAPSHOT"; @@ -115,6 +114,7 @@ public class SetMojo * version of the current project. On Windows you can omit * the single quotes on Linux they are necessary to prevent * expansion through the shell. + * * @since 1.2 */ @Parameter( property = "oldVersion", defaultValue = "${project.version}" ) @@ -178,11 +178,25 @@ public class SetMojo /** * Whether to add next version number and -SNAPSHOT to the existing version. + * Unless specified by nextSnapshotIndexToIncrement, will increment + * the last minor index of the snapshot version, e.g. the z in x.y.z-SNAPSHOT * * @since 2.10 */ @Parameter( property = "nextSnapshot", defaultValue = "false" ) - private boolean nextSnapshot; + protected boolean nextSnapshot; + + /** + *

Specifies the version index to increment when using nextSnapshot. + * Will increment the (1-based, counting from the left, or the most major component) index + * of the snapshot version, e.g. for -DnextSnapshotIndexToIncrement=1 + * and the version being 1.2.3-SNAPSHOT, the new version will become 2.2.3-SNAPSHOT.

+ *

Only valid with nextSnapshot.

+ * + * @since 2.12 + */ + @Parameter( property = "nextSnapshotIndexToIncrement" ) + protected Integer nextSnapshotIndexToIncrement; /** * Whether to process all modules whereas they have parent/child or not. @@ -205,8 +219,8 @@ public class SetMojo /** * Whether to update the project.build.outputTimestamp property in the POM when setting version. * - * @deprecated please use {@link #updateBuildOutputTimestampPolicy} instead * @since 2.10 + * @deprecated please use {@link #updateBuildOutputTimestampPolicy} instead */ @Parameter( property = "updateBuildOutputTimestamp", defaultValue = "true" ) private boolean updateBuildOutputTimestamp; @@ -238,10 +252,9 @@ private synchronized void addChange( String groupId, String artifactId, String o * Called when this mojo is executed. * * @throws org.apache.maven.plugin.MojoExecutionException when things go wrong. - * @throws org.apache.maven.plugin.MojoFailureException when things go wrong. + * @throws org.apache.maven.plugin.MojoFailureException when things go wrong. */ - public void execute() - throws MojoExecutionException, MojoFailureException + public void execute() throws MojoExecutionException, MojoFailureException { if ( getProject().getOriginalModel().getVersion() == null ) { @@ -259,30 +272,15 @@ public void execute() } } + if ( !nextSnapshot && nextSnapshotIndexToIncrement != null ) + { + throw new MojoExecutionException( "nextSnapshotIndexToIncrement is not valid when nextSnapshot is false" ); + } + if ( !removeSnapshot && nextSnapshot ) { String version = getVersion(); - String versionWithoutSnapshot = version; - if ( version.endsWith( SNAPSHOT ) ) - { - versionWithoutSnapshot = version.substring( 0, version.indexOf( SNAPSHOT ) ); - } - LinkedList numbers = new LinkedList(); - if ( versionWithoutSnapshot.contains( "." ) ) - { - // Chop the version into numbers by splitting on the dot (.) - Collections.addAll( numbers, versionWithoutSnapshot.split( "\\." ) ); - } - else - { - // The version contains no dots, assume that it is only 1 number - numbers.add( versionWithoutSnapshot ); - } - - int lastNumber = Integer.parseInt( numbers.removeLast() ); - numbers.addLast( String.valueOf( lastNumber + 1 ) ); - String nextVersion = StringUtils.join( numbers.toArray( new String[0] ), "." ); - newVersion = nextVersion + "-SNAPSHOT"; + newVersion = getIncrementedVersion( version, nextSnapshotIndexToIncrement ); getLog().info( "SNAPSHOT found. BEFORE " + version + " --> AFTER: " + newVersion ); } @@ -292,8 +290,8 @@ public void execute() { try { - newVersion = - prompter.prompt( "Enter the new version to set", getProject().getOriginalModel().getVersion() ); + newVersion = prompter.prompt( "Enter the new version to set", + getProject().getOriginalModel().getVersion() ); } catch ( PrompterException e ) { @@ -303,68 +301,70 @@ public void execute() else { throw new MojoExecutionException( "You must specify the new version, either by using the newVersion " - + "property (that is -DnewVersion=... on the command line) or run in interactive mode" ); + + "property (that is -DnewVersion=... on the command line) or run in interactive mode" ); } } if ( StringUtils.isEmpty( newVersion ) ) { throw new MojoExecutionException( "You must specify the new version, either by using the newVersion " - + "property (that is -DnewVersion=... on the command line) or run in interactive mode" ); + + "property (that is -DnewVersion=... on the command line) or run in interactive mode" ); } - if ( !"onchange".equals( updateBuildOutputTimestampPolicy ) - && !"always".equals( updateBuildOutputTimestampPolicy ) - && !"never".equals( updateBuildOutputTimestampPolicy ) ) + if ( !"onchange".equals( updateBuildOutputTimestampPolicy ) && !"always".equals( + updateBuildOutputTimestampPolicy ) && !"never".equals( updateBuildOutputTimestampPolicy ) ) { - throw new MojoExecutionException( "updateBuildOutputTimestampPolicy should be one of: " - + "\"onchange\", \"always\", \"never\"." ); + throw new MojoExecutionException( + "updateBuildOutputTimestampPolicy should be one of: " + "\"onchange\", \"always\", \"never\"." ); } try { final MavenProject project; - if ( processFromLocalAggregationRoot ) { + if ( processFromLocalAggregationRoot ) + { project = PomHelper.getLocalRoot( projectBuilder, getProject(), localRepository, null, getLog() ); } - else { + else + { project = getProject(); } getLog().info( "Local aggregation root: " + project.getBasedir() ); Map reactorModels = PomHelper.getReactorModels( project, getLog() ); final SortedMap reactor = - new TreeMap( new ReactorDepthComparator( reactorModels ) ); + new TreeMap( new ReactorDepthComparator( reactorModels ) ); reactor.putAll( reactorModels ); // set of files to update final Set files = new LinkedHashSet(); - getLog().info( "Processing change of " + groupId + ":" + artifactId + ":" + oldVersion + " -> " - + newVersion ); + getLog().info( + "Processing change of " + groupId + ":" + artifactId + ":" + oldVersion + " -> " + newVersion ); Pattern groupIdRegex = - Pattern.compile( RegexUtils.convertWildcardsToRegex( fixNullOrEmpty( groupId, "*" ), true ) ); + Pattern.compile( RegexUtils.convertWildcardsToRegex( fixNullOrEmpty( groupId, "*" ), true ) ); Pattern artifactIdRegex = - Pattern.compile( RegexUtils.convertWildcardsToRegex( fixNullOrEmpty( artifactId, "*" ), true ) ); + Pattern.compile( RegexUtils.convertWildcardsToRegex( fixNullOrEmpty( artifactId, "*" ), true ) ); Pattern oldVersionIdRegex = - Pattern.compile( RegexUtils.convertWildcardsToRegex( fixNullOrEmpty( oldVersion, "*" ), true ) ); + Pattern.compile( RegexUtils.convertWildcardsToRegex( fixNullOrEmpty( oldVersion, "*" ), true ) ); boolean found = false; for ( Model m : reactor.values() ) { final String mGroupId = PomHelper.getGroupId( m ); final String mArtifactId = PomHelper.getArtifactId( m ); final String mVersion = PomHelper.getVersion( m ); - if (( ( groupIdRegex.matcher( mGroupId ).matches() && artifactIdRegex.matcher( mArtifactId ).matches() ) // - || (processAllModules) ) // - && oldVersionIdRegex.matcher( mVersion ).matches() && !newVersion.equals( mVersion ) ) + if ( ( ( groupIdRegex.matcher( mGroupId ).matches() && artifactIdRegex.matcher( mArtifactId ) + .matches() ) // + || ( processAllModules ) ) // + && oldVersionIdRegex.matcher( mVersion ).matches() && !newVersion.equals( mVersion ) ) { found = true; // if the change is not one we have swept up already applyChange( project, reactor, files, mGroupId, m.getArtifactId(), - StringUtils.isBlank( oldVersion ) || "*".equals( oldVersion ) ? "" : m.getVersion() ); + StringUtils.isBlank( oldVersion ) || "*".equals( oldVersion ) ? "" : m.getVersion() ); } } if ( !found && RegexUtils.getWildcardScore( groupId ) == 0 && RegexUtils.getWildcardScore( artifactId ) == 0 - && RegexUtils.getWildcardScore( oldVersion ) == 0 ) + && RegexUtils.getWildcardScore( oldVersion ) == 0 ) { applyChange( project, reactor, files, groupId, artifactId, oldVersion ); } @@ -382,6 +382,40 @@ public void execute() } } + /** + * Returns the incremented version, with the nextSnapshotIndexToIncrement indicating the 1-based index, + * conunting from the left, or the most major version component, of the version string. + * + * @param version input version + * @return version with the incremented index specified by nextSnapshotIndexToIncrement or last index + * @throws MojoExecutionException thrown if the input parameters are invalid + */ + protected String getIncrementedVersion( String version, Integer nextSnapshotIndexToIncrement ) + throws MojoExecutionException + { + String versionWithoutSnapshot = + version.endsWith( SNAPSHOT ) ? version.substring( 0, version.indexOf( SNAPSHOT ) ) : version; + List numbers = new LinkedList<>( Arrays.asList( versionWithoutSnapshot.split( "\\." ) ) ); + + if ( nextSnapshotIndexToIncrement == null ) + { + nextSnapshotIndexToIncrement = numbers.size(); + } + else if ( nextSnapshotIndexToIncrement < 1 ) + { + throw new MojoExecutionException( "nextSnapshotIndexToIncrement cannot be less than 1" ); + } + else if ( nextSnapshotIndexToIncrement > numbers.size() ) + { + throw new MojoExecutionException( + "nextSnapshotIndexToIncrement cannot be greater than the last version index" ); + } + int snapshotVersionToIncrement = Integer.parseInt( numbers.remove( nextSnapshotIndexToIncrement - 1 ) ); + numbers.add( nextSnapshotIndexToIncrement - 1, String.valueOf( snapshotVersionToIncrement + 1 ) ); + + return StringUtils.join( numbers.toArray( new String[0] ), "." ) + "-SNAPSHOT"; + } + private static String fixNullOrEmpty( String value, String defaultValue ) { return StringUtils.isBlank( value ) ? defaultValue : value; @@ -406,8 +440,8 @@ private void applyChange( MavenProject project, SortedMap reactor final String sourcePath = sourceEntry.getKey(); final Model sourceModel = sourceEntry.getValue(); - getLog().debug( sourcePath.length() == 0 ? "Processing root module as parent" - : "Processing " + sourcePath + " as a parent." ); + getLog().debug( sourcePath.length() == 0 ? "Processing root module as parent" : + "Processing " + sourcePath + " as a parent." ); final String sourceGroupId = PomHelper.getGroupId( sourceModel ); if ( sourceGroupId == null ) @@ -430,50 +464,53 @@ private void applyChange( MavenProject project, SortedMap reactor addFile( files, project, sourcePath ); - getLog().debug( "Looking for modules which use " - + ArtifactUtils.versionlessKey( sourceGroupId, sourceArtifactId ) + " as their parent" ); + getLog().debug( + "Looking for modules which use " + ArtifactUtils.versionlessKey( sourceGroupId, sourceArtifactId ) + + " as their parent" ); for ( Map.Entry stringModelEntry : processAllModules ? reactor.entrySet() : // - PomHelper.getChildModels( reactor, sourceGroupId, - sourceArtifactId ).entrySet() ) + PomHelper.getChildModels( reactor, sourceGroupId, sourceArtifactId ).entrySet() ) { final Model targetModel = stringModelEntry.getValue(); final Parent parent = targetModel.getParent(); getLog().debug( "Module: " + stringModelEntry.getKey() ); if ( parent != null && sourceVersion.equals( parent.getVersion() ) ) { - getLog().debug( " parent already is " - + ArtifactUtils.versionlessKey( sourceGroupId, sourceArtifactId ) + ":" + sourceVersion ); + getLog().debug( + " parent already is " + ArtifactUtils.versionlessKey( sourceGroupId, sourceArtifactId ) + + ":" + sourceVersion ); } else { - getLog().debug( " parent is " + ArtifactUtils.versionlessKey( sourceGroupId, sourceArtifactId ) - + ":" + ( parent == null ? "" : parent.getVersion() )); - getLog().debug( " will become " + ArtifactUtils.versionlessKey( sourceGroupId, sourceArtifactId ) - + ":" + sourceVersion ); + getLog().debug( + " parent is " + ArtifactUtils.versionlessKey( sourceGroupId, sourceArtifactId ) + ":" + ( + parent == null ? "" : parent.getVersion() ) ); + getLog().debug( + " will become " + ArtifactUtils.versionlessKey( sourceGroupId, sourceArtifactId ) + ":" + + sourceVersion ); } final boolean targetExplicit = PomHelper.isExplicitVersion( targetModel ); if ( ( updateMatchingVersions || !targetExplicit ) // - && ( parent != null && StringUtils.equals( parent.getVersion(), PomHelper.getVersion( targetModel ) ) ) ) + && ( parent != null && StringUtils.equals( parent.getVersion(), + PomHelper.getVersion( targetModel ) ) ) ) { - getLog().debug( " module is " - + ArtifactUtils.versionlessKey( PomHelper.getGroupId( targetModel ), - PomHelper.getArtifactId( targetModel ) ) - + ":" + PomHelper.getVersion( targetModel ) ); - getLog().debug( " will become " - + ArtifactUtils.versionlessKey( PomHelper.getGroupId( targetModel ), - PomHelper.getArtifactId( targetModel ) ) - + ":" + sourceVersion ); + getLog().debug( + " module is " + ArtifactUtils.versionlessKey( PomHelper.getGroupId( targetModel ), + PomHelper.getArtifactId( targetModel ) ) + ":" + PomHelper.getVersion( + targetModel ) ); + getLog().debug( + " will become " + ArtifactUtils.versionlessKey( PomHelper.getGroupId( targetModel ), + PomHelper.getArtifactId( targetModel ) ) + ":" + sourceVersion ); addChange( PomHelper.getGroupId( targetModel ), PomHelper.getArtifactId( targetModel ), - PomHelper.getVersion( targetModel ), sourceVersion ); + PomHelper.getVersion( targetModel ), sourceVersion ); targetModel.setVersion( sourceVersion ); } else { - getLog().debug( " module is " - + ArtifactUtils.versionlessKey( PomHelper.getGroupId( targetModel ), - PomHelper.getArtifactId( targetModel ) ) - + ":" + PomHelper.getVersion( targetModel ) ); + getLog().debug( + " module is " + ArtifactUtils.versionlessKey( PomHelper.getGroupId( targetModel ), + PomHelper.getArtifactId( targetModel ) ) + ":" + PomHelper.getVersion( + targetModel ) ); } } } @@ -509,11 +546,11 @@ else if ( moduleDir.isDirectory() ) * * @param pom The pom file to update. * @throws org.apache.maven.plugin.MojoExecutionException when things go wrong. - * @throws org.apache.maven.plugin.MojoFailureException when things go wrong. - * @throws javax.xml.stream.XMLStreamException when things go wrong. + * @throws org.apache.maven.plugin.MojoFailureException when things go wrong. + * @throws javax.xml.stream.XMLStreamException when things go wrong. */ protected synchronized void update( ModifiedPomXMLEventReader pom ) - throws MojoExecutionException, MojoFailureException, XMLStreamException + throws MojoExecutionException, MojoFailureException, XMLStreamException { ContextualLog log = new DelegatingContextualLog( getLog() ); try @@ -526,18 +563,21 @@ protected synchronized void update( ModifiedPomXMLEventReader pom ) versionChangerFactory.setLog( log ); versionChangerFactory.setModel( model ); - VersionChanger changer = versionChangerFactory.newVersionChanger( processParent, processProject, - processDependencies, processPlugins ); + VersionChanger changer = + versionChangerFactory.newVersionChanger( processParent, processProject, processDependencies, + processPlugins ); for ( VersionChange versionChange : sourceChanges ) { changer.apply( versionChange ); } - if ( updateBuildOutputTimestamp && !"never".equals( updateBuildOutputTimestampPolicy ) ) { - if ( "always".equals( updateBuildOutputTimestampPolicy) || !sourceChanges.isEmpty() ) { + if ( updateBuildOutputTimestamp && !"never".equals( updateBuildOutputTimestampPolicy ) ) + { + if ( "always".equals( updateBuildOutputTimestampPolicy ) || !sourceChanges.isEmpty() ) + { // also update project.build.outputTimestamp - updateBuildOutputTimestamp(pom, model); + updateBuildOutputTimestamp( pom, model ); } } } @@ -548,8 +588,7 @@ protected synchronized void update( ModifiedPomXMLEventReader pom ) log.clearContext(); } - private void updateBuildOutputTimestamp( ModifiedPomXMLEventReader pom, Model model ) - throws XMLStreamException + private void updateBuildOutputTimestamp( ModifiedPomXMLEventReader pom, Model model ) throws XMLStreamException { String buildOutputTimestamp = model.getProperties().getProperty( "project.build.outputTimestamp" ); diff --git a/src/test/java/org/codehaus/mojo/versions/SetMojoTest.java b/src/test/java/org/codehaus/mojo/versions/SetMojoTest.java new file mode 100644 index 000000000..b078dbecc --- /dev/null +++ b/src/test/java/org/codehaus/mojo/versions/SetMojoTest.java @@ -0,0 +1,95 @@ +package org.codehaus.mojo.versions; + +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.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.fail; + +public class SetMojoTest +{ + @Test + public void testGetIncrementedVersion() throws MojoExecutionException + { + new SetMojo() + { + { + assertThat( getIncrementedVersion( "1.0.0", null ), is( "1.0.1-SNAPSHOT" ) ); + assertThat( getIncrementedVersion( "1.0.0-SNAPSHOT", null ), is( "1.0.1-SNAPSHOT" ) ); + assertThat( getIncrementedVersion( "1.0.0-SNAPSHOT", 1 ), is( "2.0.0-SNAPSHOT" ) ); + assertThat( getIncrementedVersion( "1.0.0-SNAPSHOT", 2 ), is( "1.1.0-SNAPSHOT" ) ); + assertThat( getIncrementedVersion( "1.0.0-SNAPSHOT", 3 ), is( "1.0.1-SNAPSHOT" ) ); + } + }; + } + + @Test + public void testNextSnapshotIndexLowerBound() + { + new SetMojo() + { + { + try + { + getIncrementedVersion( "1.0.0", 0 ); + fail(); + } + catch ( MojoExecutionException e ) + { + assertThat( e.getMessage(), + containsString( "nextSnapshotIndexToIncrement cannot be less than 1" ) ); + } + } + }; + } + + @Test + public void testNextSnapshotIndexUpperBound() + { + new SetMojo() + { + { + try + { + getIncrementedVersion( "1.0.0", 4 ); + fail(); + } + catch ( MojoExecutionException e ) + { + assertThat( e.getMessage(), containsString( + "nextSnapshotIndexToIncrement cannot be greater than the last version index" ) ); + } + } + }; + } + + @Test + public void testNextSnapshotIndexWithoutNextSnapshot() throws MojoFailureException + { + try + { + new SetMojo() + { + { + project = new MavenProject(); + project.setParent( new MavenProject() ); + project.setOriginalModel( new Model() ); + project.getOriginalModel().setVersion( "1.2.3-SNAPSHOT" ); + + nextSnapshotIndexToIncrement = 4; + } + }.execute(); + } + catch ( MojoExecutionException e ) + { + assertThat( e.getMessage(), + containsString( "nextSnapshotIndexToIncrement is not valid when nextSnapshot is false" ) ); + } + } + +}