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