1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.eluder.coveralls.maven.plugin;
25
26 import org.apache.maven.plugin.AbstractMojo;
27 import org.apache.maven.plugin.MojoExecutionException;
28 import org.apache.maven.plugin.MojoFailureException;
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.apache.maven.settings.Settings;
33 import org.eluder.coveralls.maven.plugin.domain.CoverallsResponse;
34 import org.eluder.coveralls.maven.plugin.domain.Git;
35 import org.eluder.coveralls.maven.plugin.domain.GitRepository;
36 import org.eluder.coveralls.maven.plugin.domain.Job;
37 import org.eluder.coveralls.maven.plugin.httpclient.CoverallsClient;
38 import org.eluder.coveralls.maven.plugin.httpclient.CoverallsProxyClient;
39 import org.eluder.coveralls.maven.plugin.json.JsonWriter;
40 import org.eluder.coveralls.maven.plugin.logging.CoverageTracingLogger;
41 import org.eluder.coveralls.maven.plugin.logging.DryRunLogger;
42 import org.eluder.coveralls.maven.plugin.logging.JobLogger;
43 import org.eluder.coveralls.maven.plugin.logging.Logger;
44 import org.eluder.coveralls.maven.plugin.logging.Logger.Position;
45 import org.eluder.coveralls.maven.plugin.service.Appveyor;
46 import org.eluder.coveralls.maven.plugin.service.Bamboo;
47 import org.eluder.coveralls.maven.plugin.service.Circle;
48 import org.eluder.coveralls.maven.plugin.service.General;
49 import org.eluder.coveralls.maven.plugin.service.Jenkins;
50 import org.eluder.coveralls.maven.plugin.service.ServiceSetup;
51 import org.eluder.coveralls.maven.plugin.service.Shippable;
52 import org.eluder.coveralls.maven.plugin.service.Travis;
53 import org.eluder.coveralls.maven.plugin.service.Wercker;
54 import org.eluder.coveralls.maven.plugin.source.SourceCallback;
55 import org.eluder.coveralls.maven.plugin.source.SourceLoader;
56 import org.eluder.coveralls.maven.plugin.source.UniqueSourceCallback;
57 import org.eluder.coveralls.maven.plugin.util.CoverageParsersFactory;
58 import org.eluder.coveralls.maven.plugin.util.SourceLoaderFactory;
59 import org.eluder.coveralls.maven.plugin.util.TimestampParser;
60
61 import java.io.File;
62 import java.io.IOException;
63 import java.util.ArrayList;
64 import java.util.Date;
65 import java.util.List;
66 import java.util.Map;
67 import java.util.Properties;
68
69 @Mojo(name = "report", threadSafe = false, aggregator = true)
70 public class CoverallsReportMojo extends AbstractMojo {
71
72
73
74
75 @Parameter(property = "jacocoReports")
76 protected List<File> jacocoReports;
77
78
79
80
81 @Parameter(property = "coberturaReports")
82 protected List<File> coberturaReports;
83
84
85
86
87 @Parameter(property = "sagaReports")
88 protected List<File> sagaReports;
89
90
91
92
93 @Parameter(property = "relativeReportDirs")
94 protected List<String> relativeReportDirs;
95
96
97
98
99 @Parameter(property = "coverallsFile", defaultValue = "${project.build.directory}/coveralls.json")
100 protected File coverallsFile;
101
102
103
104
105 @Parameter(property = "coverallsUrl", defaultValue = "https://coveralls.io/api/v1/jobs")
106 protected String coverallsUrl;
107
108
109
110
111 @Parameter(property = "sourceDirectories")
112 protected List<File> sourceDirectories;
113
114
115
116
117 @Parameter(property = "sourceEncoding", defaultValue = "${project.build.sourceEncoding}")
118 protected String sourceEncoding;
119
120
121
122
123 @Parameter(property = "serviceName")
124 protected String serviceName;
125
126
127
128
129 @Parameter(property = "serviceJobId")
130 protected String serviceJobId;
131
132
133
134
135 @Parameter(property = "serviceBuildNumber")
136 protected String serviceBuildNumber;
137
138
139
140
141 @Parameter(property = "serviceBuildUrl")
142 protected String serviceBuildUrl;
143
144
145
146
147 @Parameter(property = "serviceEnvironment")
148 protected Properties serviceEnvironment;
149
150
151
152
153 @Parameter(property = "repoToken")
154 protected String repoToken;
155
156
157
158
159 @Parameter(property = "branch")
160 protected String branch;
161
162
163
164
165 @Parameter(property = "pullRequest")
166 protected String pullRequest;
167
168
169
170
171 @Parameter(property = "parallel")
172 protected boolean parallel;
173
174
175
176
177 @Parameter(property = "timestampFormat", defaultValue = "${maven.build.timestamp.format}")
178 protected String timestampFormat;
179
180
181
182
183
184 @Parameter(property = "timestamp", defaultValue = "${maven.build.timestamp}")
185 protected String timestamp;
186
187
188
189
190 @Parameter(property = "dryRun", defaultValue = "false")
191 protected boolean dryRun;
192
193
194
195
196 @Parameter(property = "failOnServiceError", defaultValue = "true")
197 protected boolean failOnServiceError;
198
199
200
201
202 @Parameter(property = "scanForSources", defaultValue = "false")
203 protected boolean scanForSources;
204
205
206
207
208 @Parameter(property = "coveralls.basedir", defaultValue = "${project.basedir}")
209 protected File basedir;
210
211
212
213
214 @Parameter(property = "coveralls.skip", defaultValue = "false")
215 protected boolean skip;
216
217
218
219
220
221 @Parameter(defaultValue = "${settings}", readonly = true, required = true)
222 protected Settings settings;
223
224
225
226
227 @Parameter(defaultValue = "${project}", readonly = true)
228 protected MavenProject project;
229
230
231 @Override
232 public final void execute() throws MojoExecutionException, MojoFailureException {
233 if (skip) {
234 getLog().info("Skip property set, skipping plugin execution");
235 return;
236 }
237
238 try {
239 createEnvironment().setup();
240 Job job = createJob();
241 job.validate().throwOrInform(getLog());
242 SourceLoader sourceLoader = createSourceLoader(job);
243 List<CoverageParser> parsers = createCoverageParsers(sourceLoader);
244 JsonWriter writer = createJsonWriter(job);
245 CoverallsClient client = createCoverallsClient();
246 List<Logger> reporters = new ArrayList<>();
247 reporters.add(new JobLogger(job));
248 SourceCallback sourceCallback = createSourceCallbackChain(writer, reporters);
249 reporters.add(new DryRunLogger(job.isDryRun(), writer.getCoverallsFile()));
250
251 report(reporters, Position.BEFORE);
252 writeCoveralls(writer, sourceCallback, parsers);
253 report(reporters, Position.AFTER);
254
255 if (!job.isDryRun()) {
256 submitData(client, writer.getCoverallsFile());
257 }
258 } catch (ProcessingException ex) {
259 throw new MojoFailureException("Processing of input or output data failed", ex);
260 } catch (IOException ex) {
261 throw new MojoFailureException("I/O operation failed", ex);
262 } catch (Exception ex) {
263 throw new MojoExecutionException("Build error", ex);
264 }
265 }
266
267
268
269
270
271
272
273
274 protected List<CoverageParser> createCoverageParsers(final SourceLoader sourceLoader) throws IOException {
275 return new CoverageParsersFactory(project, sourceLoader)
276 .withJaCoCoReports(jacocoReports)
277 .withCoberturaReports(coberturaReports)
278 .withSagaReports(sagaReports)
279 .withRelativeReportDirs(relativeReportDirs)
280 .createParsers();
281 }
282
283
284
285
286
287
288
289 protected SourceLoader createSourceLoader(final Job job) {
290 return new SourceLoaderFactory(job.getGit().getBaseDir(), project, sourceEncoding)
291 .withSourceDirectories(sourceDirectories)
292 .withScanForSources(scanForSources)
293 .createSourceLoader();
294 }
295
296
297
298
299
300
301 protected Environment createEnvironment() {
302 return new Environment(this, getServices());
303 }
304
305
306
307
308
309
310 protected List<ServiceSetup> getServices() {
311 Map<String, String> env = System.getenv();
312 List<ServiceSetup> services = new ArrayList<>();
313 services.add(new Shippable(env));
314 services.add(new Travis(env));
315 services.add(new Circle(env));
316 services.add(new Jenkins(env));
317 services.add(new Bamboo(env));
318 services.add(new Appveyor(env));
319 services.add(new Wercker(env));
320 services.add(new General(env));
321 return services;
322 }
323
324
325
326
327
328
329
330
331 protected Job createJob() throws ProcessingException, IOException {
332 Git git = new GitRepository(basedir).load();
333 Date time = new TimestampParser(timestampFormat).parse(timestamp);
334 return new Job()
335 .withRepoToken(repoToken)
336 .withServiceName(serviceName)
337 .withServiceJobId(serviceJobId)
338 .withServiceBuildNumber(serviceBuildNumber)
339 .withServiceBuildUrl(serviceBuildUrl)
340 .withParallel(parallel)
341 .withServiceEnvironment(serviceEnvironment)
342 .withDryRun(dryRun)
343 .withBranch(branch)
344 .withPullRequest(pullRequest)
345 .withTimestamp(time)
346 .withGit(git);
347 }
348
349
350
351
352
353
354
355
356 protected JsonWriter createJsonWriter(final Job job) throws IOException {
357 return new JsonWriter(job, coverallsFile);
358 }
359
360
361
362
363
364
365 protected CoverallsClient createCoverallsClient() {
366 return new CoverallsProxyClient(coverallsUrl, settings.getActiveProxy());
367 }
368
369
370
371
372
373
374
375
376 protected SourceCallback createSourceCallbackChain(final JsonWriter writer, final List<Logger> reporters) {
377 SourceCallback chain = writer;
378 if (getLog().isInfoEnabled()) {
379 CoverageTracingLogger coverageTracingReporter = new CoverageTracingLogger(chain);
380 chain = coverageTracingReporter;
381 reporters.add(coverageTracingReporter);
382 }
383 chain = new UniqueSourceCallback(chain);
384 return chain;
385 }
386
387
388
389
390
391
392
393
394
395
396 protected void writeCoveralls(final JsonWriter writer, final SourceCallback sourceCallback, final List<CoverageParser> parsers) throws ProcessingException, IOException {
397 try {
398 getLog().info("Writing Coveralls data to " + writer.getCoverallsFile().getAbsolutePath() + "...");
399 long now = System.currentTimeMillis();
400 sourceCallback.onBegin();
401 for (CoverageParser parser : parsers) {
402 getLog().info("Processing coverage report from " + parser.getCoverageFile().getAbsolutePath());
403 parser.parse(sourceCallback);
404 }
405 sourceCallback.onComplete();
406 long duration = System.currentTimeMillis() - now;
407 getLog().info("Successfully wrote Coveralls data in " + duration + "ms");
408 } finally {
409 writer.close();
410 }
411 }
412
413 private void submitData(final CoverallsClient client, final File coverallsFile) throws ProcessingException, IOException {
414 getLog().info("Submitting Coveralls data to API");
415 long now = System.currentTimeMillis();
416 try {
417 CoverallsResponse response = client.submit(coverallsFile);
418 long duration = System.currentTimeMillis() - now;
419 getLog().info("Successfully submitted Coveralls data in " + duration + "ms for " + response.getMessage());
420 getLog().info(response.getUrl());
421 getLog().info("*** It might take hours for Coveralls to update the actual coverage numbers for a job");
422 getLog().info(" If you see question marks in the report, please be patient");
423 } catch (ProcessingException ex) {
424 long duration = System.currentTimeMillis() - now;
425 String message = "Submission failed in " + duration + "ms while processing data";
426 handleSubmissionError(ex, message, true);
427 } catch (IOException ex) {
428 long duration = System.currentTimeMillis() - now;
429 String message = "Submission failed in " + duration + "ms while handling I/O operations";
430 handleSubmissionError(ex, message, failOnServiceError);
431 }
432 }
433
434 private <T extends Exception> void handleSubmissionError(final T ex, final String message, final boolean failOnException) throws T {
435 if (failOnException) {
436 getLog().error(message);
437 throw ex;
438 } else {
439 getLog().warn(message);
440 }
441 }
442
443 private void report(final List<Logger> reporters, final Position position) {
444 for (Logger reporter : reporters) {
445 if (position.equals(reporter.getPosition())) {
446 reporter.log(getLog());
447 }
448 }
449 }
450 }