View Javadoc
1   package org.apache.maven.shared.release.phase;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.apache.maven.artifact.ArtifactUtils;
26  import org.apache.maven.project.MavenProject;
27  import org.apache.maven.shared.release.ReleaseExecutionException;
28  import org.apache.maven.shared.release.ReleaseResult;
29  import org.apache.maven.shared.release.config.ReleaseDescriptor;
30  import org.apache.maven.shared.release.env.ReleaseEnvironment;
31  import org.apache.maven.shared.release.policy.PolicyException;
32  import org.apache.maven.shared.release.policy.version.VersionPolicy;
33  import org.apache.maven.shared.release.policy.version.VersionPolicyRequest;
34  import org.apache.maven.shared.release.util.ReleaseUtil;
35  import org.apache.maven.shared.release.versions.VersionParseException;
36  import org.codehaus.plexus.components.interactivity.Prompter;
37  import org.codehaus.plexus.components.interactivity.PrompterException;
38  import org.codehaus.plexus.util.StringUtils;
39  
40  import static java.util.Objects.requireNonNull;
41  import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
42  
43  /**
44   * Map projects to their new versions after release / into the next development cycle.
45   * <p>
46   * The map-phases per goal are:
47   * <dl>
48   *  <dt>release:prepare</dt><dd>map-release-versions + map-development-versions; RD.isBranchCreation() = false</dd>
49   *  <dt>release:branch</dt><dd>map-branch-versions + map-development-versions; RD.isBranchCreation() = true</dd>
50   *  <dt>release:update-versions</dt><dd>map-development-versions; RD.isBranchCreation() = false</dd>
51   * </dl>
52   *
53   * <table>
54   *   <caption>MapVersionsPhase</caption>
55   *   <tr>
56   *     <th>MapVersionsPhase field</th><th>map-release-versions</th><th>map-branch-versions</th>
57   *     <th>map-development-versions</th>
58   *   </tr>
59   *   <tr>
60   *     <td>convertToSnapshot</td>     <td>false</td>               <td>true</td>               <td>true</td>
61   *   </tr>
62   *   <tr>
63   *     <td>convertToBranch</td>       <td>false</td>               <td>true</td>               <td>false</td>
64   *   </tr>
65   * </table>
66   *
67   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
68   * @author Robert Scholte
69   */
70  public abstract class AbstractMapVersionsPhase
71          extends AbstractReleasePhase
72  {
73      /**
74       * Component used to prompt for input.
75       */
76      private final Prompter prompter;
77  
78      /**
79       * Component used for custom or default version policy
80       */
81      private final Map<String, VersionPolicy> versionPolicies;
82  
83      /**
84       * Whether to convert to a snapshot or a release.
85       */
86      private final boolean convertToSnapshot;
87  
88      /**
89       * Whether to convert to a snapshot or a release.
90       */
91      private final boolean convertToBranch;
92  
93      public AbstractMapVersionsPhase( Prompter prompter, Map<String, VersionPolicy> versionPolicies,
94                                       boolean convertToSnapshot, boolean convertToBranch )
95      {
96          this.prompter = requireNonNull( prompter );
97          this.versionPolicies = requireNonNull( versionPolicies );
98          this.convertToSnapshot = convertToSnapshot;
99          this.convertToBranch = convertToBranch;
100 
101     }
102 
103     @Override
104     public ReleaseResult execute( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
105                                   List<MavenProject> reactorProjects )
106             throws ReleaseExecutionException
107     {
108         ReleaseResult result = new ReleaseResult();
109 
110         MavenProject rootProject = ReleaseUtil.getRootProject( reactorProjects );
111 
112         if ( releaseDescriptor.isAutoVersionSubmodules() && ArtifactUtils.isSnapshot( rootProject.getVersion() ) )
113         {
114             // get the root project
115             MavenProject project = rootProject;
116 
117             String projectId = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
118 
119             String nextVersion = resolveNextVersion( project, projectId, releaseDescriptor );
120 
121             if ( !convertToSnapshot )
122             {
123                 releaseDescriptor.addReleaseVersion( projectId, nextVersion );
124             }
125             else if ( releaseDescriptor.isBranchCreation() && convertToBranch )
126             {
127                 releaseDescriptor.addReleaseVersion( projectId, nextVersion );
128             }
129             else
130             {
131                 releaseDescriptor.addDevelopmentVersion( projectId, nextVersion );
132             }
133 
134             for ( MavenProject subProject : reactorProjects )
135             {
136                 String subProjectId =
137                         ArtifactUtils.versionlessKey( subProject.getGroupId(), subProject.getArtifactId() );
138 
139                 if ( convertToSnapshot )
140                 {
141                     String subProjectNextVersion = releaseDescriptor.getProjectDevelopmentVersion( subProjectId );
142                     String v;
143                     if ( subProjectNextVersion != null )
144                     {
145                         v = subProjectNextVersion;
146                     }
147                     else if ( ArtifactUtils.isSnapshot( subProject.getVersion() ) )
148                     {
149                         v = nextVersion;
150                     }
151                     else
152                     {
153                         v = subProject.getVersion();
154                     }
155 
156                     if ( releaseDescriptor.isBranchCreation() && convertToBranch )
157                     {
158                         releaseDescriptor.addReleaseVersion( subProjectId, v );
159                     }
160                     else
161                     {
162                         releaseDescriptor.addDevelopmentVersion( subProjectId, v );
163                     }
164                 }
165                 else
166                 {
167                     String subProjectNextVersion = releaseDescriptor.getProjectReleaseVersion( subProjectId );
168                     if ( subProjectNextVersion != null )
169                     {
170                         releaseDescriptor.addReleaseVersion( subProjectId, subProjectNextVersion );
171                     }
172                     else
173                     {
174                         releaseDescriptor.addReleaseVersion( subProjectId, nextVersion );
175                     }
176                 }
177             }
178         }
179         else
180         {
181             for ( MavenProject project : reactorProjects )
182             {
183                 String projectId = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
184 
185                 String nextVersion = resolveNextVersion( project, projectId, releaseDescriptor );
186 
187                 if ( !convertToSnapshot )
188                 {
189                     releaseDescriptor.addReleaseVersion( projectId, nextVersion );
190                 }
191                 else if ( releaseDescriptor.isBranchCreation() && convertToBranch )
192                 {
193                     releaseDescriptor.addReleaseVersion( projectId, nextVersion );
194                 }
195                 else
196                 {
197                     releaseDescriptor.addDevelopmentVersion( projectId, nextVersion );
198                 }
199             }
200         }
201 
202         result.setResultCode( ReleaseResult.SUCCESS );
203 
204         return result;
205     }
206 
207     private String resolveNextVersion( MavenProject project,
208                                        String projectId,
209                                        ReleaseDescriptor releaseDescriptor )
210             throws ReleaseExecutionException
211     {
212         String defaultVersion;
213         if ( convertToBranch )
214         {
215             // no branch modification
216             if ( !( releaseDescriptor.isUpdateBranchVersions()
217                     && ( ArtifactUtils.isSnapshot( project.getVersion() )
218                     || releaseDescriptor.isUpdateVersionsToSnapshot() ) ) )
219             {
220                 return project.getVersion();
221             }
222 
223             defaultVersion = getReleaseVersion( projectId, releaseDescriptor );
224         }
225         else if ( !convertToSnapshot ) // map-release-version
226         {
227             defaultVersion = getReleaseVersion( projectId, releaseDescriptor );
228         }
229         else if ( releaseDescriptor.isBranchCreation() )
230         {
231             // no working copy modification
232             if ( !( ArtifactUtils.isSnapshot( project.getVersion() )
233                     && releaseDescriptor.isUpdateWorkingCopyVersions() ) )
234             {
235                 return project.getVersion();
236             }
237 
238             defaultVersion = getDevelopmentVersion( projectId, releaseDescriptor );
239         }
240         else
241         {
242             // no working copy modification
243             if ( !( releaseDescriptor.isUpdateWorkingCopyVersions() ) )
244             {
245                 return project.getVersion();
246             }
247 
248             defaultVersion = getDevelopmentVersion( projectId, releaseDescriptor );
249         }
250         //@todo validate default version, maybe with DefaultArtifactVersion
251 
252         String suggestedVersion = null;
253         String nextVersion = defaultVersion;
254         String messageFormat = null;
255         try
256         {
257             while ( nextVersion == null || ArtifactUtils.isSnapshot( nextVersion ) != convertToSnapshot )
258             {
259                 if ( suggestedVersion == null )
260                 {
261                     String baseVersion = null;
262                     if ( convertToSnapshot )
263                     {
264                         baseVersion = getReleaseVersion( projectId, releaseDescriptor );
265                     }
266                     // unspecified and unmapped version, so use project version
267                     if ( baseVersion == null )
268                     {
269                         baseVersion = project.getVersion();
270                     }
271 
272                     try
273                     {
274                         try
275                         {
276                             suggestedVersion =
277                                     resolveSuggestedVersion( baseVersion,
278                                             releaseDescriptor.getProjectVersionPolicyId() );
279                         }
280                         catch ( VersionParseException e )
281                         {
282                             if ( releaseDescriptor.isInteractive() )
283                             {
284                                 suggestedVersion =
285                                         resolveSuggestedVersion( "1.0", releaseDescriptor.getProjectVersionPolicyId() );
286                             }
287                             else
288                             {
289                                 throw new ReleaseExecutionException( "Error parsing version, cannot determine next "
290                                         + "version: " + e.getMessage(), e );
291                             }
292                         }
293                     }
294                     catch ( PolicyException | VersionParseException e )
295                     {
296                         throw new ReleaseExecutionException( e.getMessage(), e );
297                     }
298                 }
299 
300                 if ( releaseDescriptor.isInteractive() )
301                 {
302                     if ( messageFormat == null )
303                     {
304                         messageFormat = "What is the " + getContextString( releaseDescriptor )
305                             + " version for \"%s\"? (" + buffer().project( "%s" ) + ")";
306                     }
307                     String message = String.format( messageFormat, project.getName(), project.getArtifactId() );
308                     nextVersion = prompter.prompt( message, suggestedVersion );
309 
310                     //@todo validate next version, maybe with DefaultArtifactVersion
311                 }
312                 else if ( defaultVersion == null )
313                 {
314                     nextVersion = suggestedVersion;
315                 }
316                 else if ( convertToSnapshot )
317                 {
318                     throw new ReleaseExecutionException( defaultVersion + " is invalid, expected a snapshot" );
319                 }
320                 else
321                 {
322                     throw new ReleaseExecutionException( defaultVersion + " is invalid, expected a non-snapshot" );
323                 }
324             }
325         }
326         catch ( PrompterException e )
327         {
328             throw new ReleaseExecutionException( "Error reading version from input handler: " + e.getMessage(), e );
329         }
330         return nextVersion;
331     }
332 
333     private String getContextString( ReleaseDescriptor releaseDescriptor )
334     {
335         if ( convertToBranch )
336         {
337             return "branch";
338         }
339         if ( !convertToSnapshot )
340         {
341             return "release";
342         }
343         if ( releaseDescriptor.isBranchCreation() )
344         {
345             return "new working copy";
346         }
347         return "new development";
348     }
349 
350     private String resolveSuggestedVersion( String baseVersion, String policyId )
351             throws PolicyException, VersionParseException
352     {
353         VersionPolicy policy = versionPolicies.get( policyId );
354         if ( policy == null )
355         {
356             throw new PolicyException( "Policy '" + policyId + "' is unknown, available: " + versionPolicies.keySet() );
357         }
358 
359         VersionPolicyRequest request = new VersionPolicyRequest().setVersion( baseVersion );
360         return convertToSnapshot ? policy.getDevelopmentVersion( request ).getVersion()
361                 : policy.getReleaseVersion( request ).getVersion();
362     }
363 
364     private String getDevelopmentVersion( String projectId, ReleaseDescriptor releaseDescriptor )
365     {
366         String projectVersion = releaseDescriptor.getProjectDevelopmentVersion( projectId );
367 
368         if ( StringUtils.isEmpty( projectVersion ) )
369         {
370             projectVersion = releaseDescriptor.getDefaultDevelopmentVersion();
371         }
372 
373         if ( StringUtils.isEmpty( projectVersion ) )
374         {
375             return null;
376         }
377 
378         return projectVersion;
379     }
380 
381     private String getReleaseVersion( String projectId, ReleaseDescriptor releaseDescriptor )
382     {
383         String projectVersion = releaseDescriptor.getProjectReleaseVersion( projectId );
384 
385         if ( StringUtils.isEmpty( projectVersion ) )
386         {
387             projectVersion = releaseDescriptor.getDefaultReleaseVersion();
388         }
389 
390         if ( StringUtils.isEmpty( projectVersion ) )
391         {
392             return null;
393         }
394 
395         return projectVersion;
396     }
397 
398     @Override
399     public ReleaseResult simulate( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
400                                    List<MavenProject> reactorProjects )
401             throws ReleaseExecutionException
402     {
403         ReleaseResult result = new ReleaseResult();
404 
405         // It makes no modifications, so simulate is the same as execute
406         execute( releaseDescriptor, releaseEnvironment, reactorProjects );
407 
408         result.setResultCode( ReleaseResult.SUCCESS );
409 
410         return result;
411     }
412 }