Skip to content

Commit

Permalink
Merge pull request #120 from glimmerveen/feature/partial-multi-module…
Browse files Browse the repository at this point in the history
…-reversioning

Extended the OtherBranchVersionExtension to deal with partial multi-module builds
  • Loading branch information
bvarner committed Jan 14, 2021
2 parents 9889c7f + b30df68 commit 13fcbaf
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 2 deletions.
Expand Up @@ -2,38 +2,54 @@

import com.e_gineering.maven.gitflowhelper.properties.PropertyResolver;
import org.apache.maven.AbstractMavenLifecycleParticipant;
import org.apache.maven.Maven;
import org.apache.maven.MavenExecutionException;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.io.ModelWriter;
import org.apache.maven.plugin.LegacySupport;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component(role = AbstractMavenLifecycleParticipant.class, hint = "other-branch-version")
public class OtherBranchVersionExtension extends AbstractBranchDetectingExtension {

@Requirement(role = ModelWriter.class)
ModelWriter modelWriter;

@Requirement(role = Maven.class)
Maven maven;

@Requirement(role = LegacySupport.class)
LegacySupport legacySupport;

private static final int ORIGINAL_VERSION_IDX = 0;
private static final int ADJUSTED_VERSION_IDX = 1;

@Override
public void afterProjectsRead(final MavenSession session) throws MavenExecutionException {
super.afterProjectsRead(session);


if (session.getUserProperties().containsKey("gitflow.skip.extension")) {
return;
}

if (pluginFound) {
logger.debug("other-branch-version extension active.");
if (branchInfo != null) {
Expand Down Expand Up @@ -62,7 +78,60 @@ public void afterProjectsRead(final MavenSession session) throws MavenExecutionE
project.getArtifact().setVersionRange(VersionRange.createFromVersion(newVersion));
}
}


final MavenProject topLevelProjectInsideReactor = session.getTopLevelProject();
final MavenProject topLevelProject = findTopLevelProject(topLevelProjectInsideReactor);
logger.info("Top level project: " + topLevelProjectInsideReactor.getGroupId() + ":" + topLevelProjectInsideReactor.getArtifactId());
if (!topLevelProjectInsideReactor.equals(topLevelProject)) {
// When only the partial tree of modules is being built, not all modules could be indexed
// into the cross-walk map. In order to deal with dependencies to modules outside the reactor,
// Another Maven execution is performed on the 'actual' top level project. This allows the
// cross-walk map to be completed with the modules that *are* part of the multi-module project,
// but are currently not part of the reactor.

logger.info("Found top level project, but outside reactor: " + topLevelProject.getGroupId() + ":" + topLevelProject.getArtifactId() + " (" + topLevelProject.getFile() + ")");

// Initialization of the nested Maven execution, based on the current session's request
final MavenExecutionRequest request = DefaultMavenExecutionRequest.copy(session.getRequest())
.setExecutionListener(null) /* Disable the observer of the outer maven session */
.setTransferListener(null) /* Disable the observer of the outer maven session */
.setGoals(null) /* Disable the goals used to execute the outer maven session */
.setReactorFailureBehavior(MavenExecutionRequest.REACTOR_FAIL_NEVER)
.setPom(topLevelProject.getFile()) /* Use the pom file of the top-level project */
.setBaseDirectory(topLevelProject.getBasedir()) /* Use the basedir of the top-level project */
;
// The following user property on the nested execution prevents this extension to activate
// in the nested execution. This is needed, as the extension is not reentrant.
request.getUserProperties().put("gitflow.skip.extension", true);

// Perform the nested Maven execution, and grab the list of *all* projects (ie modules of the
// multi-module build).
final MavenExecutionResult mavenExecutionResult;
try {
mavenExecutionResult = maven.execute(request);
} finally {
// The additional Maven execution uses a new session, and at the end of the execution
// clears the Session object in LegacySupport. This may break other plugins/uses of
// LegacySupport; therefore always restore the session *after* the additional Maven
// execution.
legacySupport.setSession(session);
}
final List<MavenProject> topologicallySortedProjects = mavenExecutionResult.getTopologicallySortedProjects();

// Iterate over these modules and process the 'new' ones just as the modules that are part
// of the reactor.
for (MavenProject parsedProject : topologicallySortedProjects) {
if (adjustedVersions.containsKey(parsedProject)) {
logger.info("Skipping " + parsedProject.getGroupId() + ":" + parsedProject.getArtifactId() + ": already part of reactor");
} else {
String originalVersion = PropertyResolver.resolveValue(parsedProject.getVersion(), parsedProject.getProperties(), systemEnvVars);
String newVersion = getAsBranchSnapshotVersion(originalVersion, branchInfo.getName());
adjustedVersions.put(parsedProject, new String[]{originalVersion, newVersion});
logger.info("Updating outside-reactor project " + parsedProject.getGroupId() + ":" + parsedProject.getArtifactId() + ":" + originalVersion + " to: " + newVersion);
}
}
}

// Once we have that populated, refilter the adjusted projects models
// updating dependencies on both the in-memory effective model, and the original pom.xml model.
for (Map.Entry<MavenProject, String[]> adjustedProject : adjustedVersions.entrySet()) {
Expand Down Expand Up @@ -96,6 +165,16 @@ public void afterProjectsRead(final MavenSession session) throws MavenExecutionE
logger.info("Continuing execution....");
}

private MavenProject findTopLevelProject(MavenProject mavenProject) {
MavenProject parent = mavenProject;

while (parent.getParentFile() != null) {
parent = parent.getParent();
}

return parent;
}

private void updateProjectModel(final MavenProject projectContext, final Model model, final String[] versions, final Map<MavenProject, String[]> adjustedVersions) {
model.setVersion(versions[ADJUSTED_VERSION_IDX]);

Expand Down
Expand Up @@ -98,4 +98,48 @@ public void attachDeployed() throws Exception {
verifier.resetStreams();
}
}

@Test
public void buildMultiModuleProject() throws Exception {
Verifier verifier = createVerifier("/multi-module-project-stub", "origin/feature/poc/test-partial-multi-module", "5.0.0-SNAPSHOT");
try {
verifier.executeGoal("install");

// Verify that all 3 maven projects that are part of the multi-module build are recognized
verifier.verifyTextInLog("Updating project com.e-gineering:gitflow-helper-maven-plugin-multi-module-parent-stub:5.0.0-SNAPSHOT to: 5.0.0+origin-feature-poc-test-partial-multi-module-SNAPSHOT");
verifier.verifyTextInLog("Updating project com.e-gineering:gitflow-helper-maven-plugin-multi-module-child1-stub:5.0.0-SNAPSHOT to: 5.0.0+origin-feature-poc-test-partial-multi-module-SNAPSHOT");
verifier.verifyTextInLog("Updating project com.e-gineering:gitflow-helper-maven-plugin-multi-module-child2-stub:5.0.0-SNAPSHOT to: 5.0.0+origin-feature-poc-test-partial-multi-module-SNAPSHOT");

verifier.verifyErrorFreeLog();
} finally {
verifier.resetStreams();
}
}

@Test
public void partialBuildMultiModuleProject() throws Exception {
// Partial builds of a multi-module project *must* be preceded by a full build
buildMultiModuleProject();

// After a full build, try to build module child2 in isolation
Verifier verifier = createVerifier("/multi-module-project-stub", "origin/feature/poc/test-partial-multi-module", "5.0.0-SNAPSHOT");
try {
// Only build module child2 (thus this being a partial build)
verifier.addCliOption("-pl child2");
verifier.executeGoal("install");

// Verify that the plugin detects that we are doing a partial build
verifier.verifyTextInLog("Found top level project, but outside reactor: com.e-gineering:gitflow-helper-maven-plugin-multi-module-parent-stub");

// Verify that the dependency to a module not part of the current build *is* being rewritten
verifier.verifyTextInLog("Updating outside-reactor project com.e-gineering:gitflow-helper-maven-plugin-multi-module-child1-stub:5.0.0-SNAPSHOT to: 5.0.0+origin-feature-poc-test-partial-multi-module-SNAPSHOT");

// Verify that the project that *is* part of the reactor is left alone in the construction of the cross-walk map.
verifier.verifyTextInLog("Skipping com.e-gineering:gitflow-helper-maven-plugin-multi-module-child2-stub: already part of reactor");

verifier.verifyErrorFreeLog();
} finally {
verifier.resetStreams();
}
}
}

0 comments on commit 13fcbaf

Please sign in to comment.