Skip to content

Commit

Permalink
Merge c2db550 into 877f841
Browse files Browse the repository at this point in the history
  • Loading branch information
glimmerveen committed Nov 23, 2020
2 parents 877f841 + c2db550 commit a26c977
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 5 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,64 @@ 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
final MavenExecutionRequest request = new DefaultMavenExecutionRequest()
.setLocalRepository(session.getLocalRepository())
.setPom(topLevelProject.getFile())
.setBaseDirectory(topLevelProject.getBasedir())
.setReactorFailureBehavior(MavenExecutionRequest.REACTOR_FAIL_NEVER)
.setUserProperties(session.getUserProperties())
.setMirrors(session.getRequest().getMirrors())
.setRemoteRepositories(session.getRequest().getRemoteRepositories())
.setPluginArtifactRepositories(session.getRequest().getPluginArtifactRepositories())
.setServers(session.getRequest().getServers())
.setProxies(session.getRequest().getProxies())
;
// 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 +169,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 @@ -218,14 +301,18 @@ private boolean isProjectOfReplacedArtifactVersion(final MavenProject project, f

/**
* Given a String version (which may be a final or -SNAPSHOT version) return a
* version version string mangled to include a `+normalized-branch-name-SNAPSHOT format version.
* version string mangled to include a `+normalized-branch-name-SNAPSHOT format version.
*
* @param version The base version (ie, 1.0.2-SNAPSHOT)
* @param branchName to be normalized
* @return A mangled version string with the branchname and -SNAPSHOT.
*/
private String getAsBranchSnapshotVersion(final String version, final String branchName) {
return version.replace("-SNAPSHOT", "") + otherBranchVersionDelimiter + branchName.replaceAll("[^0-9A-Za-z-.]", "-") + "-SNAPSHOT";
public String getAsBranchSnapshotVersion(final String version, final String branchName) {
String branchNameSanitized = otherBranchVersionDelimiter + branchName.replaceAll("[^0-9A-Za-z-.]", "-") + "-SNAPSHOT";
if(version.endsWith(branchNameSanitized)) {
return version;
}
return version.replace("-SNAPSHOT", "") + branchNameSanitized;
}

}
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,0 +1,33 @@
package com.e_gineering.maven.gitflowhelper;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;

@RunWith(BlockJUnit4ClassRunner.class)
public class OtherBranchTest {

private final String baseVersion = "1.0.2";
private final String baseSnapshotVersion = "1.0.2-SNAPSHOT";
private final String branchName = "feature/other-branch-name";
private final String expectedSnapshotResult = "1.0.2+feature-other-branch-name-SNAPSHOT";

private OtherBranchVersionExtension getExtension() {
OtherBranchVersionExtension extension = new OtherBranchVersionExtension();
extension.otherBranchVersionDelimiter = "+";
return extension;
}

@Test
public void assertOtherBranchNameIsPrefixedBeforeSnapshot() {
OtherBranchVersionExtension extension = getExtension();
Assert.assertEquals(expectedSnapshotResult, extension.getAsBranchSnapshotVersion(baseSnapshotVersion,branchName));
}

@Test
public void assertOtherBranchNameIsOnlyPrefixedBeforeSnapshotOneTime() {
OtherBranchVersionExtension extension = getExtension();
Assert.assertEquals(expectedSnapshotResult, extension.getAsBranchSnapshotVersion(extension.getAsBranchSnapshotVersion(baseSnapshotVersion,branchName),branchName));
}
}

0 comments on commit a26c977

Please sign in to comment.