com.github.danielflower.mavenplugins.release.Reactor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of multi-module-maven-release-plugin Show documentation
Show all versions of multi-module-maven-release-plugin Show documentation
A maven release plugin built for multi-maven-module git repositories allowing continuous deployment
package com.github.danielflower.mavenplugins.release;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Reactor {
private final List modulesInBuildOrder;
public Reactor(List modulesInBuildOrder) {
this.modulesInBuildOrder = modulesInBuildOrder;
}
public List getModulesInBuildOrder() {
return modulesInBuildOrder;
}
public static Reactor fromProjects(Log log, LocalGitRepo gitRepo, MavenProject rootProject, List projects, Long buildNumber, List modulesToForceRelease, NoChangesAction actionWhenNoChangesDetected, ResolverWrapper resolverWrapper, VersionNamer versionNamer) throws ValidationException, GitAPIException, MojoExecutionException {
DiffDetector detector = new TreeWalkingDiffDetector(gitRepo.git.getRepository());
List modules = new ArrayList();
for (MavenProject project : projects) {
String relativePathToModule = calculateModulePath(rootProject, project);
String artifactId = project.getArtifactId();
String versionWithoutBuildNumber = project.getVersion().replace("-SNAPSHOT", "");
List previousTagsForThisModule = AnnotatedTagFinder.tagsForVersion(gitRepo.git, artifactId, versionWithoutBuildNumber);
Collection previousBuildNumbers = new ArrayList();
if (previousTagsForThisModule != null) {
for (AnnotatedTag previousTag : previousTagsForThisModule) {
previousBuildNumbers.add(previousTag.buildNumber());
}
}
Collection remoteBuildNumbers = getRemoteBuildNumbers(gitRepo, artifactId, versionWithoutBuildNumber);
previousBuildNumbers.addAll(remoteBuildNumbers);
VersionName newVersion = versionNamer.name(project.getVersion(), buildNumber, previousBuildNumbers);
boolean oneOfTheDependenciesHasChanged = false;
String changedDependency = null;
for (ReleasableModule module : modules) {
if (module.willBeReleased()) {
for (Dependency dependency : project.getModel().getDependencies()) {
if (dependency.getGroupId().equals(module.getGroupId()) && dependency.getArtifactId().equals(module.getArtifactId())) {
oneOfTheDependenciesHasChanged = true;
changedDependency = dependency.getArtifactId();
break;
}
}
if (project.getParent() != null
&& (project.getParent().getGroupId().equals(module.getGroupId()) && project.getParent().getArtifactId().equals(module.getArtifactId()))) {
oneOfTheDependenciesHasChanged = true;
changedDependency = project.getParent().getArtifactId();
break;
}
}
if (oneOfTheDependenciesHasChanged) {
break;
}
}
String equivalentVersion = null;
if(modulesToForceRelease != null && modulesToForceRelease.contains(artifactId)) {
log.info("Releasing " + artifactId + " " + newVersion.releaseVersion() + " as we was asked to forced release.");
} else if (oneOfTheDependenciesHasChanged) {
log.info("Releasing " + artifactId + " " + newVersion.releaseVersion() + " as " + changedDependency + " has changed.");
} else {
AnnotatedTag previousTagThatIsTheSameAsHEADForThisModule = hasChangedSinceLastRelease(previousTagsForThisModule, detector, project, relativePathToModule);
if (previousTagThatIsTheSameAsHEADForThisModule != null) {
equivalentVersion = previousTagThatIsTheSameAsHEADForThisModule.version() + "." + previousTagThatIsTheSameAsHEADForThisModule.buildNumber();
// attempt to resolve
if (resolverWrapper.isResolvable(project.getGroupId(), project.getArtifactId(), equivalentVersion, project.getPackaging(), log)) {
log.info("Will use version " + equivalentVersion + " for " + artifactId + " as it has not been changed since that release.");
} else {
log.info("Will use version " + newVersion.releaseVersion() + " for " + artifactId + " as although no change was detected, the artifact cannot be resolved!");
equivalentVersion = null;
}
} else {
log.info("Will use version " + newVersion.releaseVersion() + " for " + artifactId + " as it has changed since the last release.");
}
}
ReleasableModule module = new ReleasableModule(project, newVersion, equivalentVersion, relativePathToModule);
modules.add(module);
}
if (!atLeastOneBeingReleased(modules)) {
switch (actionWhenNoChangesDetected) {
case ReleaseNone:
log.warn("No changes have been detected in any modules so will not perform release");
return null;
case FailBuild:
throw new MojoExecutionException("No module changes have been detected");
default:
log.warn("No changes have been detected in any modules so will re-release them all");
List newList = new ArrayList();
for (ReleasableModule module : modules) {
newList.add(module.createReleasableVersion());
}
modules = newList;
}
}
return new Reactor(modules);
}
private static Collection getRemoteBuildNumbers(LocalGitRepo gitRepo, String artifactId, String versionWithoutBuildNumber) throws GitAPIException {
Collection remoteTagRefs = gitRepo.allTags();
Collection remoteBuildNumbers = new ArrayList();
String tagWithoutBuildNumber = artifactId + "-" + versionWithoutBuildNumber;
for (Ref remoteTagRef : remoteTagRefs) {
String remoteTagName = remoteTagRef.getName();
Long buildNumber = AnnotatedTagFinder.buildNumberOf(tagWithoutBuildNumber, remoteTagName);
if (buildNumber != null) {
remoteBuildNumbers.add(buildNumber);
}
}
return remoteBuildNumbers;
}
private static boolean atLeastOneBeingReleased(List modules) {
for (ReleasableModule module : modules) {
if (module.willBeReleased()) {
return true;
}
}
return false;
}
private static String calculateModulePath(MavenProject rootProject, MavenProject project) throws MojoExecutionException {
// Getting canonical files because on Windows, it's possible one returns "C:\..." and the other "c:\..." which is rather amazing
File projectRoot;
File moduleRoot;
try {
projectRoot = rootProject.getBasedir().getCanonicalFile();
moduleRoot = project.getBasedir().getCanonicalFile();
} catch (IOException e) {
throw new MojoExecutionException("Could not find directory paths for maven project", e);
}
String relativePathToModule = Repository.stripWorkDir(projectRoot, moduleRoot);
if (relativePathToModule.length() == 0) {
relativePathToModule = ".";
}
return relativePathToModule;
}
static AnnotatedTag hasChangedSinceLastRelease(List previousTagsForThisModule, DiffDetector detector, MavenProject project, String relativePathToModule) throws MojoExecutionException {
try {
if (previousTagsForThisModule.size() == 0) return null;
boolean hasChanged = detector.hasChangedSince(relativePathToModule, project.getModel().getModules(), previousTagsForThisModule);
return hasChanged ? null : tagWithHighestBuildNumber(previousTagsForThisModule);
} catch (Exception e) {
throw new MojoExecutionException("Error while detecting whether or not " + project.getArtifactId() + " has changed since the last release", e);
}
}
private static AnnotatedTag tagWithHighestBuildNumber(List tags) {
AnnotatedTag cur = null;
for (AnnotatedTag tag : tags) {
if (cur == null || tag.buildNumber() > cur.buildNumber()) {
cur = tag;
}
}
return cur;
}
public ReleasableModule findByLabel(String label) {
for (ReleasableModule module : modulesInBuildOrder) {
String currentLabel = module.getGroupId() + ":" + module.getArtifactId();
if (currentLabel.equals(label)) {
return module;
}
}
return null;
}
public ReleasableModule find(String groupId, String artifactId, String version) throws UnresolvedSnapshotDependencyException {
ReleasableModule value = findByLabel(groupId + ":" + artifactId);
if (value == null) {
throw new UnresolvedSnapshotDependencyException(groupId, artifactId, version);
}
return value;
}
}