1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.report.projectinfo;
20
21 import javax.inject.Inject;
22
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Locale;
26
27 import org.apache.maven.doxia.sink.Sink;
28 import org.apache.maven.model.Model;
29 import org.apache.maven.model.Scm;
30 import org.apache.maven.plugin.logging.Log;
31 import org.apache.maven.plugins.annotations.Mojo;
32 import org.apache.maven.plugins.annotations.Parameter;
33 import org.apache.maven.project.ProjectBuilder;
34 import org.apache.maven.reporting.MavenReportException;
35 import org.apache.maven.repository.RepositorySystem;
36 import org.apache.maven.scm.manager.ScmManager;
37 import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
38 import org.apache.maven.scm.provider.hg.repository.HgScmProviderRepository;
39 import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
40 import org.apache.maven.scm.repository.ScmRepository;
41 import org.codehaus.plexus.i18n.I18N;
42 import org.codehaus.plexus.util.StringUtils;
43
44
45
46
47
48
49
50 @Mojo(name = "scm")
51 public class ScmReport extends AbstractProjectInfoReport {
52
53
54
55
56
57
58
59 @Parameter(defaultValue = "${project.artifactId}")
60 private String checkoutDirectoryName;
61
62
63
64
65
66
67
68 @Parameter(defaultValue = "${project.scm.connection}")
69 private String anonymousConnection;
70
71
72
73
74
75
76
77 @Parameter(defaultValue = "${project.scm.developerConnection}")
78 private String developerConnection;
79
80
81
82
83
84
85 @Parameter(defaultValue = "${project.scm.url}")
86 private String webAccessUrl;
87
88
89
90
91
92
93 @Parameter(defaultValue = "${project.scm.tag}")
94 private String scmTag;
95
96
97
98
99 protected final ScmManager scmManager;
100
101 @Inject
102 public ScmReport(
103 RepositorySystem repositorySystem, I18N i18n, ProjectBuilder projectBuilder, ScmManager scmManager) {
104 super(repositorySystem, i18n, projectBuilder);
105 this.scmManager = scmManager;
106 }
107
108
109
110
111
112 @Override
113 public boolean canGenerateReport() throws MavenReportException {
114 boolean result = super.canGenerateReport();
115 if (result && skipEmptyReport) {
116 Scm scm = getProject().getModel().getScm();
117 result = scm != null;
118
119 if (result
120 && (anonymousConnection == null || anonymousConnection.isEmpty())
121 && (developerConnection == null || developerConnection.isEmpty())
122 && StringUtils.isEmpty(scm.getUrl())) {
123 result = false;
124 }
125 }
126
127 return result;
128 }
129
130 @Override
131 public void executeReport(Locale locale) {
132 ScmRenderer r = new ScmRenderer(
133 getLog(),
134 scmManager,
135 getSink(),
136 getProject().getModel(),
137 getI18N(locale),
138 locale,
139 checkoutDirectoryName,
140 webAccessUrl,
141 anonymousConnection,
142 developerConnection,
143 scmTag);
144
145 r.render();
146 }
147
148
149 public String getOutputName() {
150 return "scm";
151 }
152
153 @Override
154 protected String getI18Nsection() {
155 return "scm";
156 }
157
158
159
160
161
162
163
164
165 private static class ScmRenderer extends AbstractProjectInfoRenderer {
166 private static final String LS = System.lineSeparator();
167
168 private Log log;
169
170 private Model model;
171
172 private ScmManager scmManager;
173
174
175
176
177 private String anonymousConnection;
178
179 private String devConnection;
180
181 private String checkoutDirectoryName;
182
183 private String webAccessUrl;
184
185 private String scmTag;
186
187 ScmRenderer(
188 Log log,
189 ScmManager scmManager,
190 Sink sink,
191 Model model,
192 I18N i18n,
193 Locale locale,
194 String checkoutDirName,
195 String webAccessUrl,
196 String anonymousConnection,
197 String devConnection,
198 String scmTag) {
199 super(sink, i18n, locale);
200
201 this.log = log;
202
203 this.scmManager = scmManager;
204
205 this.model = model;
206
207 this.checkoutDirectoryName = checkoutDirName;
208
209 this.webAccessUrl = webAccessUrl;
210
211 this.anonymousConnection = anonymousConnection;
212
213 this.devConnection = devConnection;
214
215 this.scmTag = scmTag;
216 }
217
218 @Override
219 protected String getI18Nsection() {
220 return "scm";
221 }
222
223 @Override
224 protected void renderBody() {
225 Scm scm = model.getScm();
226 if (scm == null
227 || (anonymousConnection == null || anonymousConnection.isEmpty())
228 && (devConnection == null || devConnection.isEmpty())
229 && StringUtils.isEmpty(scm.getUrl())) {
230 startSection(getTitle());
231
232 paragraph(getI18nString("noscm"));
233
234 endSection();
235
236 return;
237 }
238
239 ScmRepository anonymousRepository = getScmRepository(anonymousConnection);
240 ScmRepository devRepository = getScmRepository(devConnection);
241
242
243 renderOverviewSection(anonymousRepository, devRepository);
244
245
246 renderWebAccessSection(webAccessUrl);
247
248
249 renderAnonymousAccessSection(anonymousRepository);
250
251
252 renderDeveloperAccessSection(devRepository);
253
254
255 renderAccessBehindFirewallSection(devRepository);
256
257
258 renderAccessThroughProxySection(anonymousRepository, devRepository);
259 }
260
261
262
263
264
265
266
267 private void renderOverviewSection(ScmRepository anonymousRepository, ScmRepository devRepository) {
268 startSection(getI18nString("overview.title"));
269
270 if (isScmSystem(anonymousRepository, "git") || isScmSystem(devRepository, "git")) {
271 sink.paragraph();
272 linkPatternedText(getI18nString("git.intro"));
273 sink.paragraph_();
274 } else if (isScmSystem(anonymousRepository, "hg") || isScmSystem(devRepository, "hg")) {
275 sink.paragraph();
276 linkPatternedText(getI18nString("hg.intro"));
277 sink.paragraph_();
278 } else if (isScmSystem(anonymousRepository, "svn") || isScmSystem(devRepository, "svn")) {
279 sink.paragraph();
280 linkPatternedText(getI18nString("svn.intro"));
281 sink.paragraph_();
282 } else {
283 paragraph(getI18nString("general.intro"));
284 }
285
286 endSection();
287 }
288
289
290
291
292
293
294 private void renderWebAccessSection(String scmUrl) {
295 startSection(getI18nString("webaccess.title"));
296
297 if (scmUrl == null || scmUrl.isEmpty()) {
298 paragraph(getI18nString("webaccess.nourl"));
299 } else {
300 paragraph(getI18nString("webaccess.url"));
301
302 verbatimLink(scmUrl, scmUrl);
303 }
304
305 endSection();
306 }
307
308
309
310
311
312
313
314
315
316 private void renderAnonymousAccessSection(ScmRepository anonymousRepository) {
317 if (anonymousConnection == null || anonymousConnection.isEmpty()) {
318 return;
319 }
320
321 startSection(getI18nString("anonymousaccess.title"));
322
323 if (anonymousRepository != null && isScmSystem(anonymousRepository, "git")) {
324 GitScmProviderRepository gitRepo =
325 (GitScmProviderRepository) anonymousRepository.getProviderRepository();
326
327 anonymousAccessGit(gitRepo);
328 } else if (anonymousRepository != null && isScmSystem(anonymousRepository, "hg")) {
329 HgScmProviderRepository hgRepo = (HgScmProviderRepository) anonymousRepository.getProviderRepository();
330
331 anonymousAccessMercurial(hgRepo);
332 } else if (anonymousRepository != null && isScmSystem(anonymousRepository, "svn")) {
333 SvnScmProviderRepository svnRepo =
334 (SvnScmProviderRepository) anonymousRepository.getProviderRepository();
335
336 anonymousAccessSubversion(svnRepo);
337 } else {
338 paragraph(getI18nString("anonymousaccess.general.intro"));
339
340 verbatimText(anonymousConnection.substring(4));
341 }
342
343 endSection();
344 }
345
346
347
348
349
350
351 private void renderDeveloperAccessSection(ScmRepository devRepository) {
352 if (devConnection == null || devConnection.isEmpty()) {
353 return;
354 }
355
356 startSection(getI18nString("devaccess.title"));
357
358 if (devRepository != null && isScmSystem(devRepository, "git")) {
359 GitScmProviderRepository gitRepo = (GitScmProviderRepository) devRepository.getProviderRepository();
360
361 developerAccessGit(gitRepo);
362 } else if (devRepository != null && isScmSystem(devRepository, "hg")) {
363 HgScmProviderRepository hgRepo = (HgScmProviderRepository) devRepository.getProviderRepository();
364
365 developerAccessMercurial(hgRepo);
366 } else if (devRepository != null && isScmSystem(devRepository, "svn")) {
367 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) devRepository.getProviderRepository();
368
369 developerAccessSubversion(svnRepo);
370 } else {
371 paragraph(getI18nString("devaccess.general.intro"));
372
373 verbatimText(devConnection.substring(4));
374 }
375
376 endSection();
377 }
378
379
380
381
382
383
384 private void renderAccessBehindFirewallSection(ScmRepository devRepository) {
385 startSection(getI18nString("accessbehindfirewall.title"));
386
387 if (devRepository != null && isScmSystem(devRepository, "svn")) {
388 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) devRepository.getProviderRepository();
389
390 paragraph(getI18nString("accessbehindfirewall.svn.intro"));
391
392 verbatimText("$ svn checkout " + svnRepo.getUrl() + " " + checkoutDirectoryName);
393 } else {
394 paragraph(getI18nString("accessbehindfirewall.general.intro"));
395 }
396
397 endSection();
398 }
399
400
401
402
403
404
405
406 private void renderAccessThroughProxySection(ScmRepository anonymousRepository, ScmRepository devRepository) {
407 if (isScmSystem(anonymousRepository, "svn") || isScmSystem(devRepository, "svn")) {
408 startSection(getI18nString("accessthroughtproxy.title"));
409
410 paragraph(getI18nString("accessthroughtproxy.svn.intro1"));
411 paragraph(getI18nString("accessthroughtproxy.svn.intro2"));
412 paragraph(getI18nString("accessthroughtproxy.svn.intro3"));
413
414 verbatimText(
415 "[global]" + LS + "http-proxy-host = your.proxy.name" + LS + "http-proxy-port = 3128" + LS);
416
417 endSection();
418 }
419 }
420
421
422
423 private void gitClone(String url) {
424
425
426 int index = url.indexOf(".git/");
427 if (index > 0) {
428 url = url.substring(0, index + 4);
429 }
430
431 boolean head = (scmTag == null || scmTag.isEmpty()) || "HEAD".equals(scmTag);
432 verbatimText("$ git clone " + (head ? "" : ("--branch " + scmTag + ' ')) + url);
433 }
434
435
436
437
438
439
440
441
442
443
444 private void anonymousAccessGit(GitScmProviderRepository gitRepo) {
445 sink.paragraph();
446 linkPatternedText(getI18nString("anonymousaccess.git.intro"));
447 sink.paragraph_();
448
449 gitClone(gitRepo.getFetchUrl());
450 }
451
452
453
454
455
456
457
458
459
460
461
462
463 private void anonymousAccessMercurial(HgScmProviderRepository hgRepo) {
464 sink.paragraph();
465 linkPatternedText(getI18nString("anonymousaccess.hg.intro"));
466 sink.paragraph_();
467
468 verbatimText("$ hg clone " + hgRepo.getURI());
469 }
470
471
472
473
474
475
476
477
478
479
480
481
482 private void developerAccessGit(GitScmProviderRepository gitRepo) {
483 sink.paragraph();
484 linkPatternedText(getI18nString("devaccess.git.intro"));
485 sink.paragraph_();
486
487 gitClone(gitRepo.getPushUrl());
488 }
489
490
491
492
493
494
495
496
497
498
499
500
501 private void developerAccessMercurial(HgScmProviderRepository hgRepo) {
502 sink.paragraph();
503 linkPatternedText(getI18nString("devaccess.hg.intro"));
504 sink.paragraph_();
505
506 verbatimText("$ hg clone " + hgRepo.getURI());
507 }
508
509
510
511
512
513
514
515
516
517
518
519
520
521 private void anonymousAccessSubversion(SvnScmProviderRepository svnRepo) {
522 paragraph(getI18nString("anonymousaccess.svn.intro"));
523
524 verbatimText("$ svn checkout " + svnRepo.getUrl() + " " + checkoutDirectoryName);
525 }
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540 private void developerAccessSubversion(SvnScmProviderRepository svnRepo) {
541 if (svnRepo.getUrl() != null) {
542 if (svnRepo.getUrl().startsWith("https://")) {
543 paragraph(getI18nString("devaccess.svn.intro1.https"));
544 } else if (svnRepo.getUrl().startsWith("svn://")) {
545 paragraph(getI18nString("devaccess.svn.intro1.svn"));
546 } else if (svnRepo.getUrl().startsWith("svn+ssh://")) {
547 paragraph(getI18nString("devaccess.svn.intro1.svnssh"));
548 } else {
549 paragraph(getI18nString("devaccess.svn.intro1.other"));
550 }
551 }
552
553 StringBuilder sb = new StringBuilder();
554
555 sb.append("$ svn checkout ").append(svnRepo.getUrl()).append(" ").append(checkoutDirectoryName);
556
557 verbatimText(sb.toString());
558
559 paragraph(getI18nString("devaccess.svn.intro2"));
560
561 sb = new StringBuilder();
562 sb.append("$ svn commit --username your-username -m \"A message\"");
563
564 verbatimText(sb.toString());
565 }
566
567
568
569
570
571
572
573 public ScmRepository getScmRepository(String scmUrl) {
574 if (scmUrl == null || scmUrl.isEmpty()) {
575 return null;
576 }
577
578 ScmRepository repo = null;
579 List<String> messages = new ArrayList<>();
580 try {
581 messages.addAll(scmManager.validateScmRepository(scmUrl));
582 } catch (Exception e) {
583 messages.add(e.getMessage());
584 }
585
586 if (!messages.isEmpty()) {
587 StringBuilder sb = new StringBuilder();
588 boolean isIntroAdded = false;
589 for (String msg : messages) {
590
591
592 if (msg.startsWith("No such provider")) {
593 continue;
594 }
595
596 if (!isIntroAdded) {
597 sb.append("This SCM url '");
598 sb.append(scmUrl);
599 sb.append("' is invalid due to the following errors:");
600 sb.append(LS);
601 isIntroAdded = true;
602 }
603 sb.append(" * ");
604 sb.append(msg);
605 sb.append(LS);
606 }
607
608 if (StringUtils.isNotEmpty(sb.toString())) {
609 sb.append("For more information about SCM URL Format, please refer to: "
610 + "http://maven.apache.org/scm/scm-url-format.html");
611
612 throw new IllegalArgumentException(sb.toString());
613 }
614 }
615
616 try {
617 repo = scmManager.makeScmRepository(scmUrl);
618 } catch (Exception e) {
619
620 if (log.isDebugEnabled()) {
621 log.debug(e.getMessage(), e);
622 }
623 }
624
625 return repo;
626 }
627
628
629
630
631
632
633
634
635
636
637
638
639
640 private static boolean isScmSystem(ScmRepository scmRepository, String scmProvider) {
641 if (scmProvider == null || scmProvider.isEmpty()) {
642 return false;
643 }
644 return scmRepository != null && scmProvider.equalsIgnoreCase(scmRepository.getProvider());
645 }
646 }
647 }