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