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