org.seedstack.maven.ReleaseMojo Maven / Gradle / Ivy
The newest version!
/*
* Copyright © 2013-2021, The SeedStack authors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.maven;
import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId;
import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration;
import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo;
import static org.twdata.maven.mojoexecutor.MojoExecutor.executionEnvironment;
import static org.twdata.maven.mojoexecutor.MojoExecutor.goal;
import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId;
import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin;
import static org.twdata.maven.mojoexecutor.MojoExecutor.version;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Profile;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
/**
* Release the project simply by stripping the -SNAPSHOT part of the version.
* Useful in continuous delivery pipelines.
*
* @author [email protected]
*/
@Mojo(name = "release", requiresProject = true, threadSafe = false, aggregator = true)
public class ReleaseMojo extends AbstractSeedStackMojo {
private static final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject mavenProject;
@Parameter(defaultValue = "${session}", readonly = true)
private MavenSession mavenSession;
@Component
private BuildPluginManager buildPluginManager;
@Component
private ProjectBuilder projectBuilder;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
String currentVersion = mavenProject.getVersion();
if (isProjectDirty(getReactorModules("", mavenProject))) {
throw new MojoFailureException(
"Cannot continue, a POM transformation is already in progress, commit and revert it before " +
"executing the release goal");
}
if (!mavenProject.equals(getLocalRoot(mavenProject))) {
throw new MojoFailureException("Cannot continue, release goal must be executed from the local project " +
"root");
}
if (!currentVersion.endsWith(SNAPSHOT_SUFFIX)) {
throw new MojoFailureException("Cannot continue, project version is not a SNAPSHOT");
}
if (mavenProject.hasParent() && mavenProject.getParent()
.getVersion()
.endsWith(SNAPSHOT_SUFFIX)) {
throw new MojoFailureException("Cannot continue, parent project is still a SNAPSHOT");
}
String newVersion = currentVersion.substring(0, currentVersion.length() - SNAPSHOT_SUFFIX.length());
getLog().info("SNAPSHOT version found, setting release version to " + newVersion);
mavenProject.getProperties().setProperty("newVersion", newVersion);
executeVersionsPlugin("set", mavenProject);
boolean shouldRevert = false;
List transformedModules = new ArrayList<>();
transformedModules.add(mavenProject);
transformedModules.addAll(getReactorModules("", mavenProject).values());
getLog().info("Checking transformed modules");
for (MavenProject transformedModule : transformedModules) {
try {
checkModule(transformedModule);
} catch (Exception e) {
getLog().error("Module " + transformedModule.getArtifactId() + " is not valid\n" + e.getMessage());
shouldRevert = true;
}
}
if (shouldRevert) {
getLog().info("Reverting transformations");
for (MavenProject transformedModule : transformedModules) {
revertModule(transformedModule);
}
throw new MojoFailureException("Release was aborted due to previous errors");
} else {
getLog().info("Committing transformations");
for (MavenProject transformedModule : transformedModules) {
commitModule(transformedModule);
}
}
}
private void revertModule(MavenProject mavenProject) {
try {
executeVersionsPlugin("revert", mavenProject);
} catch (MojoExecutionException e) {
getLog().error("Unable to revert module " + mavenProject.getArtifactId());
}
}
private void commitModule(MavenProject mavenProject) throws MojoExecutionException {
executeVersionsPlugin("commit", mavenProject);
}
private void checkModule(MavenProject mavenProject) throws MojoFailureException {
Map snapshotDependencies = new HashMap<>();
for (Dependency dependency : mavenProject.getDependencies()) {
if (dependency.getVersion().endsWith(SNAPSHOT_SUFFIX)) {
snapshotDependencies.put(mavenProject.getArtifactId(), dependency);
}
}
if (!snapshotDependencies.isEmpty()) {
StringBuilder sb = new StringBuilder(
"Cannot continue, there are still SNAPSHOT dependencies in the project:\n");
for (Map.Entry dependencyEntry : snapshotDependencies.entrySet()) {
sb.append("\t* ")
.append(dependencyEntry.getKey())
.append(": ")
.append(dependencyEntry.getValue().getManagementKey())
.append("\n");
}
throw new MojoFailureException(sb.toString());
}
}
private void executeVersionsPlugin(String goal, MavenProject mavenProject) throws MojoExecutionException {
MavenProject oldProject = mavenSession.getCurrentProject();
mavenSession.setCurrentProject(mavenProject);
executeMojo(
plugin(
groupId("org.codehaus.mojo"),
artifactId("versions-maven-plugin"),
version("2.2")
),
goal(goal),
configuration(),
executionEnvironment(mavenProject, mavenSession, buildPluginManager));
mavenSession.setCurrentProject(oldProject);
}
private boolean isTransformationInProgress(MavenProject mavenProject) {
return new File(mavenProject.getFile().getParentFile(), "pom.xml.versionsBackup").exists();
}
private boolean isProjectDirty(Map reactorProjects) throws MojoFailureException {
for (MavenProject reactorProject : reactorProjects.values()) {
if (isTransformationInProgress(reactorProject)) {
return true;
}
}
return false;
}
private Map getReactorModules(String path, MavenProject project) throws MojoFailureException {
if (path.length() > 0 && !path.endsWith("/")) {
path += '/';
}
Map result = new LinkedHashMap<>();
Map childResults = new LinkedHashMap<>();
Set childModules = getChildModules(project);
for (String moduleName : childModules) {
String modulePath = path + moduleName;
File moduleDir = new File(project.getBasedir(), moduleName);
File moduleProjectFile;
if (moduleDir.isDirectory()) {
moduleProjectFile = new File(moduleDir, "pom.xml");
} else {
// i don't think this should ever happen... but just in case
// the module references the file-name
moduleProjectFile = moduleDir;
}
try {
MavenProject moduleProject = buildProject(moduleProjectFile);
result.put(modulePath, moduleProject);
childResults.putAll(getReactorModules(modulePath, moduleProject));
} catch (ProjectBuildingException e) {
throw new MojoFailureException("Could not build project of " + moduleProjectFile.getPath(), e);
}
}
result.putAll(childResults);
return result;
}
private Set getChildModules(MavenProject mavenProject) {
Set childModules = new TreeSet<>();
childModules.addAll(mavenProject.getModules());
for (Profile profile : mavenProject.getModel().getProfiles()) {
childModules.addAll(profile.getModules());
}
return childModules;
}
private MavenProject getLocalRoot(MavenProject project) {
while (true) {
final File parentDir = project.getBasedir().getParentFile();
if (parentDir.isDirectory()) {
File parent = new File(parentDir, "pom.xml");
if (parent.isFile()) {
try {
final MavenProject parentProject = buildProject(parent);
if (getChildModules(parentProject).contains(project.getBasedir().getName())) {
project = parentProject;
continue;
}
} catch (ProjectBuildingException e) {
getLog().warn(e);
}
}
}
return project;
}
}
private MavenProject buildProject(File moduleProjectFile) throws ProjectBuildingException {
DefaultProjectBuildingRequest request = new DefaultProjectBuildingRequest();
request.setSystemProperties(System.getProperties());
request.setRepositorySession(mavenProject.getProjectBuildingRequest().getRepositorySession());
return projectBuilder.build(moduleProjectFile, request).getProject();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy