blob: 1261b2547d27f5185986d128818d709465eb3498 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.shared.release.phase;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Extension;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.Reporting;
import org.apache.maven.model.Resource;
import org.apache.maven.model.Scm;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.model.interpolation.ModelInterpolator;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.model.superpom.SuperPomProvider;
import org.apache.maven.project.MavenProject;
import org.apache.maven.scm.ScmException;
import org.apache.maven.scm.ScmFileSet;
import org.apache.maven.scm.command.add.AddScmResult;
import org.apache.maven.scm.provider.ScmProvider;
import org.apache.maven.scm.repository.ScmRepository;
import org.apache.maven.shared.release.ReleaseExecutionException;
import org.apache.maven.shared.release.ReleaseFailureException;
import org.apache.maven.shared.release.ReleaseResult;
import org.apache.maven.shared.release.config.ReleaseDescriptor;
import org.apache.maven.shared.release.env.ReleaseEnvironment;
import org.apache.maven.shared.release.scm.ReleaseScmCommandException;
import org.apache.maven.shared.release.scm.ScmRepositoryConfigurator;
import org.apache.maven.shared.release.scm.ScmTranslator;
import org.apache.maven.shared.release.util.ReleaseUtil;
import org.codehaus.plexus.util.WriterFactory;
import static java.util.Objects.requireNonNull;
/**
* Generate release POMs.
*
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
* @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
*/
@Singleton
@Named("generate-release-poms")
public class GenerateReleasePomsPhase extends AbstractReleasePomsPhase implements ResourceGenerator {
private static final String FINALNAME_EXPRESSION = "${project.artifactId}-${project.version}";
private final SuperPomProvider superPomProvider;
private final ModelInterpolator modelInterpolator;
/**
* SCM URL translators mapped by provider name.
*/
private final Map<String, ScmTranslator> scmTranslators;
@Inject
public GenerateReleasePomsPhase(
ScmRepositoryConfigurator scmRepositoryConfigurator,
SuperPomProvider superPomProvider,
ModelInterpolator modelInterpolator,
Map<String, ScmTranslator> scmTranslators) {
super(scmRepositoryConfigurator);
this.superPomProvider = requireNonNull(superPomProvider);
this.modelInterpolator = requireNonNull(modelInterpolator);
this.scmTranslators = requireNonNull(scmTranslators);
}
/*
* @see org.apache.maven.shared.release.phase.ReleasePhase#execute(org.apache.maven.shared.release.config.ReleaseDescriptor,
* org.apache.maven.settings.Settings, java.util.List)
*/
@Override
public ReleaseResult execute(
ReleaseDescriptor releaseDescriptor,
ReleaseEnvironment releaseEnvironment,
List<MavenProject> reactorProjects)
throws ReleaseExecutionException, ReleaseFailureException {
return execute(releaseDescriptor, releaseEnvironment, reactorProjects, false);
}
private ReleaseResult execute(
ReleaseDescriptor releaseDescriptor,
ReleaseEnvironment releaseEnvironment,
List<MavenProject> reactorProjects,
boolean simulate)
throws ReleaseExecutionException, ReleaseFailureException {
ReleaseResult result = new ReleaseResult();
if (releaseDescriptor.isGenerateReleasePoms()) {
logInfo(result, "Generating release POMs...");
generateReleasePoms(releaseDescriptor, releaseEnvironment, reactorProjects, simulate, result);
} else {
logInfo(result, "Not generating release POMs");
}
result.setResultCode(ReleaseResult.SUCCESS);
return result;
}
private void generateReleasePoms(
ReleaseDescriptor releaseDescriptor,
ReleaseEnvironment releaseEnvironment,
List<MavenProject> reactorProjects,
boolean simulate,
ReleaseResult result)
throws ReleaseExecutionException, ReleaseFailureException {
List<File> releasePoms = new ArrayList<>();
for (MavenProject project : reactorProjects) {
logInfo(result, "Generating release POM for '" + project.getName() + "'...");
releasePoms.add(generateReleasePom(project, releaseDescriptor, releaseEnvironment, result));
}
addReleasePomsToScm(releaseDescriptor, releaseEnvironment, reactorProjects, simulate, result, releasePoms);
}
private File generateReleasePom(
MavenProject project,
ReleaseDescriptor releaseDescriptor,
ReleaseEnvironment releaseEnvironment,
ReleaseResult result)
throws ReleaseExecutionException, ReleaseFailureException {
// create release pom
Model releasePom = createReleaseModel(project, releaseDescriptor, releaseEnvironment, result);
// write release pom to file
MavenXpp3Writer pomWriter = new MavenXpp3Writer();
File releasePomFile = ReleaseUtil.getReleasePom(project);
// MRELEASE-273 : A release pom can be null
if (releasePomFile == null) {
throw new ReleaseExecutionException("Cannot generate release POM : pom file is null");
}
try (Writer fileWriter = WriterFactory.newXmlWriter(releasePomFile)) {
pomWriter.write(fileWriter, releasePom);
} catch (IOException exception) {
throw new ReleaseExecutionException("Cannot generate release POM", exception);
}
return releasePomFile;
}
private void addReleasePomsToScm(
ReleaseDescriptor releaseDescriptor,
ReleaseEnvironment releaseEnvironment,
List<MavenProject> reactorProjects,
boolean simulate,
ReleaseResult result,
List<File> releasePoms)
throws ReleaseFailureException, ReleaseExecutionException {
if (simulate) {
logInfo(result, "Full run would be adding " + releasePoms);
} else {
ScmRepository scmRepository = getScmRepository(releaseDescriptor, releaseEnvironment);
ScmProvider scmProvider = getScmProvider(scmRepository);
MavenProject rootProject = ReleaseUtil.getRootProject(reactorProjects);
ScmFileSet scmFileSet = new ScmFileSet(rootProject.getFile().getParentFile(), releasePoms);
try {
AddScmResult scmResult = scmProvider.add(scmRepository, scmFileSet);
if (!scmResult.isSuccess()) {
throw new ReleaseScmCommandException("Cannot add release POM to SCM", scmResult);
}
} catch (ScmException exception) {
throw new ReleaseExecutionException(
"Cannot add release POM to SCM: " + exception.getMessage(), exception);
}
}
}
private Model createReleaseModel(
MavenProject project,
ReleaseDescriptor releaseDescriptor,
ReleaseEnvironment releaseEnvironment,
ReleaseResult result)
throws ReleaseFailureException, ReleaseExecutionException {
MavenProject releaseProject = project.clone();
Model releaseModel = releaseProject.getModel();
// the release POM should reflect bits of these which were injected at build time...
// we don't need these polluting the POM.
releaseModel.setParent(null);
releaseModel.setProfiles(Collections.emptyList());
releaseModel.setDependencyManagement(null);
releaseProject.getBuild().setPluginManagement(null);
// update project version
String projectVersion = releaseModel.getVersion();
String releaseVersion = getNextVersion(releaseDescriptor, project.getGroupId(), project.getArtifactId());
releaseModel.setVersion(releaseVersion);
String originalFinalName = releaseModel.getBuild().getFinalName();
// update final name if implicit
if (!FINALNAME_EXPRESSION.equals(originalFinalName)) {
originalFinalName = findOriginalFinalName(project);
if (originalFinalName == null) {
// as defined in super-pom
originalFinalName = FINALNAME_EXPRESSION;
}
}
// make finalName always explicit
String finalName = ReleaseUtil.interpolate(originalFinalName, releaseModel);
// still required?
if (finalName.contains(Artifact.SNAPSHOT_VERSION)) {
throw new ReleaseFailureException(
"Cannot reliably adjust the finalName of project: " + releaseProject.getId());
}
releaseModel.getBuild().setFinalName(finalName);
// update scm
Scm scm = releaseModel.getScm();
if (scm != null) {
ScmRepository scmRepository = getScmRepository(releaseDescriptor, releaseEnvironment);
ScmTranslator scmTranslator = getScmTranslator(scmRepository);
if (scmTranslator != null) {
releaseModel.setScm(createReleaseScm(releaseModel.getScm(), scmTranslator, releaseDescriptor));
} else {
String message = "No SCM translator found - skipping rewrite";
result.appendDebug(message);
getLogger().debug(message);
}
}
// rewrite dependencies
releaseModel.setDependencies(createReleaseDependencies(releaseDescriptor, releaseProject));
// rewrite plugins
releaseModel.getBuild().setPlugins(createReleasePlugins(releaseDescriptor, releaseProject));
// rewrite reports
releaseModel.getReporting().setPlugins(createReleaseReportPlugins(releaseDescriptor, releaseProject));
// rewrite extensions
releaseModel.getBuild().setExtensions(createReleaseExtensions(releaseDescriptor, releaseProject));
unalignFromBaseDirectory(releaseModel, project.getBasedir());
return releaseModel;
}
private void unalignFromBaseDirectory(Model releaseModel, File basedir) {
Model rawSuperModel = superPomProvider.getSuperModel(releaseModel.getModelVersion());
ModelBuildingRequest buildingRequest = new DefaultModelBuildingRequest();
buildingRequest.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_STRICT);
// inject proper values used by project.build.finalName
Properties properties = new Properties();
properties.put("project.version", releaseModel.getVersion());
properties.put("project.artifactId", releaseModel.getArtifactId());
buildingRequest.setUserProperties(properties);
Model interpolatedSuperModel =
modelInterpolator.interpolateModel(rawSuperModel.clone(), basedir, buildingRequest, null);
Build currentBuild = releaseModel.getBuild();
Build interpolatedSuperBuild = interpolatedSuperModel.getBuild();
Build rawSuperBuild = rawSuperModel.getBuild();
currentBuild.setSourceDirectory(resolvePath(
basedir.toPath(),
currentBuild.getSourceDirectory(),
interpolatedSuperBuild.getSourceDirectory(),
rawSuperBuild.getSourceDirectory()));
currentBuild.setScriptSourceDirectory(resolvePath(
basedir.toPath(),
currentBuild.getScriptSourceDirectory(),
interpolatedSuperBuild.getScriptSourceDirectory(),
rawSuperBuild.getScriptSourceDirectory()));
currentBuild.setTestSourceDirectory(resolvePath(
basedir.toPath(),
currentBuild.getTestSourceDirectory(),
interpolatedSuperBuild.getTestSourceDirectory(),
rawSuperBuild.getTestSourceDirectory()));
currentBuild.setOutputDirectory(resolvePath(
basedir.toPath(),
currentBuild.getOutputDirectory(),
interpolatedSuperBuild.getOutputDirectory(),
rawSuperBuild.getOutputDirectory()));
currentBuild.setTestOutputDirectory(resolvePath(
basedir.toPath(),
currentBuild.getTestOutputDirectory(),
interpolatedSuperBuild.getTestOutputDirectory(),
rawSuperBuild.getTestOutputDirectory()));
currentBuild.setDirectory(resolvePath(
basedir.toPath(),
currentBuild.getDirectory(),
interpolatedSuperBuild.getDirectory(),
rawSuperBuild.getDirectory()));
for (Resource currentResource : currentBuild.getResources()) {
Map<String, String> superResourceDirectories =
new LinkedHashMap<>(interpolatedSuperBuild.getResources().size());
for (int i = 0; i < interpolatedSuperBuild.getResources().size(); i++) {
superResourceDirectories.put(
interpolatedSuperBuild.getResources().get(i).getDirectory(),
rawSuperBuild.getResources().get(i).getDirectory());
}
currentResource.setDirectory(
resolvePath(basedir.toPath(), currentResource.getDirectory(), superResourceDirectories));
}
for (Resource currentResource : currentBuild.getTestResources()) {
Map<String, String> superResourceDirectories = new LinkedHashMap<>(
interpolatedSuperBuild.getTestResources().size());
for (int i = 0; i < interpolatedSuperBuild.getTestResources().size(); i++) {
superResourceDirectories.put(
interpolatedSuperBuild.getTestResources().get(i).getDirectory(),
rawSuperBuild.getTestResources().get(i).getDirectory());
}
currentResource.setDirectory(
resolvePath(basedir.toPath(), currentResource.getDirectory(), superResourceDirectories));
}
releaseModel
.getReporting()
.setOutputDirectory(resolvePath(
basedir.toPath(),
releaseModel.getReporting().getOutputDirectory(),
interpolatedSuperModel.getReporting().getOutputDirectory(),
rawSuperModel.getReporting().getOutputDirectory()));
}
private String resolvePath(Path basedir, String current, String superInterpolated, String superRaw) {
return basedir.resolve(current).equals(basedir.resolve(superInterpolated)) ? superRaw : current;
}
private String resolvePath(
Path basedir, String current, Map<String /* interpolated */, String /* raw */> superValues) {
for (Map.Entry<String, String> superValue : superValues.entrySet()) {
if (basedir.resolve(current).equals(basedir.resolve(superValue.getKey()))) {
return superValue.getValue();
}
}
return current;
}
private String findOriginalFinalName(MavenProject project) {
if (project.getOriginalModel().getBuild() != null
&& project.getOriginalModel().getBuild().getFinalName() != null) {
return project.getOriginalModel().getBuild().getFinalName();
} else if (project.hasParent()) {
return findOriginalFinalName(project.getParent());
} else {
return null;
}
}
@Override
public ReleaseResult simulate(
ReleaseDescriptor releaseDescriptor,
ReleaseEnvironment releaseEnvironment,
List<MavenProject> reactorProjects)
throws ReleaseExecutionException, ReleaseFailureException {
return execute(releaseDescriptor, releaseEnvironment, reactorProjects, true);
}
private String getNextVersion(ReleaseDescriptor releaseDescriptor, String groupId, String artifactId)
throws ReleaseFailureException {
// TODO: share with RewritePomsForReleasePhase.rewriteVersion
String id = ArtifactUtils.versionlessKey(groupId, artifactId);
String nextVersion = releaseDescriptor.getProjectReleaseVersion(id);
if (nextVersion == null) {
throw new ReleaseFailureException("Version for '" + id + "' was not mapped");
}
return nextVersion;
}
private ScmTranslator getScmTranslator(ScmRepository scmRepository) {
return scmTranslators.get(scmRepository.getProvider());
}
private Scm createReleaseScm(Scm scm, ScmTranslator scmTranslator, ReleaseDescriptor releaseDescriptor) {
// TODO: share with RewritePomsForReleasePhase.translateScm
String tag = releaseDescriptor.getScmReleaseLabel();
String tagBase = releaseDescriptor.getScmTagBase();
Scm releaseScm = new Scm();
if (scm.getConnection() != null) {
String value = scmTranslator.translateTagUrl(scm.getConnection(), tag, tagBase);
releaseScm.setConnection(value);
}
if (scm.getDeveloperConnection() != null) {
String value = scmTranslator.translateTagUrl(scm.getDeveloperConnection(), tag, tagBase);
releaseScm.setDeveloperConnection(value);
}
if (scm.getUrl() != null) {
String value = scmTranslator.translateTagUrl(scm.getUrl(), tag, tagBase);
releaseScm.setUrl(value);
}
if (scm.getTag() != null) {
String value = scmTranslator.resolveTag(scm.getTag());
releaseScm.setTag(value);
}
return releaseScm;
}
private List<Dependency> createReleaseDependencies(ReleaseDescriptor releaseDescriptor, MavenProject project)
throws ReleaseFailureException {
Set<Artifact> artifacts = project.getArtifacts();
List<Dependency> releaseDependencies = null;
if (artifacts != null) {
// make dependency order deterministic for tests (related to MNG-1412)
List<Artifact> orderedArtifacts = new ArrayList<>(artifacts);
Collections.sort(orderedArtifacts);
releaseDependencies = new ArrayList<>();
for (Artifact artifact : orderedArtifacts) {
Dependency releaseDependency = new Dependency();
releaseDependency.setGroupId(artifact.getGroupId());
releaseDependency.setArtifactId(artifact.getArtifactId());
String version = getReleaseVersion(releaseDescriptor, artifact);
releaseDependency.setVersion(version);
releaseDependency.setType(artifact.getType());
releaseDependency.setScope(artifact.getScope());
releaseDependency.setClassifier(artifact.getClassifier());
releaseDependencies.add(releaseDependency);
}
}
return releaseDependencies;
}
private String getReleaseVersion(ReleaseDescriptor releaseDescriptor, Artifact artifact)
throws ReleaseFailureException {
String key = ArtifactUtils.versionlessKey(artifact);
String originalVersion = releaseDescriptor.getProjectOriginalVersion(key);
String mappedVersion = releaseDescriptor.getProjectReleaseVersion(key);
String version = artifact.getVersion();
if (version.equals(originalVersion)) {
if (mappedVersion != null) {
version = mappedVersion;
} else {
throw new ReleaseFailureException("Version '" + version + "' for '" + key + "' was not mapped");
}
} else {
if (!ArtifactUtils.isSnapshot(version)) {
version = artifact.getBaseVersion();
}
}
return version;
}
private List<Plugin> createReleasePlugins(ReleaseDescriptor releaseDescriptor, MavenProject project)
throws ReleaseFailureException {
List<Plugin> releasePlugins = null;
// Use original - don't want the lifecycle introduced ones
Build build = project.getOriginalModel().getBuild();
if (build != null) {
List<Plugin> plugins = build.getPlugins();
if (plugins != null) {
Map<String, Artifact> artifactsById = project.getPluginArtifactMap();
releasePlugins = new ArrayList<>();
for (Plugin plugin : plugins) {
String id = ArtifactUtils.versionlessKey(plugin.getGroupId(), plugin.getArtifactId());
Artifact artifact = artifactsById.get(id);
String version = getReleaseVersion(releaseDescriptor, artifact);
Plugin releasePlugin = new Plugin();
releasePlugin.setGroupId(plugin.getGroupId());
releasePlugin.setArtifactId(plugin.getArtifactId());
releasePlugin.setVersion(version);
if (plugin.getExtensions() != null) {
releasePlugin.setExtensions(plugin.isExtensions());
}
releasePlugin.setExecutions(plugin.getExecutions());
releasePlugin.setDependencies(plugin.getDependencies());
releasePlugin.setInherited(plugin.getInherited());
if (plugin.getConfiguration() != null) {
releasePlugin.setConfiguration(plugin.getConfiguration());
}
releasePlugins.add(releasePlugin);
}
}
}
return releasePlugins;
}
private List<ReportPlugin> createReleaseReportPlugins(ReleaseDescriptor releaseDescriptor, MavenProject project)
throws ReleaseFailureException {
List<ReportPlugin> releaseReportPlugins = null;
Reporting reporting = project.getModel().getReporting();
if (reporting != null) {
List<ReportPlugin> reportPlugins = reporting.getPlugins();
if (reportPlugins != null) {
Map<String, Artifact> artifactsById = project.getReportArtifactMap();
releaseReportPlugins = new ArrayList<>();
for (ReportPlugin reportPlugin : reportPlugins) {
String id = ArtifactUtils.versionlessKey(reportPlugin.getGroupId(), reportPlugin.getArtifactId());
Artifact artifact = artifactsById.get(id);
String version = getReleaseVersion(releaseDescriptor, artifact);
ReportPlugin releaseReportPlugin = new ReportPlugin();
releaseReportPlugin.setGroupId(reportPlugin.getGroupId());
releaseReportPlugin.setArtifactId(reportPlugin.getArtifactId());
releaseReportPlugin.setVersion(version);
releaseReportPlugin.setInherited(reportPlugin.getInherited());
releaseReportPlugin.setConfiguration(reportPlugin.getConfiguration());
releaseReportPlugin.setReportSets(reportPlugin.getReportSets());
releaseReportPlugins.add(releaseReportPlugin);
}
}
}
return releaseReportPlugins;
}
private List<Extension> createReleaseExtensions(ReleaseDescriptor releaseDescriptor, MavenProject project)
throws ReleaseFailureException {
List<Extension> releaseExtensions = null;
// Use original - don't want the lifecycle introduced ones
Build build = project.getOriginalModel().getBuild();
if (build != null) {
List<Extension> extensions = build.getExtensions();
if (extensions != null) {
releaseExtensions = new ArrayList<>();
for (Extension extension : extensions) {
String id = ArtifactUtils.versionlessKey(extension.getGroupId(), extension.getArtifactId());
Artifact artifact = project.getExtensionArtifactMap().get(id);
String version = getReleaseVersion(releaseDescriptor, artifact);
Extension releaseExtension = new Extension();
releaseExtension.setGroupId(extension.getGroupId());
releaseExtension.setArtifactId(extension.getArtifactId());
releaseExtension.setVersion(version);
releaseExtensions.add(releaseExtension);
}
}
}
return releaseExtensions;
}
/*
* @see org.apache.maven.shared.release.phase.AbstractReleasePhase#clean(java.util.List)
*/
@Override
public ReleaseResult clean(List<MavenProject> reactorProjects) {
ReleaseResult result = new ReleaseResult();
for (MavenProject project : reactorProjects) {
File releasePom = ReleaseUtil.getReleasePom(project);
// MRELEASE-273 : A release pom can be null
if (releasePom != null && releasePom.exists()) {
logInfo(result, "Deleting release POM for '" + project.getName() + "'...");
if (!releasePom.delete()) {
logWarn(result, "Cannot delete release POM: " + releasePom);
}
}
}
result.setResultCode(ReleaseResult.SUCCESS);
return result;
}
}