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.plugins.release;
20  
21  import javax.inject.Inject;
22  
23  import java.io.FileInputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.util.Arrays;
27  
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.maven.plugin.MojoFailureException;
30  import org.apache.maven.plugins.annotations.Mojo;
31  import org.apache.maven.plugins.annotations.Parameter;
32  import org.apache.maven.plugins.annotations.ResolutionScope;
33  import org.apache.maven.scm.manager.ScmManager;
34  import org.apache.maven.shared.release.DefaultReleaseManagerListener;
35  import org.apache.maven.shared.release.ReleaseExecutionException;
36  import org.apache.maven.shared.release.ReleaseFailureException;
37  import org.apache.maven.shared.release.ReleaseManager;
38  import org.apache.maven.shared.release.ReleasePrepareRequest;
39  import org.apache.maven.shared.release.config.ReleaseDescriptorBuilder;
40  import org.codehaus.plexus.configuration.PlexusConfiguration;
41  
42  /**
43   * Prepare for a release in SCM. Steps through several phases to ensure the POM is ready to be released and then
44   * prepares SCM to eventually contain a tagged version of the release and a record in the local copy of the parameters
45   * used. This can be followed by a call to <code>release:perform</code>. For more info see <a
46   * href="https://maven.apache.org/plugins/maven-release-plugin/usage/prepare-release.html"
47   * >https://maven.apache.org/plugins/maven-release-plugin/usage/prepare-release.html</a>.
48   *
49   * @author <a href="mailto:jdcasey@apache.org">John Casey</a>
50   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
51   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
52   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
53   */
54  // TODO [!] check how this works with version ranges
55  @Mojo(name = "prepare", aggregator = true, requiresDependencyCollection = ResolutionScope.TEST)
56  public class PrepareReleaseMojo extends AbstractScmReadWriteReleaseMojo {
57  
58      /**
59       * Resume a previous release attempt from the point where it was stopped.
60       */
61      @Parameter(defaultValue = "true", property = "resume")
62      private boolean resume;
63  
64      /**
65       * @deprecated Please use release:prepare-with-pom instead.
66       */
67      @Deprecated
68      @Parameter(defaultValue = "false", property = "generateReleasePoms")
69      private boolean generateReleasePoms;
70  
71      /**
72       * Whether to use "edit" mode on the SCM, to lock the file for editing during SCM operations.
73       */
74      @Parameter(defaultValue = "false", property = "useEditMode")
75      private boolean useEditMode;
76  
77      /**
78       * Whether to update dependencies version to the next development version.
79       *
80       * @since 2.0-beta-5
81       */
82      @Parameter(defaultValue = "true", property = "updateDependencies")
83      private boolean updateDependencies;
84  
85      /**
86       * Whether to automatically assign submodules the parent version. If set to false, the user will be prompted for the
87       * version of each submodules.
88       *
89       * @since 2.0-beta-5
90       */
91      @Parameter(defaultValue = "false", property = "autoVersionSubmodules")
92      private boolean autoVersionSubmodules;
93  
94      /**
95       * Dry run: don't checkin or tag anything in the scm repository, or modify the checkout. Running
96       * <code>mvn -DdryRun=true release:prepare</code> is useful in order to check that modifications to poms and scm
97       * operations (only listed on the console) are working as expected. Modified POMs are written alongside the
98       * originals without modifying them.
99       */
100     @Parameter(defaultValue = "false", property = "dryRun")
101     private boolean dryRun;
102 
103     /**
104      * Whether to add a schema to the POM if it was previously missing on release.
105      */
106     @Parameter(defaultValue = "true", property = "addSchema")
107     private boolean addSchema;
108 
109     /**
110      * Comma separated profiles to enable on release prepare, in addition to active profiles for project execution.
111      *
112      * @since 3.0.0
113      */
114     @Parameter(property = "preparationProfiles")
115     private String preparationProfiles;
116 
117     /**
118      * Goals to run as part of the preparation step, after transformation but before committing. Space delimited.
119      */
120     @Parameter(defaultValue = "clean verify", property = "preparationGoals")
121     private String preparationGoals;
122 
123     /**
124      * Goals to run on completion of the preparation step, after transformation back to the next development version but
125      * before committing. Space delimited.
126      *
127      * @since 2.2
128      */
129     @Parameter(defaultValue = "", property = "completionGoals")
130     private String completionGoals;
131 
132     /**
133      * Commits to do are atomic or by project.
134      *
135      * @since 2.0-beta-5
136      */
137     @Parameter(defaultValue = "false", property = "commitByProject")
138     private boolean commitByProject;
139 
140     /**
141      * Whether to allow timestamped SNAPSHOT dependencies. Default is to fail when finding any SNAPSHOT.
142      *
143      * @since 2.0-beta-7
144      */
145     @Parameter(defaultValue = "false", property = "ignoreSnapshots")
146     private boolean allowTimestampedSnapshots;
147 
148     /**
149      * Whether to allow usage of a SNAPSHOT version of the Release Plugin. This in an internal property used to support
150      * testing of the plugin itself in batch mode.
151      *
152      * @since 2.0-beta-9
153      */
154     @Parameter(defaultValue = "false", property = "allowReleasePluginSnapshot", readonly = true)
155     private boolean allowReleasePluginSnapshot;
156 
157     /**
158      * A list of additional exclude filters that will be skipped when checking for modifications on the working copy. Is
159      * ignored, when checkModificationExcludes is set.
160      *
161      * @since 2.1
162      */
163     @Parameter
164     private String[] checkModificationExcludes;
165 
166     /**
167      * Command-line version of checkModificationExcludes.
168      *
169      * @since 2.1
170      */
171     @Parameter(property = "checkModificationExcludeList")
172     private String checkModificationExcludeList;
173 
174     /**
175      * Default version to use when preparing a release or a branch.
176      *
177      * @since 2.0-beta-8
178      */
179     @Parameter(property = "releaseVersion")
180     private String releaseVersion;
181 
182     /**
183      * Default version to use for new local working copy.
184      *
185      * @since 2.0-beta-8
186      */
187     @Parameter(property = "developmentVersion")
188     private String developmentVersion;
189 
190     /**
191      * Currently only implemented with svn scm.
192      * <ul>
193      * <li>Enables a workaround to prevent issue due to svn client > 1.5.0 (fixed in 1.6.5)
194      * (https://issues.apache.org/jira/browse/SCM-406)</li>
195      * <li>You may not want to use this in conjunction with <code>suppressCommitBeforeTag</code>, such that no poms with
196      * released versions are committed to the working copy ever.</li>
197      * </ul>
198      *
199      * @since 2.0-beta-9
200      */
201     @Parameter(defaultValue = "true", property = "remoteTagging")
202     private boolean remoteTagging;
203 
204     /**
205      * Signs SCM tag when possible, for example when using the git-exe the '--sign' argument is used.
206      *
207      * @since 3.0.0-M4
208      */
209     @Parameter(property = "signTag")
210     private boolean signTag = false;
211 
212     /**
213      * Whether to bump the working copy versions to <code>developmentVersion</code>.
214      *
215      * @since 2.1
216      */
217     @Parameter(defaultValue = "true", property = "updateWorkingCopyVersions")
218     private boolean updateWorkingCopyVersions;
219 
220     /**
221      * Whether to suppress a commit of changes to the working copy before the tag is created. <br/>
222      * <br/>
223      * This requires <code>remoteTagging</code> to be set to false. <br/>
224      * <br/>
225      * <code>suppressCommitBeforeTag</code> is useful when you want to avoid poms with released versions in all
226      * revisions of your trunk or development branch.
227      *
228      * @since 2.1
229      */
230     @Parameter(defaultValue = "false", property = "suppressCommitBeforeTag")
231     private boolean suppressCommitBeforeTag;
232 
233     /**
234      * Wait the specified number of seconds before creating the tag. <br/>
235      * <code>waitBeforeTagging</code> is useful when your source repository is synced between several instances and
236      * access to it is determined by geographical location, like the SVN repository at the Apache Software Foundation.
237      *
238      * @since 2.2
239      */
240     @Parameter(defaultValue = "0", property = "waitBeforeTagging")
241     private int waitBeforeTagging;
242 
243     /**
244      * The role-hint for the {@link org.apache.maven.shared.release.policy.version.VersionPolicy}
245      * implementation used to calculate the project versions.
246      *
247      * @see <a href="./versioning-policies.html">Versioning Policies</a>
248      * @since 2.5.1
249      */
250     @Parameter(defaultValue = "default", property = "projectVersionPolicyId")
251     private String projectVersionPolicyId;
252 
253     /**
254      * Optional config for the VersionPolicy implementation used to calculate the project versions.
255      *
256      * @since 3.0.0
257      */
258     @Parameter(property = "projectVersionPolicyConfig")
259     private PlexusConfiguration projectVersionPolicyConfig;
260 
261     /**
262      * The role-hint for the {@link org.apache.maven.shared.release.policy.naming.NamingPolicy}
263      * implementation used to calculate the project branch and tag names.
264      *
265      * @see org.apache.maven.shared.release.policies.DefaultNamingPolicy
266      * @since 3.0.0-M1
267      */
268     @Parameter(property = "projectNamingPolicyId")
269     private String projectTagNamingPolicyId;
270 
271     /**
272      * The SCM commit comment for the commit setting pom.xml to release version.
273      * Defaults to "@{prefix} prepare release @{releaseLabel}".
274      * <p>
275      * Property interpolation is performed on the value, but in order to ensure that the interpolation occurs
276      * during release, you must use <code>@{...}</code> to reference the properties rather than <code>${...}</code>.
277      * The following properties are available:
278      * <ul>
279      *     <li><code>prefix</code> - The comment prefix.
280      *     <li><code>groupId</code> - The groupId of the root project.
281      *     <li><code>artifactId</code> - The artifactId of the root project.
282      *     <li><code>releaseLabel</code> - The release version of the root project.
283      * </ul>
284      * It is recommended to automatically skip this commit from the default CI/CD build by including the string {@code ci skip}
285      * in the commit message which is understood
286      * by most CI systems, like <a href="https://docs.github.com/en/actions/how-tos/manage-workflow-runs/skip-workflow-runs">GitHub Actions</a>,
287      * <a href="https://docs.gitlab.com/ci/pipelines/#skip-a-pipeline">GitLab Pipelines</a>, and probably some more.
288      * Otherwise the non-SNAPSHOT version is built again (outside the actual release) and potentially also deployed somewhere
289      * (which often leads to failed builds).
290      * @since 3.0.0-M1
291      */
292     @Parameter(
293             defaultValue = "@{prefix} prepare release @{releaseLabel} [ci skip]",
294             property = "scmReleaseCommitComment")
295     private String scmReleaseCommitComment = "@{prefix} prepare release @{releaseLabel} [ci skip]";
296 
297     /**
298      * The SCM commit comment for the commit setting pom.xml back to development version.
299      * Defaults to "@{prefix} prepare for next development iteration".
300      * <p>
301      * Property interpolation is performed on the value, but in order to ensure that the interpolation occurs
302      * during release, you must use <code>@{...}</code> to reference the properties rather than <code>${...}</code>.
303      * The following properties are available:
304      * <ul>
305      *     <li><code>prefix</code> - The comment prefix.
306      *     <li><code>groupId</code> - The groupId of the root project.
307      *     <li><code>artifactId</code> - The artifactId of the root project.
308      *     <li><code>releaseLabel</code> - The release version of the root project.
309      * </ul>
310      *
311      * @since 3.0.0-M1
312      */
313     @Parameter(
314             defaultValue = "@{prefix} prepare for next development iteration",
315             property = "scmDevelopmentCommitComment")
316     private String scmDevelopmentCommitComment = "@{prefix} prepare for next development iteration";
317 
318     /**
319      * Specifies whether unresolved SNAPSHOT dependencies should automatically be resolved.
320      * If this is set, then this specifies the default answer to be used when unresolved SNAPSHOT
321      * dependencies should automatically be resolved ( 0:All 1:Project Dependencies 2:Plugins
322      * 3:Reports 4:Extensions ). Possible values are:
323      * <ul>
324      * <li>"all" or "0": resolve all kinds of snapshots, ie. project, plugin, report and extension dependencies </li>
325      * <li>"dependencies" or "1": resolve project dependencies</li>
326      * <li>"plugins" or "2": resolve plugin dependencis</li>
327      * <li>"reports" or "3": resolve report dependencies</li>
328      * <li>"extensions" or "4": resolve extension dependencies</li>
329      * </ul>
330      *
331      * @since 3.0.0-M4
332      */
333     @Parameter(property = "autoResolveSnapshots")
334     private String autoResolveSnapshots;
335 
336     /**
337      * Currently only implemented with svn scm. Enable the {@code --pin-externals} option in
338      * {@code svn copy} command which is new in Subversion 1.9.
339      *
340      * @since 3.0.0-M4
341      */
342     @Parameter(defaultValue = "false", property = "pinExternals")
343     private boolean pinExternals;
344 
345     @Inject
346     public PrepareReleaseMojo(ReleaseManager releaseManager, ScmManager scmManager) {
347         super(releaseManager, scmManager);
348     }
349 
350     /**
351      * {@inheritDoc}
352      */
353     @Override
354     protected String getAdditionalProfiles() {
355         return preparationProfiles;
356     }
357 
358     /**
359      * Specifies the line separator to format pom.xml. The following properties are
360      * available:
361      * <ul>
362      * <li><code>system</code> - Use the system line separator.</li>
363      * <li><code>lf</code> - Use \n as line separator.</li>
364      * <li><code>cr</code> - Use \r as line separator.</li>
365      * <li><code>crlf</code> - Use \r\n as line separator.</li>
366      * <li><code>source</code> - Use the same line separator as it is specified in the current pom.xml.</li>
367      * </ul>
368      *
369      * @since 3.0.0-M6
370      */
371     @Parameter(defaultValue = "source", property = "lineSeparator")
372     private String lineSeparator;
373 
374     /**
375      * {@inheritDoc}
376      */
377     @Override
378     public void execute() throws MojoExecutionException, MojoFailureException {
379         if (generateReleasePoms) {
380             throw new MojoFailureException("Generating release POMs is no longer supported in release:prepare. "
381                     + "Please run release:prepare-with-pom instead.");
382         }
383 
384         prepareRelease(generateReleasePoms);
385     }
386 
387     /**
388      * <p>prepareRelease.</p>
389      *
390      * @param generateReleasePoms a boolean
391      * @throws org.apache.maven.plugin.MojoExecutionException if any
392      * @throws org.apache.maven.plugin.MojoFailureException if any
393      */
394     protected void prepareRelease(boolean generateReleasePoms) throws MojoExecutionException, MojoFailureException {
395         // this is here so the subclass can call it without getting the extra generateReleasePoms check in execute()
396         // above
397         super.execute();
398 
399         final ReleaseDescriptorBuilder config = createReleaseDescriptor();
400         config.setAddSchema(addSchema);
401         config.setGenerateReleasePoms(generateReleasePoms);
402         config.setScmUseEditMode(useEditMode);
403         config.setPreparationGoals(preparationGoals);
404         config.setCompletionGoals(completionGoals);
405         config.setCommitByProject(commitByProject);
406         config.setUpdateDependencies(updateDependencies);
407         config.setAutoVersionSubmodules(autoVersionSubmodules);
408         config.setAllowTimestampedSnapshots(allowTimestampedSnapshots);
409         config.setSnapshotReleasePluginAllowed(allowReleasePluginSnapshot);
410         config.setDefaultReleaseVersion(releaseVersion);
411         config.setDefaultDevelopmentVersion(developmentVersion);
412         config.setRemoteTagging(remoteTagging);
413         config.setScmSignTags(signTag);
414         config.setUpdateWorkingCopyVersions(updateWorkingCopyVersions);
415         config.setSuppressCommitBeforeTagOrBranch(suppressCommitBeforeTag);
416         config.setWaitBeforeTagging(waitBeforeTagging);
417         config.setProjectVersionPolicyId(projectVersionPolicyId);
418         if (projectVersionPolicyConfig != null) {
419             config.setProjectVersionPolicyConfig(projectVersionPolicyConfig.toString());
420         }
421         config.setProjectNamingPolicyId(projectTagNamingPolicyId);
422         config.setScmDevelopmentCommitComment(scmDevelopmentCommitComment);
423         config.setScmReleaseCommitComment(scmReleaseCommitComment);
424         config.setAutoResolveSnapshots(autoResolveSnapshots);
425         config.setPinExternals(pinExternals);
426         config.setLineSeparator(resolveLineSeparator());
427 
428         if (checkModificationExcludeList != null) {
429             checkModificationExcludes =
430                     checkModificationExcludeList.replaceAll("\\s", "").split(",");
431         }
432 
433         if (checkModificationExcludes != null) {
434             config.setCheckModificationExcludes(Arrays.asList(checkModificationExcludes));
435         }
436 
437         ReleasePrepareRequest prepareRequest = new ReleasePrepareRequest();
438         prepareRequest.setReleaseDescriptorBuilder(config);
439         prepareRequest.setReleaseEnvironment(getReleaseEnvironment());
440         prepareRequest.setReactorProjects(getReactorProjects());
441         prepareRequest.setReleaseManagerListener(new DefaultReleaseManagerListener(getLog(), dryRun));
442         prepareRequest.setResume(resume);
443         prepareRequest.setDryRun(dryRun);
444         prepareRequest.setUserProperties(session.getUserProperties());
445 
446         try {
447             releaseManager.prepare(prepareRequest);
448         } catch (ReleaseExecutionException e) {
449             throw new MojoExecutionException(e.getMessage(), e);
450         } catch (ReleaseFailureException e) {
451             throw new MojoFailureException(e.getMessage(), e);
452         }
453     }
454 
455     private String resolveLineSeparator() throws MojoExecutionException {
456         if (lineSeparator == null) {
457             return getLineSeparatorFromPom();
458         }
459 
460         switch (lineSeparator) {
461             case "lf":
462                 return "\n";
463             case "cr":
464                 return "\r";
465             case "crlf":
466                 return "\r\n";
467             case "system":
468                 return System.lineSeparator();
469             case "source":
470                 return getLineSeparatorFromPom();
471             default:
472                 throw new IllegalArgumentException(String.format(
473                         "Unknown property lineSeparator: '%s'. Use one of"
474                                 + " the following: 'source', 'system', 'lf', 'cr', 'crlf'.",
475                         lineSeparator));
476         }
477     }
478 
479     private String getLineSeparatorFromPom() throws MojoExecutionException {
480         char current;
481         String lineSeparator = "";
482         try (InputStream is = new FileInputStream(this.project.getFile())) {
483             while (is.available() > 0) {
484                 current = (char) is.read();
485                 if ((current == '\n') || (current == '\r')) {
486                     lineSeparator += current;
487                     if (is.available() > 0) {
488                         char next = (char) is.read();
489                         if ((next != current) && ((next == '\r') || (next == '\n'))) {
490                             lineSeparator += next;
491                         }
492                     }
493                     return lineSeparator;
494                 }
495             }
496         } catch (IOException e) {
497             throw new MojoExecutionException("Failed to detect line separator of " + this.project.getFile(), e);
498         }
499 
500         return lineSeparator;
501     }
502 }