1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.shared.release.phase;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.Writer;
28 import java.nio.file.Path;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.LinkedHashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Properties;
35 import java.util.Set;
36
37 import org.apache.maven.artifact.Artifact;
38 import org.apache.maven.artifact.ArtifactUtils;
39 import org.apache.maven.model.Build;
40 import org.apache.maven.model.Dependency;
41 import org.apache.maven.model.Extension;
42 import org.apache.maven.model.Model;
43 import org.apache.maven.model.Plugin;
44 import org.apache.maven.model.ReportPlugin;
45 import org.apache.maven.model.Reporting;
46 import org.apache.maven.model.Resource;
47 import org.apache.maven.model.Scm;
48 import org.apache.maven.model.building.DefaultModelBuildingRequest;
49 import org.apache.maven.model.building.ModelBuildingRequest;
50 import org.apache.maven.model.interpolation.ModelInterpolator;
51 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
52 import org.apache.maven.model.superpom.SuperPomProvider;
53 import org.apache.maven.project.MavenProject;
54 import org.apache.maven.scm.ScmException;
55 import org.apache.maven.scm.ScmFileSet;
56 import org.apache.maven.scm.command.add.AddScmResult;
57 import org.apache.maven.scm.provider.ScmProvider;
58 import org.apache.maven.scm.repository.ScmRepository;
59 import org.apache.maven.shared.release.ReleaseExecutionException;
60 import org.apache.maven.shared.release.ReleaseFailureException;
61 import org.apache.maven.shared.release.ReleaseResult;
62 import org.apache.maven.shared.release.config.ReleaseDescriptor;
63 import org.apache.maven.shared.release.env.ReleaseEnvironment;
64 import org.apache.maven.shared.release.scm.ReleaseScmCommandException;
65 import org.apache.maven.shared.release.scm.ScmRepositoryConfigurator;
66 import org.apache.maven.shared.release.scm.ScmTranslator;
67 import org.apache.maven.shared.release.util.ReleaseUtil;
68 import org.codehaus.plexus.util.WriterFactory;
69
70 import static java.util.Objects.requireNonNull;
71
72
73
74
75
76
77
78 @Singleton
79 @Named("generate-release-poms")
80 public class GenerateReleasePomsPhase extends AbstractReleasePomsPhase implements ResourceGenerator {
81 private static final String FINALNAME_EXPRESSION = "${project.artifactId}-${project.version}";
82
83 private final SuperPomProvider superPomProvider;
84
85 private final ModelInterpolator modelInterpolator;
86
87
88
89
90 private final Map<String, ScmTranslator> scmTranslators;
91
92 @Inject
93 public GenerateReleasePomsPhase(
94 ScmRepositoryConfigurator scmRepositoryConfigurator,
95 SuperPomProvider superPomProvider,
96 ModelInterpolator modelInterpolator,
97 Map<String, ScmTranslator> scmTranslators) {
98 super(scmRepositoryConfigurator);
99 this.superPomProvider = requireNonNull(superPomProvider);
100 this.modelInterpolator = requireNonNull(modelInterpolator);
101 this.scmTranslators = requireNonNull(scmTranslators);
102 }
103
104
105
106
107
108 @Override
109 public ReleaseResult execute(
110 ReleaseDescriptor releaseDescriptor,
111 ReleaseEnvironment releaseEnvironment,
112 List<MavenProject> reactorProjects)
113 throws ReleaseExecutionException, ReleaseFailureException {
114 return execute(releaseDescriptor, releaseEnvironment, reactorProjects, false);
115 }
116
117 private ReleaseResult execute(
118 ReleaseDescriptor releaseDescriptor,
119 ReleaseEnvironment releaseEnvironment,
120 List<MavenProject> reactorProjects,
121 boolean simulate)
122 throws ReleaseExecutionException, ReleaseFailureException {
123 ReleaseResult result = new ReleaseResult();
124
125 if (releaseDescriptor.isGenerateReleasePoms()) {
126 logInfo(result, "Generating release POMs...");
127
128 generateReleasePoms(releaseDescriptor, releaseEnvironment, reactorProjects, simulate, result);
129 } else {
130 logInfo(result, "Not generating release POMs");
131 }
132
133 result.setResultCode(ReleaseResult.SUCCESS);
134
135 return result;
136 }
137
138 private void generateReleasePoms(
139 ReleaseDescriptor releaseDescriptor,
140 ReleaseEnvironment releaseEnvironment,
141 List<MavenProject> reactorProjects,
142 boolean simulate,
143 ReleaseResult result)
144 throws ReleaseExecutionException, ReleaseFailureException {
145 List<File> releasePoms = new ArrayList<>();
146
147 for (MavenProject project : reactorProjects) {
148 logInfo(result, "Generating release POM for '" + project.getName() + "'...");
149
150 releasePoms.add(generateReleasePom(project, releaseDescriptor, releaseEnvironment, result));
151 }
152
153 addReleasePomsToScm(releaseDescriptor, releaseEnvironment, reactorProjects, simulate, result, releasePoms);
154 }
155
156 private File generateReleasePom(
157 MavenProject project,
158 ReleaseDescriptor releaseDescriptor,
159 ReleaseEnvironment releaseEnvironment,
160 ReleaseResult result)
161 throws ReleaseExecutionException, ReleaseFailureException {
162
163
164 Model releasePom = createReleaseModel(project, releaseDescriptor, releaseEnvironment, result);
165
166
167
168 MavenXpp3Writer pomWriter = new MavenXpp3Writer();
169
170 File releasePomFile = ReleaseUtil.getReleasePom(project);
171
172
173 if (releasePomFile == null) {
174 throw new ReleaseExecutionException("Cannot generate release POM : pom file is null");
175 }
176
177 try (Writer fileWriter = WriterFactory.newXmlWriter(releasePomFile)) {
178 pomWriter.write(fileWriter, releasePom);
179 } catch (IOException exception) {
180 throw new ReleaseExecutionException("Cannot generate release POM", exception);
181 }
182
183 return releasePomFile;
184 }
185
186 private void addReleasePomsToScm(
187 ReleaseDescriptor releaseDescriptor,
188 ReleaseEnvironment releaseEnvironment,
189 List<MavenProject> reactorProjects,
190 boolean simulate,
191 ReleaseResult result,
192 List<File> releasePoms)
193 throws ReleaseFailureException, ReleaseExecutionException {
194 if (simulate) {
195 logInfo(result, "Full run would be adding " + releasePoms);
196 } else {
197 ScmRepository scmRepository = getScmRepository(releaseDescriptor, releaseEnvironment);
198 ScmProvider scmProvider = getScmProvider(scmRepository);
199
200 MavenProject rootProject = ReleaseUtil.getRootProject(reactorProjects);
201 ScmFileSet scmFileSet = new ScmFileSet(rootProject.getFile().getParentFile(), releasePoms);
202
203 try {
204 AddScmResult scmResult = scmProvider.add(scmRepository, scmFileSet);
205
206 if (!scmResult.isSuccess()) {
207 throw new ReleaseScmCommandException("Cannot add release POM to SCM", scmResult);
208 }
209 } catch (ScmException exception) {
210 throw new ReleaseExecutionException(
211 "Cannot add release POM to SCM: " + exception.getMessage(), exception);
212 }
213 }
214 }
215
216 private Model createReleaseModel(
217 MavenProject project,
218 ReleaseDescriptor releaseDescriptor,
219 ReleaseEnvironment releaseEnvironment,
220 ReleaseResult result)
221 throws ReleaseFailureException, ReleaseExecutionException {
222 MavenProject releaseProject = project.clone();
223 Model releaseModel = releaseProject.getModel();
224
225
226
227 releaseModel.setParent(null);
228 releaseModel.setProfiles(Collections.emptyList());
229 releaseModel.setDependencyManagement(null);
230 releaseProject.getBuild().setPluginManagement(null);
231
232
233 String releaseVersion = getNextVersion(releaseDescriptor, project.getGroupId(), project.getArtifactId());
234 releaseModel.setVersion(releaseVersion);
235
236 String originalFinalName = releaseModel.getBuild().getFinalName();
237
238 if (!FINALNAME_EXPRESSION.equals(originalFinalName)) {
239 originalFinalName = findOriginalFinalName(project);
240
241 if (originalFinalName == null) {
242
243 originalFinalName = FINALNAME_EXPRESSION;
244 }
245 }
246
247
248 String finalName = ReleaseUtil.interpolate(originalFinalName, releaseModel);
249
250
251 if (finalName.contains(Artifact.SNAPSHOT_VERSION)) {
252 throw new ReleaseFailureException(
253 "Cannot reliably adjust the finalName of project: " + releaseProject.getId());
254 }
255 releaseModel.getBuild().setFinalName(finalName);
256
257
258 Scm scm = releaseModel.getScm();
259
260 if (scm != null) {
261 ScmRepository scmRepository = getScmRepository(releaseDescriptor, releaseEnvironment);
262 ScmTranslator scmTranslator = getScmTranslator(scmRepository);
263
264 if (scmTranslator != null) {
265 releaseModel.setScm(createReleaseScm(releaseModel.getScm(), scmTranslator, releaseDescriptor));
266 } else {
267 String message = "No SCM translator found - skipping rewrite";
268
269 result.appendDebug(message);
270
271 getLogger().debug(message);
272 }
273 }
274
275
276 releaseModel.setDependencies(createReleaseDependencies(releaseDescriptor, releaseProject));
277
278
279 releaseModel.getBuild().setPlugins(createReleasePlugins(releaseDescriptor, releaseProject));
280
281
282 releaseModel.getReporting().setPlugins(createReleaseReportPlugins(releaseDescriptor, releaseProject));
283
284
285 releaseModel.getBuild().setExtensions(createReleaseExtensions(releaseDescriptor, releaseProject));
286
287 unalignFromBaseDirectory(releaseModel, project.getBasedir());
288
289 return releaseModel;
290 }
291
292 private void unalignFromBaseDirectory(Model releaseModel, File basedir) {
293 Model rawSuperModel = superPomProvider.getSuperModel(releaseModel.getModelVersion());
294
295 ModelBuildingRequest buildingRequest = new DefaultModelBuildingRequest();
296 buildingRequest.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_STRICT);
297
298
299 Properties properties = new Properties();
300 properties.put("project.version", releaseModel.getVersion());
301 properties.put("project.artifactId", releaseModel.getArtifactId());
302 buildingRequest.setUserProperties(properties);
303
304 Model interpolatedSuperModel =
305 modelInterpolator.interpolateModel(rawSuperModel.clone(), basedir, buildingRequest, null);
306
307 Build currentBuild = releaseModel.getBuild();
308 Build interpolatedSuperBuild = interpolatedSuperModel.getBuild();
309 Build rawSuperBuild = rawSuperModel.getBuild();
310
311 currentBuild.setSourceDirectory(resolvePath(
312 basedir.toPath(),
313 currentBuild.getSourceDirectory(),
314 interpolatedSuperBuild.getSourceDirectory(),
315 rawSuperBuild.getSourceDirectory()));
316 currentBuild.setScriptSourceDirectory(resolvePath(
317 basedir.toPath(),
318 currentBuild.getScriptSourceDirectory(),
319 interpolatedSuperBuild.getScriptSourceDirectory(),
320 rawSuperBuild.getScriptSourceDirectory()));
321 currentBuild.setTestSourceDirectory(resolvePath(
322 basedir.toPath(),
323 currentBuild.getTestSourceDirectory(),
324 interpolatedSuperBuild.getTestSourceDirectory(),
325 rawSuperBuild.getTestSourceDirectory()));
326 currentBuild.setOutputDirectory(resolvePath(
327 basedir.toPath(),
328 currentBuild.getOutputDirectory(),
329 interpolatedSuperBuild.getOutputDirectory(),
330 rawSuperBuild.getOutputDirectory()));
331 currentBuild.setTestOutputDirectory(resolvePath(
332 basedir.toPath(),
333 currentBuild.getTestOutputDirectory(),
334 interpolatedSuperBuild.getTestOutputDirectory(),
335 rawSuperBuild.getTestOutputDirectory()));
336 currentBuild.setDirectory(resolvePath(
337 basedir.toPath(),
338 currentBuild.getDirectory(),
339 interpolatedSuperBuild.getDirectory(),
340 rawSuperBuild.getDirectory()));
341
342 for (Resource currentResource : currentBuild.getResources()) {
343 Map<String, String> superResourceDirectories =
344 new LinkedHashMap<>(interpolatedSuperBuild.getResources().size());
345 for (int i = 0; i < interpolatedSuperBuild.getResources().size(); i++) {
346 superResourceDirectories.put(
347 interpolatedSuperBuild.getResources().get(i).getDirectory(),
348 rawSuperBuild.getResources().get(i).getDirectory());
349 }
350 currentResource.setDirectory(
351 resolvePath(basedir.toPath(), currentResource.getDirectory(), superResourceDirectories));
352 }
353
354 for (Resource currentResource : currentBuild.getTestResources()) {
355 Map<String, String> superResourceDirectories = new LinkedHashMap<>(
356 interpolatedSuperBuild.getTestResources().size());
357 for (int i = 0; i < interpolatedSuperBuild.getTestResources().size(); i++) {
358 superResourceDirectories.put(
359 interpolatedSuperBuild.getTestResources().get(i).getDirectory(),
360 rawSuperBuild.getTestResources().get(i).getDirectory());
361 }
362 currentResource.setDirectory(
363 resolvePath(basedir.toPath(), currentResource.getDirectory(), superResourceDirectories));
364 }
365
366 releaseModel
367 .getReporting()
368 .setOutputDirectory(resolvePath(
369 basedir.toPath(),
370 releaseModel.getReporting().getOutputDirectory(),
371 interpolatedSuperModel.getReporting().getOutputDirectory(),
372 rawSuperModel.getReporting().getOutputDirectory()));
373 }
374
375 private String resolvePath(Path basedir, String current, String superInterpolated, String superRaw) {
376 return basedir.resolve(current).equals(basedir.resolve(superInterpolated)) ? superRaw : current;
377 }
378
379 private String resolvePath(
380 Path basedir, String current, Map<String , String > superValues) {
381 for (Map.Entry<String, String> superValue : superValues.entrySet()) {
382 if (basedir.resolve(current).equals(basedir.resolve(superValue.getKey()))) {
383 return superValue.getValue();
384 }
385 }
386 return current;
387 }
388
389 private String findOriginalFinalName(MavenProject project) {
390 if (project.getOriginalModel().getBuild() != null
391 && project.getOriginalModel().getBuild().getFinalName() != null) {
392 return project.getOriginalModel().getBuild().getFinalName();
393 } else if (project.hasParent()) {
394 return findOriginalFinalName(project.getParent());
395 } else {
396 return null;
397 }
398 }
399
400 @Override
401 public ReleaseResult simulate(
402 ReleaseDescriptor releaseDescriptor,
403 ReleaseEnvironment releaseEnvironment,
404 List<MavenProject> reactorProjects)
405 throws ReleaseExecutionException, ReleaseFailureException {
406 return execute(releaseDescriptor, releaseEnvironment, reactorProjects, true);
407 }
408
409 private String getNextVersion(ReleaseDescriptor releaseDescriptor, String groupId, String artifactId)
410 throws ReleaseFailureException {
411
412
413 String id = ArtifactUtils.versionlessKey(groupId, artifactId);
414
415 String nextVersion = releaseDescriptor.getProjectReleaseVersion(id);
416
417 if (nextVersion == null) {
418 throw new ReleaseFailureException("Version for '" + id + "' was not mapped");
419 }
420
421 return nextVersion;
422 }
423
424 private ScmTranslator getScmTranslator(ScmRepository scmRepository) {
425 return scmTranslators.get(scmRepository.getProvider());
426 }
427
428 private Scm createReleaseScm(Scm scm, ScmTranslator scmTranslator, ReleaseDescriptor releaseDescriptor) {
429
430
431 String tag = releaseDescriptor.getScmReleaseLabel();
432 String tagBase = releaseDescriptor.getScmTagBase();
433
434 Scm releaseScm = new Scm();
435
436 if (scm.getConnection() != null) {
437 String value = scmTranslator.translateTagUrl(scm.getConnection(), tag, tagBase);
438 releaseScm.setConnection(value);
439 }
440
441 if (scm.getDeveloperConnection() != null) {
442 String value = scmTranslator.translateTagUrl(scm.getDeveloperConnection(), tag, tagBase);
443 releaseScm.setDeveloperConnection(value);
444 }
445
446 if (scm.getUrl() != null) {
447 String value = scmTranslator.translateTagUrl(scm.getUrl(), tag, tagBase);
448 releaseScm.setUrl(value);
449 }
450
451 if (scm.getTag() != null) {
452 String value = scmTranslator.resolveTag(scm.getTag());
453 releaseScm.setTag(value);
454 }
455
456 return releaseScm;
457 }
458
459 private List<Dependency> createReleaseDependencies(ReleaseDescriptor releaseDescriptor, MavenProject project)
460 throws ReleaseFailureException {
461 Set<Artifact> artifacts = project.getDependencyArtifacts();
462
463 List<Dependency> releaseDependencies = null;
464
465 if (artifacts != null) {
466
467 List<Artifact> orderedArtifacts = new ArrayList<>(artifacts);
468 Collections.sort(orderedArtifacts);
469
470 releaseDependencies = new ArrayList<>();
471
472 for (Artifact artifact : orderedArtifacts) {
473 if (artifact.getVersion() == null) {
474 artifact.setVersion(project.getArtifactMap()
475 .get(ArtifactUtils.versionlessKey(artifact))
476 .getVersion());
477 }
478
479 Dependency releaseDependency = new Dependency();
480
481 releaseDependency.setGroupId(artifact.getGroupId());
482 releaseDependency.setArtifactId(artifact.getArtifactId());
483
484 String version = getReleaseVersion(releaseDescriptor, artifact);
485
486 releaseDependency.setVersion(version);
487 releaseDependency.setType(artifact.getType());
488 releaseDependency.setScope(artifact.getScope());
489 releaseDependency.setClassifier(artifact.getClassifier());
490
491 releaseDependencies.add(releaseDependency);
492 }
493 }
494
495 return releaseDependencies;
496 }
497
498 private String getReleaseVersion(ReleaseDescriptor releaseDescriptor, Artifact artifact)
499 throws ReleaseFailureException {
500 String key = ArtifactUtils.versionlessKey(artifact);
501
502 String originalVersion = releaseDescriptor.getProjectOriginalVersion(key);
503 String mappedVersion = releaseDescriptor.getProjectReleaseVersion(key);
504
505 String version = artifact.getVersion();
506
507 if (version.equals(originalVersion)) {
508 if (mappedVersion != null) {
509 version = mappedVersion;
510 } else {
511 throw new ReleaseFailureException("Version '" + version + "' for '" + key + "' was not mapped");
512 }
513 } else {
514 if (!ArtifactUtils.isSnapshot(version)) {
515 version = artifact.getBaseVersion();
516 }
517 }
518
519 return version;
520 }
521
522 private List<Plugin> createReleasePlugins(ReleaseDescriptor releaseDescriptor, MavenProject project)
523 throws ReleaseFailureException {
524 List<Plugin> releasePlugins = null;
525
526
527 Build build = project.getOriginalModel().getBuild();
528
529 if (build != null) {
530 List<Plugin> plugins = build.getPlugins();
531
532 if (plugins != null) {
533 Map<String, Artifact> artifactsById = project.getPluginArtifactMap();
534
535 releasePlugins = new ArrayList<>();
536
537 for (Plugin plugin : plugins) {
538 String id = ArtifactUtils.versionlessKey(plugin.getGroupId(), plugin.getArtifactId());
539 Artifact artifact = artifactsById.get(id);
540 String version = getReleaseVersion(releaseDescriptor, artifact);
541
542 Plugin releasePlugin = new Plugin();
543 releasePlugin.setGroupId(plugin.getGroupId());
544 releasePlugin.setArtifactId(plugin.getArtifactId());
545 releasePlugin.setVersion(version);
546 if (plugin.getExtensions() != null) {
547 releasePlugin.setExtensions(plugin.isExtensions());
548 }
549 releasePlugin.setExecutions(plugin.getExecutions());
550 releasePlugin.setDependencies(plugin.getDependencies());
551 releasePlugin.setInherited(plugin.getInherited());
552 if (plugin.getConfiguration() != null) {
553 releasePlugin.setConfiguration(plugin.getConfiguration());
554 }
555
556 releasePlugins.add(releasePlugin);
557 }
558 }
559 }
560
561 return releasePlugins;
562 }
563
564 private List<ReportPlugin> createReleaseReportPlugins(ReleaseDescriptor releaseDescriptor, MavenProject project)
565 throws ReleaseFailureException {
566 List<ReportPlugin> releaseReportPlugins = null;
567
568 Reporting reporting = project.getModel().getReporting();
569
570 if (reporting != null) {
571 List<ReportPlugin> reportPlugins = reporting.getPlugins();
572
573 if (reportPlugins != null) {
574 Map<String, Artifact> artifactsById = project.getReportArtifactMap();
575
576 releaseReportPlugins = new ArrayList<>();
577
578 for (ReportPlugin reportPlugin : reportPlugins) {
579 String id = ArtifactUtils.versionlessKey(reportPlugin.getGroupId(), reportPlugin.getArtifactId());
580 Artifact artifact = artifactsById.get(id);
581 String version = getReleaseVersion(releaseDescriptor, artifact);
582
583 ReportPlugin releaseReportPlugin = new ReportPlugin();
584 releaseReportPlugin.setGroupId(reportPlugin.getGroupId());
585 releaseReportPlugin.setArtifactId(reportPlugin.getArtifactId());
586 releaseReportPlugin.setVersion(version);
587 releaseReportPlugin.setInherited(reportPlugin.getInherited());
588 releaseReportPlugin.setConfiguration(reportPlugin.getConfiguration());
589 releaseReportPlugin.setReportSets(reportPlugin.getReportSets());
590
591 releaseReportPlugins.add(releaseReportPlugin);
592 }
593 }
594 }
595
596 return releaseReportPlugins;
597 }
598
599 private List<Extension> createReleaseExtensions(ReleaseDescriptor releaseDescriptor, MavenProject project)
600 throws ReleaseFailureException {
601 List<Extension> releaseExtensions = null;
602
603
604 Build build = project.getOriginalModel().getBuild();
605
606 if (build != null) {
607 List<Extension> extensions = build.getExtensions();
608
609 if (extensions != null) {
610 releaseExtensions = new ArrayList<>();
611
612 for (Extension extension : extensions) {
613 String id = ArtifactUtils.versionlessKey(extension.getGroupId(), extension.getArtifactId());
614 Artifact artifact = project.getExtensionArtifactMap().get(id);
615 String version = getReleaseVersion(releaseDescriptor, artifact);
616
617 Extension releaseExtension = new Extension();
618 releaseExtension.setGroupId(extension.getGroupId());
619 releaseExtension.setArtifactId(extension.getArtifactId());
620 releaseExtension.setVersion(version);
621
622 releaseExtensions.add(releaseExtension);
623 }
624 }
625 }
626
627 return releaseExtensions;
628 }
629
630
631
632
633 @Override
634 public ReleaseResult clean(List<MavenProject> reactorProjects) {
635 ReleaseResult result = new ReleaseResult();
636
637 for (MavenProject project : reactorProjects) {
638 File releasePom = ReleaseUtil.getReleasePom(project);
639
640
641 if (releasePom != null && releasePom.exists()) {
642 logInfo(result, "Deleting release POM for '" + project.getName() + "'...");
643
644 if (!releasePom.delete()) {
645 logWarn(result, "Cannot delete release POM: " + releasePom);
646 }
647 }
648 }
649
650 result.setResultCode(ReleaseResult.SUCCESS);
651
652 return result;
653 }
654 }