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.io.File;
23  import java.io.IOException;
24  import java.io.Writer;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.maven.artifact.Artifact;
32  import org.apache.maven.artifact.ArtifactUtils;
33  import org.apache.maven.model.Build;
34  import org.apache.maven.model.Dependency;
35  import org.apache.maven.model.Extension;
36  import org.apache.maven.model.Model;
37  import org.apache.maven.model.Plugin;
38  import org.apache.maven.model.Profile;
39  import org.apache.maven.model.ReportPlugin;
40  import org.apache.maven.model.Reporting;
41  import org.apache.maven.model.Scm;
42  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
43  import org.apache.maven.project.MavenProject;
44  import org.apache.maven.project.path.PathTranslator;
45  import org.apache.maven.scm.ScmException;
46  import org.apache.maven.scm.ScmFileSet;
47  import org.apache.maven.scm.command.add.AddScmResult;
48  import org.apache.maven.scm.provider.ScmProvider;
49  import org.apache.maven.scm.repository.ScmRepository;
50  import org.apache.maven.shared.release.ReleaseExecutionException;
51  import org.apache.maven.shared.release.ReleaseFailureException;
52  import org.apache.maven.shared.release.ReleaseResult;
53  import org.apache.maven.shared.release.config.ReleaseDescriptor;
54  import org.apache.maven.shared.release.env.ReleaseEnvironment;
55  import org.apache.maven.shared.release.scm.ReleaseScmCommandException;
56  import org.apache.maven.shared.release.scm.ScmTranslator;
57  import org.apache.maven.shared.release.util.ReleaseUtil;
58  import org.codehaus.plexus.util.IOUtil;
59  import org.codehaus.plexus.util.WriterFactory;
60  
61  /**
62   * Generate release POMs.
63   *
64   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
65   * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
66   * @plexus.component role="org.apache.maven.shared.release.phase.ReleasePhase" role-hint="generate-release-poms"
67   */
68  public class GenerateReleasePomsPhase
69      extends AbstractReleasePomsPhase
70  {
71      private static final String FINALNAME_EXPRESSION = "${project.artifactId}-${project.version}";
72  
73      /**
74       *
75       *
76       * @plexus.requirement
77       */
78      private PathTranslator pathTranslator;
79  
80      /**
81       * SCM URL translators mapped by provider name.
82       *
83       * @plexus.requirement role="org.apache.maven.shared.release.scm.ScmTranslator"
84       */
85      private Map<String, ScmTranslator> scmTranslators;
86  
87      /*
88       * @see org.apache.maven.shared.release.phase.ReleasePhase#execute(org.apache.maven.shared.release.config.ReleaseDescriptor,
89       *      org.apache.maven.settings.Settings, java.util.List)
90       */
91      public ReleaseResult execute( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
92                                    List<MavenProject> reactorProjects )
93          throws ReleaseExecutionException, ReleaseFailureException
94      {
95          return execute( releaseDescriptor, releaseEnvironment, reactorProjects, false );
96      }
97  
98      private ReleaseResult execute( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
99                                     List<MavenProject> reactorProjects, boolean simulate )
100         throws ReleaseExecutionException, ReleaseFailureException
101     {
102         ReleaseResult result = new ReleaseResult();
103 
104         if ( releaseDescriptor.isGenerateReleasePoms() )
105         {
106             logInfo( result, "Generating release POMs..." );
107 
108             generateReleasePoms( releaseDescriptor, releaseEnvironment, reactorProjects, simulate, result );
109         }
110         else
111         {
112             logInfo( result, "Not generating release POMs" );
113         }
114 
115         result.setResultCode( ReleaseResult.SUCCESS );
116 
117         return result;
118     }
119 
120     private void generateReleasePoms( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
121                                       List<MavenProject> reactorProjects, boolean simulate, ReleaseResult result )
122         throws ReleaseExecutionException, ReleaseFailureException
123     {
124         List<File> releasePoms = new ArrayList<File>();
125 
126         for ( MavenProject project : reactorProjects )
127         {
128             logInfo( result, "Generating release POM for '" + project.getName() + "'..." );
129 
130             releasePoms.add( generateReleasePom( project, releaseDescriptor, releaseEnvironment, reactorProjects,
131                                                  simulate, result ) );
132         }
133 
134         addReleasePomsToScm( releaseDescriptor, releaseEnvironment, reactorProjects, simulate, result, releasePoms );
135     }
136 
137     private File generateReleasePom( MavenProject project, ReleaseDescriptor releaseDescriptor,
138                                      ReleaseEnvironment releaseEnvironment, List<MavenProject> reactorProjects,
139                                      boolean simulate, ReleaseResult result )
140         throws ReleaseExecutionException, ReleaseFailureException
141     {
142         // create release pom
143 
144         Model releasePom = createReleaseModel( project, releaseDescriptor, releaseEnvironment, reactorProjects,
145                                                result );
146 
147         // write release pom to file
148 
149         MavenXpp3Writer pomWriter = new MavenXpp3Writer();
150 
151         File releasePomFile = ReleaseUtil.getReleasePom( project );
152 
153         // MRELEASE-273 : A release pom can be null
154         if ( releasePomFile == null )
155         {
156             throw new ReleaseExecutionException( "Cannot generate release POM : pom file is null" );
157         }
158 
159         Writer fileWriter = null;
160 
161         try
162         {
163             fileWriter = WriterFactory.newXmlWriter( releasePomFile );
164 
165             pomWriter.write( fileWriter, releasePom );
166         }
167         catch ( IOException exception )
168         {
169             throw new ReleaseExecutionException( "Cannot generate release POM", exception );
170         }
171         finally
172         {
173             IOUtil.close( fileWriter );
174         }
175 
176         return releasePomFile;
177     }
178 
179     private void addReleasePomsToScm( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
180                                       List<MavenProject> reactorProjects, boolean simulate, ReleaseResult result,
181                                       List<File> releasePoms )
182         throws ReleaseFailureException, ReleaseExecutionException
183     {
184         if ( simulate )
185         {
186             logInfo( result, "Full run would be adding " + releasePoms );
187         }
188         else
189         {
190             ScmRepository scmRepository = getScmRepository( releaseDescriptor, releaseEnvironment );
191             ScmProvider scmProvider = getScmProvider( scmRepository );
192 
193             MavenProject rootProject = ReleaseUtil.getRootProject( reactorProjects );
194             ScmFileSet scmFileSet = new ScmFileSet( rootProject.getFile().getParentFile(), releasePoms );
195 
196             try
197             {
198                 AddScmResult scmResult = scmProvider.add( scmRepository, scmFileSet );
199 
200                 if ( !scmResult.isSuccess() )
201                 {
202                     throw new ReleaseScmCommandException( "Cannot add release POM to SCM", scmResult );
203                 }
204             }
205             catch ( ScmException exception )
206             {
207                 throw new ReleaseExecutionException( "Cannot add release POM to SCM: " + exception.getMessage(),
208                                                      exception );
209             }
210         }
211     }
212 
213     private Model createReleaseModel( MavenProject project, ReleaseDescriptor releaseDescriptor,
214                                       ReleaseEnvironment releaseEnvironment, List<MavenProject> reactorProjects,
215                                       ReleaseResult result )
216         throws ReleaseFailureException, ReleaseExecutionException
217     {
218         Map<String, String> originalVersions = getOriginalVersionMap( releaseDescriptor, reactorProjects );
219         Map<String, String> mappedVersions = getNextVersionMap( releaseDescriptor );
220 
221         MavenProject releaseProject = new MavenProject( project );
222         Model releaseModel = releaseProject.getModel();
223 
224         // the release POM should reflect bits of these which were injected at build time...
225         // we don't need these polluting the POM.
226         releaseModel.setParent( null );
227         releaseModel.setProfiles( Collections.<Profile>emptyList() );
228         releaseModel.setDependencyManagement( null );
229         releaseProject.getBuild().setPluginManagement( null );
230 
231         // update project version
232         String projectVersion = releaseModel.getVersion();
233         String releaseVersion =
234             getNextVersion( mappedVersions, project.getGroupId(), project.getArtifactId(), projectVersion );
235         releaseModel.setVersion( releaseVersion );
236 
237         // update final name if implicit
238         if ( !FINALNAME_EXPRESSION.equals( releaseModel.getBuild().getFinalName() ) )
239         {
240             String originalFinalName = findOriginalFinalName( project );
241             
242             if ( originalFinalName == null )
243             {
244                 // as defined in super-pom
245                 originalFinalName = FINALNAME_EXPRESSION;
246             }
247             String finalName = ReleaseUtil.interpolate( originalFinalName, releaseModel );
248             
249             // still required?
250             if ( finalName.indexOf( Artifact.SNAPSHOT_VERSION ) != -1 )
251             {
252                 throw new ReleaseFailureException( "Cannot reliably adjust the finalName of project: "
253                                 + releaseProject.getId() );
254             }
255             
256             releaseModel.getBuild().setFinalName( finalName );
257         }        
258 
259         // update scm
260         Scm scm = releaseModel.getScm();
261 
262         if ( scm != null )
263         {
264             ScmRepository scmRepository = getScmRepository( releaseDescriptor, releaseEnvironment );
265             ScmTranslator scmTranslator = getScmTranslator( scmRepository );
266 
267             if ( scmTranslator != null )
268             {
269                 releaseModel.setScm( createReleaseScm( releaseModel.getScm(), scmTranslator, releaseDescriptor ) );
270             }
271             else
272             {
273                 String message = "No SCM translator found - skipping rewrite";
274 
275                 result.appendDebug( message );
276 
277                 getLogger().debug( message );
278             }
279         }
280 
281         // rewrite dependencies
282         releaseModel.setDependencies( createReleaseDependencies( originalVersions, mappedVersions, releaseProject ) );
283 
284         // rewrite plugins
285         releaseModel.getBuild().setPlugins( createReleasePlugins( originalVersions, mappedVersions, releaseProject ) );
286 
287         // rewrite reports
288         releaseModel.getReporting().setPlugins( createReleaseReportPlugins( originalVersions, mappedVersions,
289                                                                             releaseProject ) );
290 
291         // rewrite extensions
292         releaseModel.getBuild().setExtensions( createReleaseExtensions( originalVersions, mappedVersions,
293                                                                         releaseProject ) );
294 
295         pathTranslator.unalignFromBaseDirectory( releaseProject.getModel(), project.getFile().getParentFile() );
296 
297         return releaseModel;
298     }
299     
300     private String findOriginalFinalName( MavenProject project )
301     {
302         if ( project.getOriginalModel().getBuild() != null
303             && project.getOriginalModel().getBuild().getFinalName() != null )
304         {
305             return project.getOriginalModel().getBuild().getFinalName();
306         }
307         else if ( project.hasParent() )
308         {
309             return findOriginalFinalName( project.getParent() );
310         }
311         else
312         {
313             return null;
314         }
315     }
316 
317     public ReleaseResult simulate( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
318                                    List<MavenProject> reactorProjects )
319         throws ReleaseExecutionException, ReleaseFailureException
320     {
321         return execute( releaseDescriptor, releaseEnvironment, reactorProjects, true );
322     }
323 
324     protected Map<String, String> getOriginalVersionMap( ReleaseDescriptor releaseDescriptor,
325                                                          List<MavenProject> reactorProjects )
326     {
327         return releaseDescriptor.getOriginalVersions( reactorProjects );
328     }
329 
330     @SuppressWarnings( "unchecked" )
331     protected Map<String, String> getNextVersionMap( ReleaseDescriptor releaseDescriptor )
332     {
333         return releaseDescriptor.getReleaseVersions();
334     }
335 
336     private String getNextVersion( Map<String, String> mappedVersions, String groupId, String artifactId,
337                                    String version )
338         throws ReleaseFailureException
339     {
340         // TODO: share with RewritePomsForReleasePhase.rewriteVersion
341 
342         String id = ArtifactUtils.versionlessKey( groupId, artifactId );
343 
344         String nextVersion = mappedVersions.get( id );
345 
346         if ( nextVersion == null )
347         {
348             throw new ReleaseFailureException( "Version for '" + id + "' was not mapped" );
349         }
350 
351         return nextVersion;
352     }
353 
354     private ScmTranslator getScmTranslator( ScmRepository scmRepository )
355     {
356         return scmTranslators.get( scmRepository.getProvider() );
357     }
358 
359     private Scm createReleaseScm( Scm scm, ScmTranslator scmTranslator, ReleaseDescriptor releaseDescriptor )
360     {
361         // TODO: share with RewritePomsForReleasePhase.translateScm
362 
363         String tag = releaseDescriptor.getScmReleaseLabel();
364         String tagBase = releaseDescriptor.getScmTagBase();
365 
366         Scm releaseScm = new Scm();
367 
368         if ( scm.getConnection() != null )
369         {
370             String value = scmTranslator.translateTagUrl( scm.getConnection(), tag, tagBase );
371             releaseScm.setConnection( value );
372         }
373 
374         if ( scm.getDeveloperConnection() != null )
375         {
376             String value = scmTranslator.translateTagUrl( scm.getDeveloperConnection(), tag, tagBase );
377             releaseScm.setDeveloperConnection( value );
378         }
379 
380         if ( scm.getUrl() != null )
381         {
382             String value = scmTranslator.translateTagUrl( scm.getUrl(), tag, tagBase );
383             releaseScm.setUrl( value );
384         }
385 
386         if ( scm.getTag() != null )
387         {
388             String value = scmTranslator.resolveTag( scm.getTag() );
389             releaseScm.setTag( value );
390         }
391 
392         return releaseScm;
393     }
394 
395     private List<Dependency> createReleaseDependencies( Map<String, String> originalVersions,
396                                                         Map<String, String> mappedVersions, MavenProject project )
397         throws ReleaseFailureException
398     {
399         @SuppressWarnings( "unchecked" )
400         Set<Artifact> artifacts = project.getArtifacts();
401 
402         List<Dependency> releaseDependencies = null;
403 
404         if ( artifacts != null )
405         {
406             // make dependency order deterministic for tests (related to MNG-1412)
407             List<Artifact> orderedArtifacts = new ArrayList<Artifact>();
408             orderedArtifacts.addAll( artifacts );
409             Collections.sort( orderedArtifacts );
410 
411             releaseDependencies = new ArrayList<Dependency>();
412 
413             for ( Artifact artifact : orderedArtifacts )
414             {
415                 Dependency releaseDependency = new Dependency();
416 
417                 releaseDependency.setGroupId( artifact.getGroupId() );
418                 releaseDependency.setArtifactId( artifact.getArtifactId() );
419 
420                 String version = getReleaseVersion( originalVersions, mappedVersions, artifact );
421 
422                 releaseDependency.setVersion( version );
423                 releaseDependency.setType( artifact.getType() );
424                 releaseDependency.setScope( artifact.getScope() );
425                 releaseDependency.setClassifier( artifact.getClassifier() );
426 
427                 releaseDependencies.add( releaseDependency );
428             }
429         }
430 
431         return releaseDependencies;
432     }
433 
434     private String getReleaseVersion( Map<String, String> originalVersions, Map<String, String> mappedVersions,
435                                       Artifact artifact )
436         throws ReleaseFailureException
437     {
438         String key = ArtifactUtils.versionlessKey( artifact );
439 
440         String originalVersion = originalVersions.get( key );
441         String mappedVersion = mappedVersions.get( key );
442 
443         String version = artifact.getVersion();
444 
445         if ( version.equals( originalVersion ) )
446         {
447             if ( mappedVersion != null )
448             {
449                 version = mappedVersion;
450             }
451             else
452             {
453                 throw new ReleaseFailureException( "Version '" + version + "' for '" + key + "' was not mapped" );
454             }
455         }
456         else
457         {
458             if ( !ArtifactUtils.isSnapshot( version ) )
459             {
460                 version = artifact.getBaseVersion();
461             }
462         }
463 
464         return version;
465     }
466 
467     private List<Plugin> createReleasePlugins( Map<String, String> originalVersions,
468                                                Map<String, String> mappedVersions, MavenProject project )
469         throws ReleaseFailureException
470     {
471         List<Plugin> releasePlugins = null;
472 
473         // Use original - don't want the lifecycle introduced ones
474         Build build = project.getOriginalModel().getBuild();
475 
476         if ( build != null )
477         {
478             List<Plugin> plugins = build.getPlugins();
479 
480             if ( plugins != null )
481             {
482                 @SuppressWarnings( "unchecked" )
483                 Map<String, Artifact> artifactsById = project.getPluginArtifactMap();
484 
485                 releasePlugins = new ArrayList<Plugin>();
486 
487                 for ( Plugin plugin : plugins )
488                 {
489                     String id = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() );
490                     Artifact artifact = artifactsById.get( id );
491                     String version = getReleaseVersion( originalVersions, mappedVersions, artifact );
492 
493                     Plugin releasePlugin = new Plugin();
494                     releasePlugin.setGroupId( plugin.getGroupId() );
495                     releasePlugin.setArtifactId( plugin.getArtifactId() );
496                     releasePlugin.setVersion( version );
497                     releasePlugin.setExtensions( plugin.isExtensions() );
498                     releasePlugin.setExecutions( plugin.getExecutions() );
499                     releasePlugin.setDependencies( plugin.getDependencies() );
500                     releasePlugin.setGoals( plugin.getGoals() );
501                     releasePlugin.setInherited( plugin.getInherited() );
502                     releasePlugin.setConfiguration( plugin.getConfiguration() );
503 
504                     releasePlugins.add( releasePlugin );
505                 }
506             }
507         }
508 
509         return releasePlugins;
510     }
511 
512     private List<ReportPlugin> createReleaseReportPlugins( Map<String, String> originalVersions,
513                                                            Map<String, String> mappedVersions, MavenProject project )
514         throws ReleaseFailureException
515     {
516         List<ReportPlugin> releaseReportPlugins = null;
517 
518         Reporting reporting = project.getModel().getReporting();
519 
520         if ( reporting != null )
521         {
522             List<ReportPlugin> reportPlugins = reporting.getPlugins();
523 
524             if ( reportPlugins != null )
525             {
526                 @SuppressWarnings( "unchecked" )
527                 Map<String, Artifact> artifactsById = project.getReportArtifactMap();
528 
529                 releaseReportPlugins = new ArrayList<ReportPlugin>();
530 
531                 for ( ReportPlugin reportPlugin : reportPlugins )
532                 {
533                     String id = ArtifactUtils.versionlessKey( reportPlugin.getGroupId(), reportPlugin.getArtifactId() );
534                     Artifact artifact = artifactsById.get( id );
535                     String version = getReleaseVersion( originalVersions, mappedVersions, artifact );
536 
537                     ReportPlugin releaseReportPlugin = new ReportPlugin();
538                     releaseReportPlugin.setGroupId( reportPlugin.getGroupId() );
539                     releaseReportPlugin.setArtifactId( reportPlugin.getArtifactId() );
540                     releaseReportPlugin.setVersion( version );
541                     releaseReportPlugin.setInherited( reportPlugin.getInherited() );
542                     releaseReportPlugin.setConfiguration( reportPlugin.getConfiguration() );
543                     releaseReportPlugin.setReportSets( reportPlugin.getReportSets() );
544 
545                     releaseReportPlugins.add( releaseReportPlugin );
546                 }
547             }
548         }
549 
550         return releaseReportPlugins;
551     }
552 
553     private List<Extension> createReleaseExtensions( Map<String, String> originalVersions,
554                                                      Map<String, String> mappedVersions, MavenProject project )
555         throws ReleaseFailureException
556     {
557         List<Extension> releaseExtensions = null;
558 
559         // Use original - don't want the lifecycle introduced ones
560         Build build = project.getOriginalModel().getBuild();
561 
562         if ( build != null )
563         {
564             List<Extension> extensions = build.getExtensions();
565 
566             if ( extensions != null )
567             {
568                 releaseExtensions = new ArrayList<Extension>();
569 
570                 for ( Extension extension : extensions )
571                 {
572                     String id = ArtifactUtils.versionlessKey( extension.getGroupId(), extension.getArtifactId() );
573                     Artifact artifact = (Artifact) project.getExtensionArtifactMap().get( id );
574                     String version = getReleaseVersion( originalVersions, mappedVersions, artifact );
575 
576                     Extension releaseExtension = new Extension();
577                     releaseExtension.setGroupId( extension.getGroupId() );
578                     releaseExtension.setArtifactId( extension.getArtifactId() );
579                     releaseExtension.setVersion( version );
580 
581                     releaseExtensions.add( releaseExtension );
582                 }
583             }
584         }
585 
586         return releaseExtensions;
587     }
588 
589     /*
590      * @see org.apache.maven.shared.release.phase.AbstractReleasePhase#clean(java.util.List)
591      */
592     public ReleaseResult clean( List<MavenProject> reactorProjects )
593     {
594         ReleaseResult result = new ReleaseResult();
595 
596         for ( MavenProject project : reactorProjects )
597         {
598             File releasePom = ReleaseUtil.getReleasePom( project );
599 
600             // MRELEASE-273 : A release pom can be null
601             if ( releasePom != null && releasePom.exists() )
602             {
603                 logInfo( result, "Deleting release POM for '" + project.getName() + "'..." );
604 
605                 if ( !releasePom.delete() )
606                 {
607                     logWarn( result, "Cannot delete release POM: " + releasePom );
608                 }
609             }
610         }
611 
612         result.setResultCode( ReleaseResult.SUCCESS );
613 
614         return result;
615     }
616 }