View Javadoc
1   package org.apache.maven.plugins.scmpublish;
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.nio.file.Files;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collection;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.TreeSet;
33  
34  import org.apache.commons.io.FileUtils;
35  import org.apache.commons.io.FilenameUtils;
36  import org.apache.commons.lang3.time.DurationFormatUtils;
37  import org.apache.maven.plugin.AbstractMojo;
38  import org.apache.maven.plugin.MojoExecutionException;
39  import org.apache.maven.plugin.MojoFailureException;
40  import org.apache.maven.plugins.annotations.Component;
41  import org.apache.maven.plugins.annotations.Parameter;
42  import org.apache.maven.scm.CommandParameter;
43  import org.apache.maven.scm.CommandParameters;
44  import org.apache.maven.scm.ScmBranch;
45  import org.apache.maven.scm.ScmException;
46  import org.apache.maven.scm.ScmFileSet;
47  import org.apache.maven.scm.ScmResult;
48  import org.apache.maven.scm.command.add.AddScmResult;
49  import org.apache.maven.scm.command.checkin.CheckInScmResult;
50  import org.apache.maven.scm.manager.NoSuchScmProviderException;
51  import org.apache.maven.scm.manager.ScmManager;
52  import org.apache.maven.scm.provider.ScmProvider;
53  import org.apache.maven.scm.provider.ScmUrlUtils;
54  import org.apache.maven.scm.provider.svn.AbstractSvnScmProvider;
55  import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
56  import org.apache.maven.scm.repository.ScmRepository;
57  import org.apache.maven.scm.repository.ScmRepositoryException;
58  import org.apache.maven.settings.Server;
59  import org.apache.maven.settings.Settings;
60  import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
61  import org.apache.maven.settings.crypto.SettingsDecrypter;
62  import org.apache.maven.settings.crypto.SettingsDecryptionRequest;
63  import org.apache.maven.settings.crypto.SettingsDecryptionResult;
64  import org.apache.maven.shared.release.config.ReleaseDescriptor;
65  import org.apache.maven.shared.release.scm.ScmRepositoryConfigurator;
66  import org.apache.maven.shared.utils.logging.MessageUtils;
67  
68  /**
69   * Base class for the scm-publish mojos.
70   */
71  public abstract class AbstractScmPublishMojo
72      extends AbstractMojo
73  {
74      // CHECKSTYLE_OFF: LineLength
75      /**
76       * Location of the scm publication tree:
77       * <code>scm:&lt;scm_provider&gt;&lt;delimiter&gt;&lt;provider_specific_part&gt;</code>.
78       * Example:
79       * <code>scm:svn:https://svn.apache.org/repos/infra/websites/production/maven/content/plugins/maven-scm-publish-plugin-LATEST/</code>
80       */
81      // CHECKSTYLE_ON: LineLength
82      @Parameter ( property = "scmpublish.pubScmUrl", defaultValue = "${project.distributionManagement.site.url}",
83                   required = true )
84      protected String pubScmUrl;
85  
86      /**
87       * If the checkout directory exists and this flag is activated, the plugin will try an SCM-update instead
88       * of delete then checkout.
89       */
90      @Parameter ( property = "scmpublish.tryUpdate", defaultValue = "false" )
91      protected boolean tryUpdate;
92  
93      // CHECKSTYLE_OFF: LineLength
94     /**
95       * Location where the scm check-out is done. By default, scm checkout is done in build (target) directory,
96       * which is deleted on every <code>mvn clean</code>. To avoid this and get better performance, configure
97       * this location outside build structure and set <code>tryUpdate</code> to <code>true</code>.
98       * See <a href="http://maven.apache.org/plugins/maven-scm-publish-plugin/various-tips.html#Improving_SCM_Checkout_Performance">
99       * Improving SCM Checkout Performance</a> for more information.
100      */
101     // CHECKSTYLE_ON: LineLength
102     @Parameter ( property = "scmpublish.checkoutDirectory",
103                  defaultValue = "${project.build.directory}/scmpublish-checkout" )
104     protected File checkoutDirectory;
105 
106     /**
107      * Location where the content is published inside the <code>${checkoutDirectory}</code>.
108      * By default, content is copyed at the root of <code>${checkoutDirectory}</code>.
109      */
110     @Parameter ( property = "scmpublish.subDirectory" )
111     protected String subDirectory;
112 
113     /**
114      * Display list of added, deleted, and changed files, but do not do any actual SCM operations.
115      */
116     @Parameter ( property = "scmpublish.dryRun" )
117     private boolean dryRun;
118 
119     /**
120      * Set this to 'true' to skip site deployment.
121      */
122     @Parameter ( property = "scmpublish.skipDeploy", alias = "maven.site.deploy.skip", defaultValue = "false" )
123     private boolean skipDeployement;
124 
125     /**
126      * Run add and delete commands, but leave the actually checkin for the user to run manually.
127      */
128     @Parameter ( property = "scmpublish.skipCheckin" )
129     private boolean skipCheckin;
130 
131     /**
132      * SCM log/checkin comment for this publication.
133      */
134     @Parameter ( property = "scmpublish.checkinComment", defaultValue = "Site checkin for project ${project.name}" )
135     private String checkinComment;
136 
137     /**
138      * Patterns to exclude from the scm tree.
139      */
140     @Parameter
141     protected String excludes;
142 
143     /**
144      * Patterns to include in the scm tree.
145      */
146     @Parameter
147     protected String includes;
148 
149     /**
150      * List of SCM provider implementations.
151      * Key is the provider type, eg. <code>cvs</code>.
152      * Value is the provider implementation (the role-hint of the provider), eg. <code>cvs</code> or
153      * <code>cvs_native</code>.
154      * @see ScmManager.setScmProviderImplementation
155      */
156     @Parameter
157     private Map<String, String> providerImplementations;
158 
159     /**
160      * The SCM manager.
161      */
162     @Component
163     private ScmManager scmManager;
164 
165     /**
166      * Tool that gets a configured SCM repository from release configuration.
167      */
168     @Component
169     protected ScmRepositoryConfigurator scmRepositoryConfigurator;
170     
171     /**
172      * The serverId specified in the settings.xml, which should be used for the authentication.
173      */
174     @Parameter
175     private String serverId;
176 
177     /**
178      * The SCM username to use.
179      */
180     @Parameter ( property = "username" )
181     protected String username;
182 
183     /**
184      * The SCM password to use.
185      */
186     @Parameter ( property = "password" )
187     protected String password;
188 
189     /**
190      * Use a local checkout instead of doing a checkout from the upstream repository.
191      * <b>WARNING</b>: This will only work with distributed SCMs which support the file:// protocol.
192      * TODO: we should think about having the defaults for the various SCM providers provided via Modello!
193      */
194     @Parameter ( property = "localCheckout", defaultValue = "false" )
195     protected boolean localCheckout;
196 
197     /**
198      * The outputEncoding parameter of the site plugin. This plugin will corrupt your site
199      * if this does not match the value used by the site plugin.
200      */
201     @Parameter ( property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}" )
202     protected String siteOutputEncoding;
203 
204     /**
205      * Do not delete files to the scm
206      */
207     @Parameter ( property = "scmpublish.skipDeletedFiles", defaultValue = "false" )
208     protected boolean skipDeletedFiles;
209 
210     /**
211      * Add each directory in a separated SCM command: this can be necessary if SCM does not support
212      * adding subdirectories in one command.
213      */
214     @Parameter( defaultValue = "false" )
215     protected boolean addUniqueDirectory;
216 
217     /**
218      */
219     @Parameter ( defaultValue = "${basedir}", readonly = true )
220     protected File basedir;
221 
222     /**
223      */
224     @Parameter( defaultValue = "${settings}", readonly = true, required = true )
225     protected Settings settings;
226     
227     @Component
228     private SettingsDecrypter settingsDecrypter;
229  
230 
231     /**
232      * Collections of paths not to delete when checking content to delete.
233      * If your site has subdirectories published by an other mechanism/build
234      */
235     @Parameter
236     protected String[] ignorePathsToDelete;
237 
238     /**
239      * SCM branch to use. For github, you must configure with <code>gh-pages</code>.
240      */
241     @Parameter ( property = "scmpublish.scm.branch" )
242     protected String scmBranch;
243 
244     /**
245      * Configure svn automatic remote url creation.
246      */
247     @Parameter ( property = "scmpublish.automaticRemotePathCreation", defaultValue = "true" )
248     protected boolean automaticRemotePathCreation;
249 
250     /**
251      * Filename extensions of files which need new line normalization.
252      */
253     private static final String[] NORMALIZE_EXTENSIONS = { "html", "css", "js" };
254 
255     /**
256      * Extra file extensions to normalize line ending (will be added to default
257      * <code>html</code>,<code>css</code>,<code>js</code> list)
258      */
259     @Parameter
260     protected String[] extraNormalizeExtensions;
261 
262     private Set<String> normalizeExtensions;
263 
264     protected ScmProvider scmProvider;
265 
266     protected ScmRepository scmRepository;
267 
268     protected void logInfo( String format, Object... params )
269     {
270         getLog().info( String.format( format, params ) );
271     }
272 
273     protected void logWarn( String format, Object... params )
274     {
275         getLog().warn( String.format( format, params ) );
276     }
277 
278     protected void logError( String format, Object... params )
279     {
280         getLog().error( String.format( format, params ) );
281     }
282 
283     private File relativize( File base, File file )
284     {
285         return new File( base.toURI().relativize( file.toURI() ).getPath() );
286     }
287 
288     protected boolean requireNormalizeNewlines( File f )
289         throws IOException
290     {
291         if ( normalizeExtensions == null )
292         {
293             normalizeExtensions = new HashSet<String>( Arrays.asList( NORMALIZE_EXTENSIONS ) );
294             if ( extraNormalizeExtensions != null )
295             {
296                 normalizeExtensions.addAll( Arrays.asList( extraNormalizeExtensions ) );
297             }
298         }
299 
300         return FilenameUtils.isExtension( f.getName(), normalizeExtensions );
301     }
302 
303     private ReleaseDescriptor setupScm()
304         throws ScmRepositoryException, NoSuchScmProviderException
305     {
306         String scmUrl;
307         if ( localCheckout )
308         {
309             // in the release phase we have to change the checkout URL
310             // to do a local checkout instead of going over the network.
311 
312             String provider = ScmUrlUtils.getProvider( pubScmUrl );
313             String delimiter = ScmUrlUtils.getDelimiter( pubScmUrl );
314             
315             String providerPart = "scm:" + provider + delimiter;
316 
317             // X TODO: also check the information from releaseDescriptor.getScmRelativePathProjectDirectory()
318             // X TODO: in case our toplevel git directory has no pom.
319             // X TODO: fix pathname once I understand this.
320             scmUrl = providerPart + "file://" + "target/localCheckout";
321             logInfo( "Performing a LOCAL checkout from " + scmUrl );
322         }
323 
324         ReleaseDescriptor releaseDescriptor = new ReleaseDescriptor();
325         releaseDescriptor.setInteractive( settings.isInteractiveMode() );
326 
327         if ( username == null || password == null )
328         {
329             for ( Server server : settings.getServers() )
330             {
331                 if ( server.getId().equals( serverId ) )
332                 {
333                     SettingsDecryptionRequest decryptionRequest = new DefaultSettingsDecryptionRequest( server );
334 
335                     SettingsDecryptionResult decryptionResult = settingsDecrypter.decrypt( decryptionRequest );
336 
337                     if ( !decryptionResult.getProblems().isEmpty() )
338                     {
339                         // todo throw exception?
340                     }
341 
342                     if ( username == null )
343                     {
344                         username = decryptionResult.getServer().getUsername();
345                     }
346 
347                     if ( password == null )
348                     {
349                         password = decryptionResult.getServer().getPassword();
350                     }
351 
352                     break;
353                 }
354             }
355         }
356 
357         releaseDescriptor.setScmPassword( password );
358         releaseDescriptor.setScmUsername( username );
359 
360         releaseDescriptor.setWorkingDirectory( basedir.getAbsolutePath() );
361         releaseDescriptor.setLocalCheckout( localCheckout );
362         releaseDescriptor.setScmSourceUrl( pubScmUrl );
363 
364         if ( providerImplementations != null )
365         {
366             for ( Map.Entry<String, String> providerEntry : providerImplementations.entrySet() )
367             {
368                 logInfo( "Changing the default '%s' provider implementation to '%s'.", providerEntry.getKey(),
369                          providerEntry.getValue() );
370                 scmManager.setScmProviderImplementation( providerEntry.getKey(), providerEntry.getValue() );
371             }
372         }
373 
374         scmRepository = scmRepositoryConfigurator.getConfiguredRepository( releaseDescriptor, settings );
375 
376         scmProvider = scmRepositoryConfigurator.getRepositoryProvider( scmRepository );
377 
378         return releaseDescriptor;
379     }
380 
381     protected void checkoutExisting()
382         throws MojoExecutionException
383     {
384 
385         if ( scmProvider instanceof AbstractSvnScmProvider )
386         {
387             checkCreateRemoteSvnPath();
388         }
389 
390         logInfo( MessageUtils.buffer().strong( "%s" ) + " the pub tree from " + MessageUtils.buffer().strong( "%s" )
391             + " into %s", ( tryUpdate ? "Updating" : "Checking out" ), pubScmUrl, checkoutDirectory );
392 
393         if ( checkoutDirectory.exists() && !tryUpdate )
394 
395         {
396             try
397             {
398                 FileUtils.deleteDirectory( checkoutDirectory );
399             }
400             catch ( IOException e )
401             {
402                 logError( e.getMessage() );
403 
404                 throw new MojoExecutionException( "Unable to remove old checkout directory: " + e.getMessage(), e );
405             }
406         }
407 
408         boolean forceCheckout = false;
409 
410         if ( !checkoutDirectory.exists() )
411 
412         {
413             if ( tryUpdate )
414             {
415                 logInfo( "TryUpdate is configured but no local copy currently available: forcing checkout." );
416             }
417             checkoutDirectory.mkdirs();
418             forceCheckout = true;
419         }
420 
421         try
422         {
423             ScmFileSet fileSet = new ScmFileSet( checkoutDirectory, includes, excludes );
424 
425             ScmBranch branch = ( scmBranch == null ) ? null : new ScmBranch( scmBranch );
426 
427             ScmResult scmResult = null;
428             if ( tryUpdate && !forceCheckout )
429             {
430                 scmResult = scmProvider.update( scmRepository, fileSet, branch );
431             }
432             else
433             {
434                 int attempt = 0;
435                 while ( scmResult == null )
436                 {
437                     try
438                     {
439                         scmResult = scmProvider.checkOut( scmRepository, fileSet, branch );
440                     }
441                     catch ( ScmException e )
442                     {
443                         // give it max 2 times to retry
444                         if ( attempt++ < 2 )
445                         {
446                             try
447                             {
448                                 // wait 3 seconds
449                                 Thread.sleep( 3 * 1000 );
450                             }
451                             catch ( InterruptedException ie )
452                             {
453                                 // noop
454                             }
455                         }
456                         else
457                         {
458                             throw e;
459                         }
460                     }
461                 }
462             }
463             checkScmResult( scmResult, "check out from SCM" );
464         }
465         catch ( ScmException e )
466         {
467             logError( e.getMessage() );
468 
469             throw new MojoExecutionException( "An error occurred during the checkout process: " + e.getMessage(), e );
470         }
471         catch ( IOException e )
472         {
473             logError( e.getMessage() );
474 
475             throw new MojoExecutionException( "An error occurred during the checkout process: " + e.getMessage(), e );
476         }
477     }
478 
479     private void checkCreateRemoteSvnPath()
480         throws MojoExecutionException
481     {
482         getLog().debug( "AbstractSvnScmProvider used, so we can check if remote url exists and eventually create it." );
483         AbstractSvnScmProvider svnScmProvider = (AbstractSvnScmProvider) scmProvider;
484 
485         try
486         {
487             boolean remoteExists = svnScmProvider.remoteUrlExist( scmRepository.getProviderRepository(), null );
488 
489             if ( remoteExists )
490             {
491                 return;
492             }
493         }
494         catch ( ScmException e )
495         {
496             throw new MojoExecutionException( e.getMessage(), e );
497         }
498 
499         String remoteUrl = ( (SvnScmProviderRepository) scmRepository.getProviderRepository() ).getUrl();
500 
501         if ( !automaticRemotePathCreation )
502         {
503             // olamy: return ?? that will fail during checkout IMHO :-)
504             logWarn( "Remote svn url %s does not exist and automatic remote path creation disabled.",
505                      remoteUrl );
506             return;
507         }
508 
509         logInfo( "Remote svn url %s does not exist: creating.", remoteUrl );
510 
511         File baseDir = null;
512         try
513         {
514 
515             // create a temporary directory for svnexec
516             baseDir = Files.createTempDirectory( "scm" ).toFile();
517 
518             // to prevent fileSet cannot be empty
519             ScmFileSet scmFileSet = new ScmFileSet( baseDir, new File( "" ) );
520 
521             CommandParameters commandParameters = new CommandParameters();
522             commandParameters.setString( CommandParameter.SCM_MKDIR_CREATE_IN_LOCAL, Boolean.FALSE.toString() );
523             commandParameters.setString( CommandParameter.MESSAGE, "Automatic svn path creation: " + remoteUrl );
524             svnScmProvider.mkdir( scmRepository.getProviderRepository(), scmFileSet, commandParameters );
525 
526             // new remote url so force checkout!
527             if ( checkoutDirectory.exists() )
528             {
529                 FileUtils.deleteDirectory( checkoutDirectory );
530             }
531         }
532         catch ( IOException e )
533         {
534             throw new MojoExecutionException( e.getMessage(), e );
535         }
536         catch ( ScmException e )
537         {
538             throw new MojoExecutionException( e.getMessage(), e );
539         }
540         finally
541         {
542             if ( baseDir != null )
543             {
544                 try
545                 {
546                     FileUtils.forceDeleteOnExit( baseDir );
547                 }
548                 catch ( IOException e )
549                 {
550                     throw new MojoExecutionException( e.getMessage(), e );
551                 }
552             }
553         }
554     }
555 
556     public void execute()
557         throws MojoExecutionException, MojoFailureException
558     {
559         if ( skipDeployement )
560         {
561             getLog().info( "scmpublish.skipDeploy = true: Skipping site deployment" );
562             return;
563         }
564 
565         // setup the scm plugin with help from release plugin utilities
566         try
567         {
568             setupScm();
569         }
570         catch ( ScmRepositoryException e )
571         {
572             throw new MojoExecutionException( e.getMessage(), e );
573         }
574         catch ( NoSuchScmProviderException e )
575         {
576             throw new MojoExecutionException( e.getMessage(), e );
577         }
578 
579         boolean tmpCheckout = false;
580 
581         if ( checkoutDirectory.getPath().contains( "${project." ) )
582         {
583             try
584             {
585                 tmpCheckout = true;
586                 checkoutDirectory = File.createTempFile( "maven-scm-publish", ".checkout" );
587                 checkoutDirectory.delete();
588                 checkoutDirectory.mkdir();
589             }
590             catch ( IOException ioe )
591             {
592                 throw new MojoExecutionException( ioe.getMessage(), ioe );
593             }
594         }
595 
596         try
597         {
598             scmPublishExecute();
599         }
600         finally
601         {
602             if ( tmpCheckout )
603             {
604                 FileUtils.deleteQuietly( checkoutDirectory );
605             }
606         }
607     }
608 
609     /**
610      * Check-in content from scm checkout.
611      *
612      * @throws MojoExecutionException in case of issue
613      */
614     protected void checkinFiles()
615         throws MojoExecutionException
616     {
617         if ( skipCheckin )
618         {
619             return;
620         }
621 
622         ScmFileSet updatedFileSet = new ScmFileSet( checkoutDirectory );
623         try
624         {
625             long start = System.currentTimeMillis();
626 
627             CheckInScmResult checkinResult =
628                 checkScmResult( scmProvider.checkIn( scmRepository, updatedFileSet, new ScmBranch( scmBranch ),
629                                                      checkinComment ), "check-in files to SCM" );
630 
631             logInfo( "Checked in %d file(s) to revision %s in %s", checkinResult.getCheckedInFiles().size(),
632                      checkinResult.getScmRevision(),
633                      DurationFormatUtils.formatPeriod( start, System.currentTimeMillis(), "H' h 'm' m 's' s'" ) );
634         }
635         catch ( ScmException e )
636         {
637             throw new MojoExecutionException( "Failed to perform SCM checkin", e );
638         }
639     }
640 
641     protected void deleteFiles( Collection<File> deleted )
642         throws MojoExecutionException
643     {
644         if ( skipDeletedFiles )
645         {
646             logInfo( "Deleting files is skipped." );
647             return;
648         }
649         List<File> deletedList = new ArrayList<File>();
650         for ( File f : deleted )
651         {
652             deletedList.add( relativize( checkoutDirectory, f ) );
653         }
654         ScmFileSet deletedFileSet = new ScmFileSet( checkoutDirectory, deletedList );
655         try
656         {
657             getLog().info( "Deleting files: " + deletedList );
658 
659             checkScmResult( scmProvider.remove( scmRepository, deletedFileSet, "Deleting obsolete site files." ),
660                             "delete files from SCM" );
661         }
662         catch ( ScmException e )
663         {
664             throw new MojoExecutionException( "Failed to delete removed files to SCM", e );
665         }
666     }
667 
668     /**
669      * Add files to scm.
670      *
671      * @param added files to be added
672      * @throws MojoFailureException in case of issue
673      * @throws MojoExecutionException in case of issue
674      */
675     protected void addFiles( Collection<File> added )
676         throws MojoFailureException, MojoExecutionException
677     {
678         List<File> addedList = new ArrayList<File>();
679         Set<File> createdDirs = new HashSet<File>();
680         Set<File> dirsToAdd = new TreeSet<File>();
681 
682         createdDirs.add( relativize( checkoutDirectory, checkoutDirectory ) );
683 
684         for ( File f : added )
685         {
686             for ( File dir = f.getParentFile(); !dir.equals( checkoutDirectory ); dir = dir.getParentFile() )
687             {
688                 File relativized = relativize( checkoutDirectory, dir );
689                 //  we do the best we can with the directories
690                 if ( createdDirs.add( relativized ) )
691                 {
692                     dirsToAdd.add( relativized );
693                 }
694                 else
695                 {
696                     break;
697                 }
698             }
699             addedList.add( relativize( checkoutDirectory, f ) );
700         }
701 
702         if ( addUniqueDirectory )
703         { // add one directory at a time
704             for ( File relativized : dirsToAdd )
705             {
706                 try
707                 {
708                     ScmFileSet fileSet = new ScmFileSet( checkoutDirectory, relativized );
709                     getLog().info( "scm add directory: " + relativized );
710                     AddScmResult addDirResult = scmProvider.add( scmRepository, fileSet, "Adding directory" );
711                     if ( !addDirResult.isSuccess() )
712                     {
713                         getLog().warn( " Error adding directory " + relativized + ": "
714                                            + addDirResult.getCommandOutput() );
715                     }
716                 }
717                 catch ( ScmException e )
718                 {
719                     //
720                 }
721             }
722         }
723         else
724         { // add all directories in one command
725             try
726             {
727                 List<File> dirs = new ArrayList<File>( dirsToAdd );
728                 ScmFileSet fileSet = new ScmFileSet( checkoutDirectory, dirs );
729                 getLog().info( "scm add directories: " + dirs );
730                 AddScmResult addDirResult = scmProvider.add( scmRepository, fileSet, "Adding directories" );
731                 if ( !addDirResult.isSuccess() )
732                 {
733                     getLog().warn( " Error adding directories " + dirs + ": " + addDirResult.getCommandOutput() );
734                 }
735             }
736             catch ( ScmException e )
737             {
738                 //
739             }
740         }
741 
742         // remove directories already added !
743         addedList.removeAll( dirsToAdd );
744 
745         ScmFileSet addedFileSet = new ScmFileSet( checkoutDirectory, addedList );
746         getLog().info( "scm add files: " + addedList );
747         try
748         {
749                 CommandParameters commandParameters = new CommandParameters();
750                 commandParameters.setString( CommandParameter.MESSAGE, "Adding new site files." );
751                 commandParameters.setString( CommandParameter.FORCE_ADD, Boolean.TRUE.toString() );
752                 checkScmResult( scmProvider.add( scmRepository, addedFileSet, commandParameters ),
753                                 "add new files to SCM" );
754         }
755         catch ( ScmException e )
756         {
757             throw new MojoExecutionException( "Failed to add new files to SCM", e );
758         }
759     }
760 
761     private <T extends ScmResult> T checkScmResult( T result, String failure )
762         throws MojoExecutionException
763     {
764         if ( !result.isSuccess() )
765         {
766             String msg = "Failed to " + failure + ": " + result.getProviderMessage() + " " + result.getCommandOutput();
767             logError( msg );
768             throw new MojoExecutionException( msg );
769         }
770         return result;
771     }
772 
773     public boolean isDryRun()
774     {
775         return dryRun;
776     }
777 
778     public abstract void scmPublishExecute()
779         throws MojoExecutionException, MojoFailureException;
780 
781     public void setPubScmUrl( String pubScmUrl )
782     {
783         // Fix required for Windows, which fit other OS as well
784         if ( pubScmUrl.startsWith( "scm:svn:" ) )
785         {
786             pubScmUrl = pubScmUrl.replaceFirst( "file:/[/]*", "file:///" );
787         }
788 
789         this.pubScmUrl = pubScmUrl;
790     }
791 
792 }