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 org.apache.commons.io.FileUtils;
23  import org.apache.commons.io.IOUtils;
24  import org.apache.commons.io.filefilter.NameFileFilter;
25  import org.apache.commons.io.filefilter.NotFileFilter;
26  import org.apache.maven.plugin.MojoExecutionException;
27  import org.apache.maven.plugin.MojoFailureException;
28  import org.apache.maven.plugins.annotations.Component;
29  import org.apache.maven.plugins.annotations.Mojo;
30  import org.apache.maven.plugins.annotations.Parameter;
31  import org.apache.maven.project.MavenProject;
32  import org.codehaus.plexus.util.MatchPatterns;
33  
34  import java.io.BufferedReader;
35  import java.io.File;
36  import java.io.FileInputStream;
37  import java.io.FileOutputStream;
38  import java.io.IOException;
39  import java.io.InputStreamReader;
40  import java.io.OutputStreamWriter;
41  import java.io.PrintWriter;
42  import java.util.ArrayList;
43  import java.util.Arrays;
44  import java.util.Collections;
45  import java.util.HashSet;
46  import java.util.List;
47  import java.util.Set;
48  
49  /**
50   * Publish a content to scm. By default, content is taken from default site staging directory
51   * <code>${project.build.directory}/staging</code>.
52   * Can be used without project, so usable to update any SCM with any content.
53   */
54  @Mojo ( name = "publish-scm", aggregator = true, requiresProject = false )
55  public class ScmPublishPublishScmMojo
56      extends AbstractScmPublishMojo
57  {
58      /**
59       * The content to be published.
60       */
61      @Parameter ( property = "scmpublish.content", defaultValue = "${project.build.directory}/staging" )
62      private File content;
63  
64      /**
65       */
66      @Component
67      protected MavenProject project;
68  
69      private List<File> deleted = new ArrayList<File>();
70  
71      private List<File> added = new ArrayList<File>();
72  
73      private List<File> updated = new ArrayList<File>();
74  
75      /**
76       * Update scm checkout directory with content.
77       *
78       * @param checkout        the scm checkout directory
79       * @param dir             the content to put in scm (can be <code>null</code>)
80       * @param doNotDeleteDirs directory names that should not be deleted from scm even if not in new content:
81       *                        used for modules, which content is available only when staging
82       * @throws IOException
83       */
84      private void update( File checkout, File dir, List<String> doNotDeleteDirs )
85          throws IOException
86      {
87          String[] files =
88              checkout.list( new NotFileFilter( new NameFileFilter( scmProvider.getScmSpecificFilename() ) ) );
89  
90          Set<String> checkoutContent = new HashSet<String>( Arrays.asList( files ) );
91          List<String> dirContent = ( dir != null ) ? Arrays.asList( dir.list() ) : Collections.<String>emptyList();
92  
93          Set<String> deleted = new HashSet<String>( checkoutContent );
94          deleted.removeAll( dirContent );
95  
96          MatchPatterns ignoreDeleteMatchPatterns = null;
97          List<String> pathsAsList = new ArrayList<String>( 0 );
98          if ( ignorePathsToDelete != null && ignorePathsToDelete.length > 0 )
99          {
100             ignoreDeleteMatchPatterns = MatchPatterns.from( ignorePathsToDelete );
101             pathsAsList = Arrays.asList( ignorePathsToDelete );
102         }
103 
104         for ( String name : deleted )
105         {
106             if ( ignoreDeleteMatchPatterns != null && ignoreDeleteMatchPatterns.matches( name, true ) )
107             {
108                 getLog().debug(
109                     name + " match one of the patterns '" + pathsAsList + "': do not add to deleted files" );
110                 continue;
111             }
112             getLog().debug( "file marked for deletion:" + name );
113             File file = new File( checkout, name );
114 
115             if ( ( doNotDeleteDirs != null ) && file.isDirectory() && ( doNotDeleteDirs.contains( name ) ) )
116             {
117                 // ignore directory not available
118                 continue;
119             }
120 
121             if ( file.isDirectory() )
122             {
123                 update( file, null, null );
124             }
125             this.deleted.add( file );
126         }
127 
128         for ( String name : dirContent )
129         {
130             File file = new File( checkout, name );
131             File source = new File( dir, name );
132 
133             if ( source.isDirectory() )
134             {
135                 if ( !checkoutContent.contains( name ) )
136                 {
137                     this.added.add( file );
138                     file.mkdir();
139                 }
140 
141                 update( file, source, null );
142             }
143             else
144             {
145                 if ( checkoutContent.contains( name ) )
146                 {
147                     this.updated.add( file );
148                 }
149                 else
150                 {
151                     this.added.add( file );
152                 }
153 
154                 copyFile( source, file );
155             }
156         }
157     }
158 
159     /**
160      * Copy a file content, normalizing newlines when necessary.
161      *
162      * @param srcFile  the source file
163      * @param destFile the destination file
164      * @throws IOException
165      * @see #requireNormalizeNewlines(File)
166      */
167     private void copyFile( File srcFile, File destFile )
168         throws IOException
169     {
170         if ( requireNormalizeNewlines( srcFile ) )
171         {
172             copyAndNormalizeNewlines( srcFile, destFile );
173         }
174         else
175         {
176             FileUtils.copyFile( srcFile, destFile );
177         }
178     }
179 
180     /**
181      * Copy and normalize newlines.
182      *
183      * @param srcFile  the source file
184      * @param destFile the destination file
185      * @throws IOException
186      */
187     private void copyAndNormalizeNewlines( File srcFile, File destFile )
188         throws IOException
189     {
190         BufferedReader in = null;
191         PrintWriter out = null;
192         try
193         {
194             in = new BufferedReader( new InputStreamReader( new FileInputStream( srcFile ), siteOutputEncoding ) );
195             out = new PrintWriter( new OutputStreamWriter( new FileOutputStream( destFile ), siteOutputEncoding ) );
196             String line;
197             while ( ( line = in.readLine() ) != null )
198             {
199                 if ( in.ready() )
200                 {
201                     out.println( line );
202                 }
203                 else
204                 {
205                     out.print( line );
206                 }
207             }
208         }
209         finally
210         {
211             IOUtils.closeQuietly( out );
212             IOUtils.closeQuietly( in );
213         }
214     }
215 
216     public void scmPublishExecute()
217         throws MojoExecutionException, MojoFailureException
218     {
219         if ( siteOutputEncoding == null )
220         {
221             getLog().warn( "No output encoding, defaulting to UTF-8." );
222             siteOutputEncoding = "utf-8";
223         }
224 
225         if ( !content.exists() )
226         {
227             throw new MojoExecutionException( "Configured content directory does not exist: " + content );
228         }
229 
230         if ( !content.canRead() )
231         {
232             throw new MojoExecutionException( "Can't read content directory: " + content );
233         }
234 
235         checkoutExisting();
236 
237         try
238         {
239             logInfo( "Updating content..." );
240             update( checkoutDirectory, content, ( project == null ) ? null : project.getModel().getModules() );
241         }
242         catch ( IOException ioe )
243         {
244             throw new MojoExecutionException( "Could not copy content to scm checkout", ioe );
245         }
246 
247         logInfo( "Publish files: %d addition(s), %d update(s), %d delete(s)", added.size(), updated.size(),
248                  deleted.size() );
249 
250         if ( isDryRun() )
251         {
252             for ( File addedFile : added )
253             {
254                 logInfo( "Added %s", addedFile.getAbsolutePath() );
255             }
256             for ( File deletedFile : deleted )
257             {
258                 logInfo( "Deleted %s", deletedFile.getAbsolutePath() );
259             }
260             for ( File updatedFile : updated )
261             {
262                 logInfo( "Updated %s", updatedFile.getAbsolutePath() );
263             }
264             return;
265         }
266 
267         if ( !added.isEmpty() )
268         {
269             addFiles( added );
270         }
271 
272         if ( !deleted.isEmpty() )
273         {
274             deleteFiles( deleted );
275         }
276 
277         logInfo( "Checking in SCM..." );
278         checkinFiles();
279     }
280 }