1 package org.apache.maven.plugins.scmpublish;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStreamReader;
28 import java.io.OutputStreamWriter;
29 import java.io.PrintWriter;
30 import java.nio.file.Files;
31 import java.nio.file.LinkOption;
32 import java.nio.file.StandardCopyOption;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.Date;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Set;
40
41 import org.apache.commons.io.FileUtils;
42 import org.apache.commons.io.IOUtils;
43 import org.apache.commons.io.filefilter.NameFileFilter;
44 import org.apache.commons.io.filefilter.NotFileFilter;
45 import org.apache.maven.plugin.MojoExecutionException;
46 import org.apache.maven.plugin.MojoFailureException;
47 import org.apache.maven.plugins.annotations.Mojo;
48 import org.apache.maven.plugins.annotations.Parameter;
49 import org.apache.maven.project.MavenProject;
50 import org.apache.maven.shared.utils.logging.MessageUtils;
51 import org.codehaus.plexus.util.MatchPatterns;
52
53
54
55
56
57
58 @Mojo ( name = "publish-scm", aggregator = true, requiresProject = false )
59 public class ScmPublishPublishScmMojo
60 extends AbstractScmPublishMojo
61 {
62
63
64
65 @Parameter ( property = "scmpublish.content", defaultValue = "${project.build.directory}/staging" )
66 private File content;
67
68
69
70 @Parameter( defaultValue = "${project}", readonly = true, required = true )
71 protected MavenProject project;
72
73 private List<File> deleted = new ArrayList<File>();
74
75 private List<File> added = new ArrayList<File>();
76
77 private List<File> updated = new ArrayList<File>();
78
79 private int directories = 0;
80 private int files = 0;
81 private long size = 0;
82
83
84
85
86
87
88
89
90
91
92 private void update( File checkout, File dir, List<String> doNotDeleteDirs )
93 throws IOException
94 {
95 String scmSpecificFilename = scmProvider.getScmSpecificFilename();
96 String[] files = scmSpecificFilename != null
97 ? checkout.list( new NotFileFilter( new NameFileFilter( scmSpecificFilename ) ) )
98 : checkout.list();
99
100 Set<String> checkoutContent = new HashSet<String>( Arrays.asList( files ) );
101 List<String> dirContent = ( dir != null ) ? Arrays.asList( dir.list() ) : Collections.<String>emptyList();
102
103 Set<String> deleted = new HashSet<String>( checkoutContent );
104 deleted.removeAll( dirContent );
105
106 MatchPatterns ignoreDeleteMatchPatterns = null;
107 List<String> pathsAsList = new ArrayList<String>( 0 );
108 if ( ignorePathsToDelete != null && ignorePathsToDelete.length > 0 )
109 {
110 ignoreDeleteMatchPatterns = MatchPatterns.from( ignorePathsToDelete );
111 pathsAsList = Arrays.asList( ignorePathsToDelete );
112 }
113
114 for ( String name : deleted )
115 {
116 if ( ignoreDeleteMatchPatterns != null && ignoreDeleteMatchPatterns.matches( name, true ) )
117 {
118 getLog().debug(
119 name + " match one of the patterns '" + pathsAsList + "': do not add to deleted files" );
120 continue;
121 }
122 getLog().debug( "file marked for deletion: " + name );
123 File file = new File( checkout, name );
124
125 if ( ( doNotDeleteDirs != null ) && file.isDirectory() && ( doNotDeleteDirs.contains( name ) ) )
126 {
127
128 continue;
129 }
130
131 if ( file.isDirectory() )
132 {
133 update( file, null, null );
134 }
135 this.deleted.add( file );
136 }
137
138 for ( String name : dirContent )
139 {
140 File file = new File( checkout, name );
141 File source = new File( dir, name );
142
143 if ( Files.isSymbolicLink( source.toPath() ) )
144 {
145 if ( !checkoutContent.contains( name ) )
146 {
147 this.added.add( file );
148 }
149
150
151 copySymLink( source, file );
152 }
153 else if ( source.isDirectory() )
154 {
155 directories++;
156 if ( !checkoutContent.contains( name ) )
157 {
158 this.added.add( file );
159 file.mkdir();
160 }
161
162 update( file, source, null );
163 }
164 else
165 {
166 if ( checkoutContent.contains( name ) )
167 {
168 this.updated.add( file );
169 }
170 else
171 {
172 this.added.add( file );
173 }
174
175 copyFile( source, file );
176 }
177 }
178 }
179
180
181
182
183
184
185
186
187 private void copySymLink( File srcFile, File destFile )
188 throws IOException
189 {
190 Files.copy( srcFile.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING,
191 StandardCopyOption.COPY_ATTRIBUTES, LinkOption.NOFOLLOW_LINKS );
192 }
193
194
195
196
197
198
199
200
201
202 private void copyFile( File srcFile, File destFile )
203 throws IOException
204 {
205 if ( requireNormalizeNewlines( srcFile ) )
206 {
207 copyAndNormalizeNewlines( srcFile, destFile );
208 }
209 else
210 {
211 FileUtils.copyFile( srcFile, destFile );
212 }
213 files++;
214 size += destFile.length();
215 }
216
217
218
219
220
221
222
223
224 private void copyAndNormalizeNewlines( File srcFile, File destFile )
225 throws IOException
226 {
227 BufferedReader in = null;
228 PrintWriter out = null;
229 try
230 {
231 in = new BufferedReader( new InputStreamReader( new FileInputStream( srcFile ), siteOutputEncoding ) );
232 out = new PrintWriter( new OutputStreamWriter( new FileOutputStream( destFile ), siteOutputEncoding ) );
233
234 for ( String line = in.readLine(); line != null; line = in.readLine() )
235 {
236 if ( in.ready() )
237 {
238 out.println( line );
239 }
240 else
241 {
242 out.print( line );
243 }
244 }
245
246 out.close();
247 out = null;
248 in.close();
249 in = null;
250 }
251 finally
252 {
253 IOUtils.closeQuietly( out );
254 IOUtils.closeQuietly( in );
255 }
256 }
257
258 public void scmPublishExecute()
259 throws MojoExecutionException, MojoFailureException
260 {
261 if ( siteOutputEncoding == null )
262 {
263 getLog().warn( "No output encoding, defaulting to UTF-8." );
264 siteOutputEncoding = "utf-8";
265 }
266
267 if ( !content.exists() )
268 {
269 throw new MojoExecutionException( "Configured content directory does not exist: " + content );
270 }
271
272 if ( !content.canRead() )
273 {
274 throw new MojoExecutionException( "Can't read content directory: " + content );
275 }
276
277 checkoutExisting();
278
279 final File updateDirectory;
280 if ( subDirectory == null )
281 {
282 updateDirectory = checkoutDirectory;
283 }
284 else
285 {
286 updateDirectory = new File( checkoutDirectory, subDirectory );
287
288
289 if ( !updateDirectory.toPath().normalize().startsWith( checkoutDirectory.toPath().normalize() ) )
290 {
291 logError( "Try to acces outside of the checkout directory with sub-directory: %s", subDirectory );
292 return;
293 }
294
295 if ( !updateDirectory.exists() )
296 {
297 updateDirectory.mkdirs();
298 }
299
300 logInfo( "Will copy content in sub-directory: %s", subDirectory );
301 }
302
303 try
304 {
305 logInfo( "Updating checkout directory with actual content in %s", content );
306 update( updateDirectory, content, ( project == null ) ? null : project.getModel().getModules() );
307 String displaySize = org.apache.commons.io.FileUtils.byteCountToDisplaySize( size );
308 logInfo( "Content consists of " + MessageUtils.buffer().strong( "%d directories and %d files = %s" ),
309 directories, files, displaySize );
310 }
311 catch ( IOException ioe )
312 {
313 throw new MojoExecutionException( "Could not copy content to SCM checkout", ioe );
314 }
315
316 logInfo( "Publishing content to SCM will result in "
317 + MessageUtils.buffer().strong( "%d addition(s), %d update(s), %d delete(s)" ), added.size(),
318 updated.size(), deleted.size() );
319
320 if ( isDryRun() )
321 {
322 int pos = checkoutDirectory.getAbsolutePath().length() + 1;
323 for ( File addedFile : added )
324 {
325 logInfo( "- addition %s", addedFile.getAbsolutePath().substring( pos ) );
326 }
327 for ( File updatedFile : updated )
328 {
329 logInfo( "- update %s", updatedFile.getAbsolutePath().substring( pos ) );
330 }
331 for ( File deletedFile : deleted )
332 {
333 logInfo( "- delete %s", deletedFile.getAbsolutePath().substring( pos ) );
334 }
335 return;
336 }
337
338 if ( !added.isEmpty() )
339 {
340 addFiles( added );
341 }
342
343 if ( !deleted.isEmpty() )
344 {
345 deleteFiles( deleted );
346 }
347
348 logInfo( "Checking in SCM, starting at " + new Date() + "..." );
349 checkinFiles();
350 }
351 }