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.io.File;
22  import java.nio.file.FileSystems;
23  import java.nio.file.Paths;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Set;
29  
30  import org.apache.maven.project.MavenProject;
31  import org.apache.maven.scm.ScmException;
32  import org.apache.maven.scm.ScmFileSet;
33  import org.apache.maven.scm.ScmVersion;
34  import org.apache.maven.scm.command.checkin.CheckInScmResult;
35  import org.apache.maven.scm.manager.NoSuchScmProviderException;
36  import org.apache.maven.scm.provider.ScmProvider;
37  import org.apache.maven.scm.repository.ScmRepository;
38  import org.apache.maven.scm.repository.ScmRepositoryException;
39  import org.apache.maven.shared.release.ReleaseExecutionException;
40  import org.apache.maven.shared.release.ReleaseFailureException;
41  import org.apache.maven.shared.release.ReleaseResult;
42  import org.apache.maven.shared.release.config.ReleaseDescriptor;
43  import org.apache.maven.shared.release.env.ReleaseEnvironment;
44  import org.apache.maven.shared.release.scm.ReleaseScmCommandException;
45  import org.apache.maven.shared.release.scm.ReleaseScmRepositoryException;
46  import org.apache.maven.shared.release.scm.ScmRepositoryConfigurator;
47  import org.apache.maven.shared.release.util.ReleaseUtil;
48  
49  import static java.util.Objects.requireNonNull;
50  
51  /**
52   * Holds the basic concept of committing changes to the current working copy.
53   *
54   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
55   * @author <a href="mailto:me@lcorneliussen.de">Lars Corneliussen</a>
56   */
57  public abstract class AbstractScmCommitPhase extends AbstractReleasePhase {
58      /**
59       * Tool that gets a configured SCM repository from release configuration.
60       */
61      protected final ScmRepositoryConfigurator scmRepositoryConfigurator;
62  
63      /**
64       * The getter in the descriptor for the comment.
65       */
66      protected final String descriptorCommentGetter;
67  
68      private final Set<String> exclusionPatterns = new HashSet<>();
69  
70      protected AbstractScmCommitPhase(
71              ScmRepositoryConfigurator scmRepositoryConfigurator, String descriptorCommentGetter) {
72          this.scmRepositoryConfigurator = requireNonNull(scmRepositoryConfigurator);
73          this.descriptorCommentGetter = requireNonNull(descriptorCommentGetter);
74      }
75  
76      @Override
77      public ReleaseResult execute(
78              ReleaseDescriptor releaseDescriptor,
79              ReleaseEnvironment releaseEnvironment,
80              List<MavenProject> reactorProjects)
81              throws ReleaseExecutionException, ReleaseFailureException {
82          ReleaseResult relResult = new ReleaseResult();
83  
84          validateConfiguration(releaseDescriptor);
85  
86          List<String> additionalExcludes = releaseDescriptor.getCheckModificationExcludes();
87  
88          if (additionalExcludes != null) {
89              exclusionPatterns.addAll(additionalExcludes);
90          }
91  
92          runLogic(releaseDescriptor, releaseEnvironment, reactorProjects, relResult, false);
93  
94          relResult.setResultCode(ReleaseResult.SUCCESS);
95  
96          return relResult;
97      }
98  
99      @Override
100     public ReleaseResult simulate(
101             ReleaseDescriptor releaseDescriptor,
102             ReleaseEnvironment releaseEnvironment,
103             List<MavenProject> reactorProjects)
104             throws ReleaseExecutionException, ReleaseFailureException {
105         ReleaseResult result = new ReleaseResult();
106 
107         validateConfiguration(releaseDescriptor);
108 
109         runLogic(releaseDescriptor, releaseEnvironment, reactorProjects, result, true);
110 
111         result.setResultCode(ReleaseResult.SUCCESS);
112         return result;
113     }
114 
115     /**
116      * <p>runLogic.</p>
117      *
118      * @param releaseDescriptor  a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
119      * @param releaseEnvironment a {@link org.apache.maven.shared.release.env.ReleaseEnvironment} object
120      * @param reactorProjects    a {@link java.util.List} object
121      * @param result             a {@link org.apache.maven.shared.release.ReleaseResult} object
122      * @param simulating         a boolean
123      * @throws org.apache.maven.shared.release.scm.ReleaseScmCommandException    if any.
124      * @throws org.apache.maven.shared.release.ReleaseExecutionException         if any.
125      * @throws org.apache.maven.shared.release.scm.ReleaseScmRepositoryException if any.
126      */
127     protected abstract void runLogic(
128             ReleaseDescriptor releaseDescriptor,
129             ReleaseEnvironment releaseEnvironment,
130             List<MavenProject> reactorProjects,
131             ReleaseResult result,
132             boolean simulating)
133             throws ReleaseScmCommandException, ReleaseExecutionException, ReleaseScmRepositoryException;
134 
135     /**
136      * <p>performCheckins.</p>
137      *
138      * @param releaseDescriptor  a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
139      * @param releaseEnvironment a {@link org.apache.maven.shared.release.env.ReleaseEnvironment} object
140      * @param reactorProjects    a {@link java.util.List} object
141      * @param message            a {@link java.lang.String} object
142      * @throws org.apache.maven.shared.release.scm.ReleaseScmRepositoryException if any.
143      * @throws org.apache.maven.shared.release.ReleaseExecutionException         if any.
144      * @throws org.apache.maven.shared.release.scm.ReleaseScmCommandException    if any.
145      */
146     protected void performCheckins(
147             ReleaseDescriptor releaseDescriptor,
148             ReleaseEnvironment releaseEnvironment,
149             List<MavenProject> reactorProjects,
150             String message)
151             throws ReleaseScmRepositoryException, ReleaseExecutionException, ReleaseScmCommandException {
152 
153         getLogger().info("Checking in modified POMs...");
154 
155         ScmRepository repository;
156         ScmProvider provider;
157         try {
158             repository = scmRepositoryConfigurator.getConfiguredRepository(
159                     releaseDescriptor, releaseEnvironment.getSettings());
160 
161             repository.getProviderRepository().setPushChanges(releaseDescriptor.isPushChanges());
162 
163             repository.getProviderRepository().setWorkItem(releaseDescriptor.getWorkItem());
164 
165             provider = scmRepositoryConfigurator.getRepositoryProvider(repository);
166         } catch (ScmRepositoryException e) {
167             throw new ReleaseScmRepositoryException(e.getMessage(), e.getValidationMessages());
168         } catch (NoSuchScmProviderException e) {
169             throw new ReleaseExecutionException("Unable to configure SCM repository: " + e.getMessage(), e);
170         }
171 
172         if (releaseDescriptor.isCommitByProject()) {
173             for (MavenProject project : reactorProjects) {
174                 List<File> pomFiles = createPomFiles(releaseDescriptor, project);
175                 ScmFileSet fileSet = new ScmFileSet(project.getFile().getParentFile(), pomFiles);
176 
177                 checkin(provider, repository, fileSet, releaseDescriptor, message);
178             }
179         } else {
180             List<File> pomFiles = createPomFiles(releaseDescriptor, reactorProjects);
181 
182             if (!pomFiles.isEmpty()) {
183                 ScmFileSet fileSet = new ScmFileSet(new File(releaseDescriptor.getWorkingDirectory()), pomFiles);
184 
185                 checkin(provider, repository, fileSet, releaseDescriptor, message);
186             }
187         }
188     }
189 
190     private void checkin(
191             ScmProvider provider,
192             ScmRepository repository,
193             ScmFileSet fileSet,
194             ReleaseDescriptor releaseDescriptor,
195             String message)
196             throws ReleaseExecutionException, ReleaseScmCommandException {
197         CheckInScmResult result;
198         try {
199             result = provider.checkIn(repository, fileSet, (ScmVersion) null, message);
200         } catch (ScmException e) {
201             throw new ReleaseExecutionException("An error is occurred in the checkin process: " + e.getMessage(), e);
202         }
203 
204         if (!result.isSuccess()) {
205             throw new ReleaseScmCommandException("Unable to commit files", result);
206         }
207         if (releaseDescriptor.isRemoteTagging()) {
208             releaseDescriptor.setScmReleasedPomRevision(result.getScmRevision());
209         }
210     }
211 
212     /**
213      * <p>simulateCheckins.</p>
214      *
215      * @param releaseDescriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
216      * @param reactorProjects   a {@link java.util.List} object
217      * @param result            a {@link org.apache.maven.shared.release.ReleaseResult} object
218      * @param message           a {@link java.lang.String} object
219      */
220     protected void simulateCheckins(
221             ReleaseDescriptor releaseDescriptor,
222             List<MavenProject> reactorProjects,
223             ReleaseResult result,
224             String message) {
225         Collection<File> pomFiles = createPomFiles(releaseDescriptor, reactorProjects);
226         logInfo(result, "Full run would be commit " + pomFiles.size() + " files with message: '" + message + "'");
227     }
228 
229     /**
230      * <p>validateConfiguration.</p>
231      *
232      * @param releaseDescriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
233      * @throws org.apache.maven.shared.release.ReleaseFailureException if any.
234      */
235     protected void validateConfiguration(ReleaseDescriptor releaseDescriptor) throws ReleaseFailureException {
236         if (releaseDescriptor.getScmReleaseLabel() == null) {
237             throw new ReleaseFailureException("A release label is required for committing");
238         }
239     }
240 
241     /**
242      * <p>createMessage.</p>
243      *
244      * @param reactorProjects   a {@link java.util.List} object
245      * @param releaseDescriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
246      * @return a {@link java.lang.String} object
247      * @throws org.apache.maven.shared.release.ReleaseExecutionException if any.
248      */
249     protected String createMessage(List<MavenProject> reactorProjects, ReleaseDescriptor releaseDescriptor)
250             throws ReleaseExecutionException {
251         String comment;
252         boolean branch = false;
253         if ("getScmReleaseCommitComment".equals(descriptorCommentGetter)) {
254             comment = releaseDescriptor.getScmReleaseCommitComment();
255         } else if ("getScmDevelopmentCommitComment".equals(descriptorCommentGetter)) {
256             comment = releaseDescriptor.getScmDevelopmentCommitComment();
257         } else if ("getScmBranchCommitComment".equals(descriptorCommentGetter)) {
258             comment = releaseDescriptor.getScmBranchCommitComment();
259             branch = true;
260         } else if ("getScmRollbackCommitComment".equals(descriptorCommentGetter)) {
261             comment = releaseDescriptor.getScmRollbackCommitComment();
262         } else {
263             throw new ReleaseExecutionException(
264                     "Invalid configuration of descriptorCommentGetter='" + descriptorCommentGetter + "'");
265         }
266 
267         MavenProject project = ReleaseUtil.getRootProject(reactorProjects);
268         comment = comment.replace(
269                 "@{prefix}", releaseDescriptor.getScmCommentPrefix().trim());
270         comment = comment.replace("@{groupId}", project.getGroupId());
271         comment = comment.replace("@{artifactId}", project.getArtifactId());
272         if (branch) {
273             comment = comment.replace("@{branchName}", releaseDescriptor.getScmReleaseLabel());
274         } else {
275             comment = comment.replace("@{releaseLabel}", releaseDescriptor.getScmReleaseLabel());
276         }
277         return comment;
278     }
279 
280     /**
281      * <p>createPomFiles.</p>
282      *
283      * @param releaseDescriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
284      * @param project           a {@link org.apache.maven.project.MavenProject} object
285      * @return a {@link java.util.List} object
286      */
287     protected static List<File> createPomFiles(ReleaseDescriptor releaseDescriptor, MavenProject project) {
288         List<File> pomFiles = new ArrayList<>();
289 
290         pomFiles.add(ReleaseUtil.getStandardPom(project));
291 
292         if (releaseDescriptor.isGenerateReleasePoms() && !releaseDescriptor.isSuppressCommitBeforeTagOrBranch()) {
293             pomFiles.add(ReleaseUtil.getReleasePom(project));
294         }
295 
296         return pomFiles;
297     }
298 
299     /**
300      * <p>createPomFiles.</p>
301      *
302      * @param releaseDescriptor a {@link org.apache.maven.shared.release.config.ReleaseDescriptor} object
303      * @param reactorProjects   a {@link java.util.List} object
304      * @return a {@link java.util.List} object
305      */
306     protected List<File> createPomFiles(ReleaseDescriptor releaseDescriptor, List<MavenProject> reactorProjects) {
307 
308         List<File> pomFiles = new ArrayList<>();
309         for (MavenProject project : reactorProjects) {
310 
311             final String path = project.getFile().getPath();
312 
313             boolean isExcludedPathFound = exclusionPatterns.stream()
314                     .anyMatch(exclusionPattern -> FileSystems.getDefault()
315                             .getPathMatcher("glob:" + exclusionPattern)
316                             .matches(Paths.get(path)));
317             if (!isExcludedPathFound) {
318                 pomFiles.addAll(createPomFiles(releaseDescriptor, project));
319             }
320         }
321         return pomFiles;
322     }
323 }