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