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 projectVersion = releaseModel.getVersion();
234 String releaseVersion = getNextVersion(releaseDescriptor, project.getGroupId(), project.getArtifactId());
235 releaseModel.setVersion(releaseVersion);
236
237 String originalFinalName = releaseModel.getBuild().getFinalName();
238
239 if (!FINALNAME_EXPRESSION.equals(originalFinalName)) {
240 originalFinalName = findOriginalFinalName(project);
241
242 if (originalFinalName == null) {
243
244 originalFinalName = FINALNAME_EXPRESSION;
245 }
246 }
247
248
249 String finalName = ReleaseUtil.interpolate(originalFinalName, releaseModel);
250
251
252 if (finalName.contains(Artifact.SNAPSHOT_VERSION)) {
253 throw new ReleaseFailureException(
254 "Cannot reliably adjust the finalName of project: " + releaseProject.getId());
255 }
256 releaseModel.getBuild().setFinalName(finalName);
257
258
259 Scm scm = releaseModel.getScm();
260
261 if (scm != null) {
262 ScmRepository scmRepository = getScmRepository(releaseDescriptor, releaseEnvironment);
263 ScmTranslator scmTranslator = getScmTranslator(scmRepository);
264
265 if (scmTranslator != null) {
266 releaseModel.setScm(createReleaseScm(releaseModel.getScm(), scmTranslator, releaseDescriptor));
267 } else {
268 String message = "No SCM translator found - skipping rewrite";
269
270 result.appendDebug(message);
271
272 getLogger().debug(message);
273 }
274 }
275
276
277 releaseModel.setDependencies(createReleaseDependencies(releaseDescriptor, releaseProject));
278
279
280 releaseModel.getBuild().setPlugins(createReleasePlugins(releaseDescriptor, releaseProject));
281
282
283 releaseModel.getReporting().setPlugins(createReleaseReportPlugins(releaseDescriptor, releaseProject));
284
285
286 releaseModel.getBuild().setExtensions(createReleaseExtensions(releaseDescriptor, releaseProject));
287
288 unalignFromBaseDirectory(releaseModel, project.getBasedir());
289
290 return releaseModel;
291 }
292
293 private void unalignFromBaseDirectory(Model releaseModel, File basedir) {
294 Model rawSuperModel = superPomProvider.getSuperModel(releaseModel.getModelVersion());
295
296 ModelBuildingRequest buildingRequest = new DefaultModelBuildingRequest();
297 buildingRequest.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_STRICT);
298
299
300 Properties properties = new Properties();
301 properties.put("project.version", releaseModel.getVersion());
302 properties.put("project.artifactId", releaseModel.getArtifactId());
303 buildingRequest.setUserProperties(properties);
304
305 Model interpolatedSuperModel =
306 modelInterpolator.interpolateModel(rawSuperModel.clone(), basedir, buildingRequest, null);
307
308 Build currentBuild = releaseModel.getBuild();
309 Build interpolatedSuperBuild = interpolatedSuperModel.getBuild();
310 Build rawSuperBuild = rawSuperModel.getBuild();
311
312 currentBuild.setSourceDirectory(resolvePath(
313 basedir.toPath(),
314 currentBuild.getSourceDirectory(),
315 interpolatedSuperBuild.getSourceDirectory(),
316 rawSuperBuild.getSourceDirectory()));
317 currentBuild.setScriptSourceDirectory(resolvePath(
318 basedir.toPath(),
319 currentBuild.getScriptSourceDirectory(),
320 interpolatedSuperBuild.getScriptSourceDirectory(),
321 rawSuperBuild.getScriptSourceDirectory()));
322 currentBuild.setTestSourceDirectory(resolvePath(
323 basedir.toPath(),
324 currentBuild.getTestSourceDirectory(),
325 interpolatedSuperBuild.getTestSourceDirectory(),
326 rawSuperBuild.getTestSourceDirectory()));
327 currentBuild.setOutputDirectory(resolvePath(
328 basedir.toPath(),
329 currentBuild.getOutputDirectory(),
330 interpolatedSuperBuild.getOutputDirectory(),
331 rawSuperBuild.getOutputDirectory()));
332 currentBuild.setTestOutputDirectory(resolvePath(
333 basedir.toPath(),
334 currentBuild.getTestOutputDirectory(),
335 interpolatedSuperBuild.getTestOutputDirectory(),
336 rawSuperBuild.getTestOutputDirectory()));
337 currentBuild.setDirectory(resolvePath(
338 basedir.toPath(),
339 currentBuild.getDirectory(),
340 interpolatedSuperBuild.getDirectory(),
341 rawSuperBuild.getDirectory()));
342
343 for (Resource currentResource : currentBuild.getResources()) {
344 Map<String, String> superResourceDirectories =
345 new LinkedHashMap<>(interpolatedSuperBuild.getResources().size());
346 for (int i = 0; i < interpolatedSuperBuild.getResources().size(); i++) {
347 superResourceDirectories.put(
348 interpolatedSuperBuild.getResources().get(i).getDirectory(),
349 rawSuperBuild.getResources().get(i).getDirectory());
350 }
351 currentResource.setDirectory(
352 resolvePath(basedir.toPath(), currentResource.getDirectory(), superResourceDirectories));
353 }
354
355 for (Resource currentResource : currentBuild.getTestResources()) {
356 Map<String, String> superResourceDirectories = new LinkedHashMap<>(
357 interpolatedSuperBuild.getTestResources().size());
358 for (int i = 0; i < interpolatedSuperBuild.getTestResources().size(); i++) {
359 superResourceDirectories.put(
360 interpolatedSuperBuild.getTestResources().get(i).getDirectory(),
361 rawSuperBuild.getTestResources().get(i).getDirectory());
362 }
363 currentResource.setDirectory(
364 resolvePath(basedir.toPath(), currentResource.getDirectory(), superResourceDirectories));
365 }
366
367 releaseModel
368 .getReporting()
369 .setOutputDirectory(resolvePath(
370 basedir.toPath(),
371 releaseModel.getReporting().getOutputDirectory(),
372 interpolatedSuperModel.getReporting().getOutputDirectory(),
373 rawSuperModel.getReporting().getOutputDirectory()));
374 }
375
376 private String resolvePath(Path basedir, String current, String superInterpolated, String superRaw) {
377 return basedir.resolve(current).equals(basedir.resolve(superInterpolated)) ? superRaw : current;
378 }
379
380 private String resolvePath(
381 Path basedir, String current, Map<String , String > superValues) {
382 for (Map.Entry<String, String> superValue : superValues.entrySet()) {
383 if (basedir.resolve(current).equals(basedir.resolve(superValue.getKey()))) {
384 return superValue.getValue();
385 }
386 }
387 return current;
388 }
389
390 private String findOriginalFinalName(MavenProject project) {
391 if (project.getOriginalModel().getBuild() != null
392 && project.getOriginalModel().getBuild().getFinalName() != null) {
393 return project.getOriginalModel().getBuild().getFinalName();
394 } else if (project.hasParent()) {
395 return findOriginalFinalName(project.getParent());
396 } else {
397 return null;
398 }
399 }
400
401 @Override
402 public ReleaseResult simulate(
403 ReleaseDescriptor releaseDescriptor,
404 ReleaseEnvironment releaseEnvironment,
405 List<MavenProject> reactorProjects)
406 throws ReleaseExecutionException, ReleaseFailureException {
407 return execute(releaseDescriptor, releaseEnvironment, reactorProjects, true);
408 }
409
410 private String getNextVersion(ReleaseDescriptor releaseDescriptor, String groupId, String artifactId)
411 throws ReleaseFailureException {
412
413
414 String id = ArtifactUtils.versionlessKey(groupId, artifactId);
415
416 String nextVersion = releaseDescriptor.getProjectReleaseVersion(id);
417
418 if (nextVersion == null) {
419 throw new ReleaseFailureException("Version for '" + id + "' was not mapped");
420 }
421
422 return nextVersion;
423 }
424
425 private ScmTranslator getScmTranslator(ScmRepository scmRepository) {
426 return scmTranslators.get(scmRepository.getProvider());
427 }
428
429 private Scm createReleaseScm(Scm scm, ScmTranslator scmTranslator, ReleaseDescriptor releaseDescriptor) {
430
431
432 String tag = releaseDescriptor.getScmReleaseLabel();
433 String tagBase = releaseDescriptor.getScmTagBase();
434
435 Scm releaseScm = new Scm();
436
437 if (scm.getConnection() != null) {
438 String value = scmTranslator.translateTagUrl(scm.getConnection(), tag, tagBase);
439 releaseScm.setConnection(value);
440 }
441
442 if (scm.getDeveloperConnection() != null) {
443 String value = scmTranslator.translateTagUrl(scm.getDeveloperConnection(), tag, tagBase);
444 releaseScm.setDeveloperConnection(value);
445 }
446
447 if (scm.getUrl() != null) {
448 String value = scmTranslator.translateTagUrl(scm.getUrl(), tag, tagBase);
449 releaseScm.setUrl(value);
450 }
451
452 if (scm.getTag() != null) {
453 String value = scmTranslator.resolveTag(scm.getTag());
454 releaseScm.setTag(value);
455 }
456
457 return releaseScm;
458 }
459
460 private List<Dependency> createReleaseDependencies(ReleaseDescriptor releaseDescriptor, MavenProject project)
461 throws ReleaseFailureException {
462 Set<Artifact> artifacts = project.getDependencyArtifacts();
463
464 List<Dependency> releaseDependencies = null;
465
466 if (artifacts != null) {
467
468 List<Artifact> orderedArtifacts = new ArrayList<>(artifacts);
469 Collections.sort(orderedArtifacts);
470
471 releaseDependencies = new ArrayList<>();
472
473 for (Artifact artifact : orderedArtifacts) {
474 if (artifact.getVersion() == null) {
475 artifact.setVersion(project.getArtifactMap()
476 .get(ArtifactUtils.versionlessKey(artifact))
477 .getVersion());
478 }
479
480 Dependency releaseDependency = new Dependency();
481
482 releaseDependency.setGroupId(artifact.getGroupId());
483 releaseDependency.setArtifactId(artifact.getArtifactId());
484
485 String version = getReleaseVersion(releaseDescriptor, artifact);
486
487 releaseDependency.setVersion(version);
488 releaseDependency.setType(artifact.getType());
489 releaseDependency.setScope(artifact.getScope());
490 releaseDependency.setClassifier(artifact.getClassifier());
491
492 releaseDependencies.add(releaseDependency);
493 }
494 }
495
496 return releaseDependencies;
497 }
498
499 private String getReleaseVersion(ReleaseDescriptor releaseDescriptor, Artifact artifact)
500 throws ReleaseFailureException {
501 String key = ArtifactUtils.versionlessKey(artifact);
502
503 String originalVersion = releaseDescriptor.getProjectOriginalVersion(key);
504 String mappedVersion = releaseDescriptor.getProjectReleaseVersion(key);
505
506 String version = artifact.getVersion();
507
508 if (version.equals(originalVersion)) {
509 if (mappedVersion != null) {
510 version = mappedVersion;
511 } else {
512 throw new ReleaseFailureException("Version '" + version + "' for '" + key + "' was not mapped");
513 }
514 } else {
515 if (!ArtifactUtils.isSnapshot(version)) {
516 version = artifact.getBaseVersion();
517 }
518 }
519
520 return version;
521 }
522
523 private List<Plugin> createReleasePlugins(ReleaseDescriptor releaseDescriptor, MavenProject project)
524 throws ReleaseFailureException {
525 List<Plugin> releasePlugins = null;
526
527
528 Build build = project.getOriginalModel().getBuild();
529
530 if (build != null) {
531 List<Plugin> plugins = build.getPlugins();
532
533 if (plugins != null) {
534 Map<String, Artifact> artifactsById = project.getPluginArtifactMap();
535
536 releasePlugins = new ArrayList<>();
537
538 for (Plugin plugin : plugins) {
539 String id = ArtifactUtils.versionlessKey(plugin.getGroupId(), plugin.getArtifactId());
540 Artifact artifact = artifactsById.get(id);
541 String version = getReleaseVersion(releaseDescriptor, artifact);
542
543 Plugin releasePlugin = new Plugin();
544 releasePlugin.setGroupId(plugin.getGroupId());
545 releasePlugin.setArtifactId(plugin.getArtifactId());
546 releasePlugin.setVersion(version);
547 if (plugin.getExtensions() != null) {
548 releasePlugin.setExtensions(plugin.isExtensions());
549 }
550 releasePlugin.setExecutions(plugin.getExecutions());
551 releasePlugin.setDependencies(plugin.getDependencies());
552 releasePlugin.setInherited(plugin.getInherited());
553 if (plugin.getConfiguration() != null) {
554 releasePlugin.setConfiguration(plugin.getConfiguration());
555 }
556
557 releasePlugins.add(releasePlugin);
558 }
559 }
560 }
561
562 return releasePlugins;
563 }
564
565 private List<ReportPlugin> createReleaseReportPlugins(ReleaseDescriptor releaseDescriptor, MavenProject project)
566 throws ReleaseFailureException {
567 List<ReportPlugin> releaseReportPlugins = null;
568
569 Reporting reporting = project.getModel().getReporting();
570
571 if (reporting != null) {
572 List<ReportPlugin> reportPlugins = reporting.getPlugins();
573
574 if (reportPlugins != null) {
575 Map<String, Artifact> artifactsById = project.getReportArtifactMap();
576
577 releaseReportPlugins = new ArrayList<>();
578
579 for (ReportPlugin reportPlugin : reportPlugins) {
580 String id = ArtifactUtils.versionlessKey(reportPlugin.getGroupId(), reportPlugin.getArtifactId());
581 Artifact artifact = artifactsById.get(id);
582 String version = getReleaseVersion(releaseDescriptor, artifact);
583
584 ReportPlugin releaseReportPlugin = new ReportPlugin();
585 releaseReportPlugin.setGroupId(reportPlugin.getGroupId());
586 releaseReportPlugin.setArtifactId(reportPlugin.getArtifactId());
587 releaseReportPlugin.setVersion(version);
588 releaseReportPlugin.setInherited(reportPlugin.getInherited());
589 releaseReportPlugin.setConfiguration(reportPlugin.getConfiguration());
590 releaseReportPlugin.setReportSets(reportPlugin.getReportSets());
591
592 releaseReportPlugins.add(releaseReportPlugin);
593 }
594 }
595 }
596
597 return releaseReportPlugins;
598 }
599
600 private List<Extension> createReleaseExtensions(ReleaseDescriptor releaseDescriptor, MavenProject project)
601 throws ReleaseFailureException {
602 List<Extension> releaseExtensions = null;
603
604
605 Build build = project.getOriginalModel().getBuild();
606
607 if (build != null) {
608 List<Extension> extensions = build.getExtensions();
609
610 if (extensions != null) {
611 releaseExtensions = new ArrayList<>();
612
613 for (Extension extension : extensions) {
614 String id = ArtifactUtils.versionlessKey(extension.getGroupId(), extension.getArtifactId());
615 Artifact artifact = project.getExtensionArtifactMap().get(id);
616 String version = getReleaseVersion(releaseDescriptor, artifact);
617
618 Extension releaseExtension = new Extension();
619 releaseExtension.setGroupId(extension.getGroupId());
620 releaseExtension.setArtifactId(extension.getArtifactId());
621 releaseExtension.setVersion(version);
622
623 releaseExtensions.add(releaseExtension);
624 }
625 }
626 }
627
628 return releaseExtensions;
629 }
630
631
632
633
634 @Override
635 public ReleaseResult clean(List<MavenProject> reactorProjects) {
636 ReleaseResult result = new ReleaseResult();
637
638 for (MavenProject project : reactorProjects) {
639 File releasePom = ReleaseUtil.getReleasePom(project);
640
641
642 if (releasePom != null && releasePom.exists()) {
643 logInfo(result, "Deleting release POM for '" + project.getName() + "'...");
644
645 if (!releasePom.delete()) {
646 logWarn(result, "Cannot delete release POM: " + releasePom);
647 }
648 }
649 }
650
651 result.setResultCode(ReleaseResult.SUCCESS);
652
653 return result;
654 }
655 }