From 735ec6157a74101252f60af74b7158e0da26f77e Mon Sep 17 00:00:00 2001 From: Arnoud Glimmerveen Date: Wed, 9 Mar 2022 22:50:25 +0100 Subject: [PATCH] Extended the MasterPromoteExtension to not only retain the hardcoded gitflow-helper-maven-plugin itself and the maven-deploy-plugin, or the plugins related to explicit supplied command-line goals, but also allow a project to configure additional plugins that may also execute on master/support. This serves two cases: 1) It solves the issue #127 (though admittedly it still would be more clean to be able to download the pom file from the release repo) 2) It can serve as a basic extension point to allow other "things" to happen on a master build, that are beyond the scope of deploying maven artifacts (ie have some antrun script run) --- README.md | 4 +- .../AbstractBranchDetectingExtension.java | 32 ++++++++++++ .../gitflowhelper/MasterPromoteExtension.java | 48 +++++++++++------ .../gitflowhelper/PromoteMasterMojo.java | 12 +++++ .../gitflowhelper/MasterSupportBranchIT.java | 52 +++++++++++++++++++ src/test/resources/project-stub/pom.xml | 35 +++++++++++++ 6 files changed, 164 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index e70671d..0ae4d87 100644 --- a/README.md +++ b/README.md @@ -332,8 +332,8 @@ directly from master**. Rather, master is really only used for tracking releases To accomplish this the `promote-master` goal and a Maven build extension work together. With the build extension added to your project, any build where the `gitBranchExpression` matches the `masterBranchPattern` or `supportBranchPattern` will have it's -build lifecycle (plugins, goals, etc) altered. Any plugin other than the gitflow-helper-maven-plugin, the maven-deploy-plugin, or plugins with goals - explicitly referenced on the command line will be ignored (removed from the project reactor). +build lifecycle (plugins, goals, etc) altered. Any plugin other than the gitflow-helper-maven-plugin, the maven-deploy-plugin, plugins with goals + explicitly referenced on the command line or those configured explicitly in the `retainPlugins` list, will be ignored (removed from the project reactor). This allows us to enforce the ideal that code should never be built in the master branch. The `promote-master` goal executes when the `gitBranchExpression` resolves to a value matching the `masterBranchPattern` or `supportBranchPattern` regular expression. diff --git a/src/main/java/com/e_gineering/maven/gitflowhelper/AbstractBranchDetectingExtension.java b/src/main/java/com/e_gineering/maven/gitflowhelper/AbstractBranchDetectingExtension.java index dd75422..dfcaf25 100644 --- a/src/main/java/com/e_gineering/maven/gitflowhelper/AbstractBranchDetectingExtension.java +++ b/src/main/java/com/e_gineering/maven/gitflowhelper/AbstractBranchDetectingExtension.java @@ -13,6 +13,8 @@ import org.codehaus.plexus.util.xml.Xpp3Dom; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Properties; public abstract class AbstractBranchDetectingExtension extends AbstractMavenLifecycleParticipant { @@ -34,6 +36,7 @@ public abstract class AbstractBranchDetectingExtension extends AbstractMavenLife String featureOrBugfixBranchPattern; String otherDeployBranchPattern; String otherBranchVersionDelimiter; + List retainPlugins; GitBranchInfo branchInfo; Properties systemEnvVars; @@ -93,6 +96,10 @@ public void afterProjectsRead(MavenSession session) throws MavenExecutionExcepti if (gitBranchExpression == null) { gitBranchExpression = extractPluginConfigValue("gitBranchExpression", plugin); } + + if (retainPlugins == null) { + retainPlugins = extractPluginConfigList("retainPlugins", plugin); + } } } } @@ -145,6 +152,10 @@ public void afterProjectsRead(MavenSession session) throws MavenExecutionExcepti logger.debug("Using default otherBranchVersionDelimiter."); otherBranchVersionDelimiter = "+"; } + + if (retainPlugins == null) { + logger.debug("No additional plugin executions will be retained on master"); + } ScmUtils scmUtils = new ScmUtils(systemEnvVars, scmManager, session.getTopLevelProject(), new PlexusLoggerToMavenLog(logger), masterBranchPattern, supportBranchPattern, releaseBranchPattern, hotfixBranchPattern, developmentBranchPattern); branchInfo = scmUtils.resolveBranchInfo(gitBranchExpression); @@ -168,4 +179,25 @@ private String extractConfigValue(String parameter, Object configuration) { } return null; } + + private List extractPluginConfigList(String parameter, Plugin plugin) { + List value = extractConfigList(parameter, plugin.getConfiguration()); + for (int i = 0; i < plugin.getExecutions().size() && value == null; i++) { + value = extractConfigList(parameter, plugin.getExecutions().get(i).getConfiguration()); + } + return value; + } + + private List extractConfigList(String parameter, Object configuration) { + try { + List result = new ArrayList<>(); + Xpp3Dom parameterNode = ((Xpp3Dom) configuration).getChild(parameter); + for (int i = 0; i < parameterNode.getChildCount(); i++) { + result.add(parameterNode.getChild(i).getValue()); + } + return result; + } catch (Exception ignored) { + } + return null; + } } diff --git a/src/main/java/com/e_gineering/maven/gitflowhelper/MasterPromoteExtension.java b/src/main/java/com/e_gineering/maven/gitflowhelper/MasterPromoteExtension.java index 754eb09..05d21e3 100644 --- a/src/main/java/com/e_gineering/maven/gitflowhelper/MasterPromoteExtension.java +++ b/src/main/java/com/e_gineering/maven/gitflowhelper/MasterPromoteExtension.java @@ -9,8 +9,14 @@ import org.codehaus.plexus.component.annotations.Component; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; /** * Maven extension which removes (skips) undesired plugins from the build reactor when running on a master branch. @@ -19,6 +25,15 @@ */ @Component(role = AbstractMavenLifecycleParticipant.class, hint = "promote-master") public class MasterPromoteExtension extends AbstractBranchDetectingExtension { + + private static final Set PLUGIN_WHITELIST = Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + "org.apache.maven.plugins:maven-deploy-plugin", + "com.e-gineering:gitflow-helper-maven-plugin" + ) + ) + ); @Override public void afterProjectsRead(final MavenSession session) throws MavenExecutionException { @@ -41,26 +56,25 @@ public void afterProjectsRead(final MavenSession session) throws MavenExecutionE } // Build up a map of plugins to remove from projects, if we're on the master branch. - HashMap> pluginsToDrop = new HashMap<>(); + Map> pluginsToDrop = new HashMap<>(); + + final List configuredPluginsToRetain; + if (this.retainPlugins != null) { + configuredPluginsToRetain = this.retainPlugins; + } else { + configuredPluginsToRetain = Collections.emptyList(); + } for (MavenProject project : session.getProjects()) { - List dropPlugins = new ArrayList<>(); - for (Plugin plugin : project.getModel().getBuild().getPlugins()) { - // Don't drop our plugin. - if (plugin.getKey().equals("com.e-gineering:gitflow-helper-maven-plugin")) { - continue; - // Don't drop things we declare goals for. - } else if (pluginsToRetain.contains(plugin)) { - logger.debug("gitflow-helper-maven-plugin retaining plugin: " + plugin + " from project: " + project.getName()); - // Don't drop the maven-deploy-plugin - } else if (plugin.getKey().equals("org.apache.maven.plugins:maven-deploy-plugin")) { - logger.debug("gitflow-helper-maven-plugin retaining plugin: " + plugin + " from project: " + project.getName()); - } else { - logger.debug("gitflow-helper-maven-plugin removing plugin: " + plugin + " from project: " + project.getName()); - dropPlugins.add(plugin); - } - } + // Create a list of all plugins that are not in the whitelist, not explicitly invoked from the commandline, + // and not configured to be allowed on master/support. + List dropPlugins = project.getModel().getBuild().getPlugins() + .stream() + .filter(plugin -> !PLUGIN_WHITELIST.contains(plugin.getKey())) + .filter(plugin -> !pluginsToRetain.contains(plugin)) + .filter(plugin -> !configuredPluginsToRetain.contains(plugin.getKey())) + .collect(Collectors.toList()); pluginsToDrop.put(project, dropPlugins); } diff --git a/src/main/java/com/e_gineering/maven/gitflowhelper/PromoteMasterMojo.java b/src/main/java/com/e_gineering/maven/gitflowhelper/PromoteMasterMojo.java index c2353a4..7283131 100644 --- a/src/main/java/com/e_gineering/maven/gitflowhelper/PromoteMasterMojo.java +++ b/src/main/java/com/e_gineering/maven/gitflowhelper/PromoteMasterMojo.java @@ -4,6 +4,9 @@ import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; + +import java.util.List; /** * If the build is being executed from a DEVELOPMENT, HOTFIX or RELEASE branch, attach an artifact containing a list of @@ -17,6 +20,15 @@ @Mojo(name = "promote-master", defaultPhase = LifecyclePhase.INSTALL) public class PromoteMasterMojo extends AbstractGitflowBasedRepositoryMojo { + /** + * List of groupId:artifactId strings that refer to plugins that should be retained while building on master. + * + * Note that this property is listed here for documentation purposes, but it is handled within + * {@link MasterPromoteExtension}. + */ + @Parameter(property = "retainPlugins") + private List retainPlugins; + @Override protected void execute(final GitBranchInfo gitBranchInfo) throws MojoExecutionException, MojoFailureException { switch (gitBranchInfo.getType()) { diff --git a/src/test/java/com/e_gineering/maven/gitflowhelper/MasterSupportBranchIT.java b/src/test/java/com/e_gineering/maven/gitflowhelper/MasterSupportBranchIT.java index ff93c84..e34f89c 100644 --- a/src/test/java/com/e_gineering/maven/gitflowhelper/MasterSupportBranchIT.java +++ b/src/test/java/com/e_gineering/maven/gitflowhelper/MasterSupportBranchIT.java @@ -71,6 +71,16 @@ public void promotionOfRelease() throws Exception { // Otherwise, it's the VerificationException from looking for "Compiling", and that's expected to fail. } + try { + // The flatten-maven-plugin should not activate, fail if it does. + verifier.verifyTextInLog("Generating flattened POM of project"); + throw new VerificationException(PROMOTION_FAILED_MESSAGE); + } catch (VerificationException ve) { + if (ve.getMessage().equals(PROMOTION_FAILED_MESSAGE)) { + throw ve; + } + } + verifier.verifyTextInLog( "gitflow-helper-maven-plugin: Enabling MasterPromoteExtension. GIT_BRANCH: [origin/master] matches masterBranchPattern"); verifier.verifyTextInLog("[INFO] Setting release artifact repository to: [releases]"); @@ -121,4 +131,46 @@ public void dontPruneExplicitlyInvokedPlugins() throws Exception { verifier.resetStreams(); } } + + @Test + public void dontPruneExplicitlyConfiguredPlugins() throws Exception { + // Create a release version and get it deployed. + Verifier verifier = createVerifier("/project-stub", "origin/release/1.2.0", "1.2.0"); + + try { + verifier.executeGoal("deploy"); + + verifier.verifyErrorFreeLog(); + } finally { + verifier.resetStreams(); + } + + // Promote (deploy) from /origin/master + verifier = createVerifier("/project-stub", "origin/master", "1.2.0"); + // Activate a profile to enable the retainPlugins configuration + verifier.addCliOption("-Pretain-configuration"); + try { + verifier.executeGoal("deploy"); + + try { + verifier.verifyTextInLog("Compiling"); + throw new VerificationException(PROMOTION_FAILED_MESSAGE); + } catch (VerificationException ve) { + if (ve.getMessage().equals(PROMOTION_FAILED_MESSAGE)) { + throw ve; + } + // Otherwise, it's the VerificationException from looking for "Compiling", and that's expected to fail. + } + + verifier.verifyTextInLog("Generating flattened POM of project"); // This should still be there. + verifier.verifyTextInLog( + "gitflow-helper-maven-plugin: Enabling MasterPromoteExtension. GIT_BRANCH: [origin/master] matches masterBranchPattern"); + verifier.verifyTextInLog("[INFO] Setting release artifact repository to: [releases]"); + verifier.verifyTextInLog( + "[INFO] Resolving & Reattaching existing artifacts from stageDeploymentRepository [test-releases"); + verifier.verifyErrorFreeLog(); + } finally { + verifier.resetStreams(); + } + } } diff --git a/src/test/resources/project-stub/pom.xml b/src/test/resources/project-stub/pom.xml index a4bd877..2b9054b 100644 --- a/src/test/resources/project-stub/pom.xml +++ b/src/test/resources/project-stub/pom.xml @@ -118,7 +118,42 @@ 4.2.0 true + + org.codehaus.mojo + flatten-maven-plugin + 1.2.2 + + + + flatten + + process-resources + + + + + + retain-configuration + + + + + com.e-gineering + gitflow-helper-maven-plugin + ${version.gitflow.plugin} + + + org.codehaus.mojo:flatten-maven-plugin + + + + + + + + +