From df46c918c31691c29171c1a25a5afbcdf52c60e5 Mon Sep 17 00:00:00 2001 From: Arnoud Glimmerveen Date: Wed, 19 Apr 2023 08:04:33 +0200 Subject: [PATCH] Refactored the retainPlugin handling, to allow more fine-grained control to retain only specific execution of a plugin as well. --- .../gitflowhelper/MasterPromoteExtension.java | 85 ++++++++++++------- .../gitflowhelper/MasterSupportBranchIT.java | 10 +++ src/test/resources/project-stub/pom.xml | 32 +++++++ 3 files changed, 97 insertions(+), 30 deletions(-) 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 05d21e3..0c92413 100644 --- a/src/main/java/com/e_gineering/maven/gitflowhelper/MasterPromoteExtension.java +++ b/src/main/java/com/e_gineering/maven/gitflowhelper/MasterPromoteExtension.java @@ -4,19 +4,22 @@ import org.apache.maven.MavenExecutionException; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginExecution; import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.component.annotations.Component; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Maven extension which removes (skips) undesired plugins from the build reactor when running on a master branch. @@ -26,7 +29,7 @@ @Component(role = AbstractMavenLifecycleParticipant.class, hint = "promote-master") public class MasterPromoteExtension extends AbstractBranchDetectingExtension { - private static final Set PLUGIN_WHITELIST = Collections.unmodifiableSet( + private static final Set DEFAULT_PLUGIN_WHITELIST = Collections.unmodifiableSet( new HashSet<>( Arrays.asList( "org.apache.maven.plugins:maven-deploy-plugin", @@ -39,45 +42,54 @@ public class MasterPromoteExtension extends AbstractBranchDetectingExtension { public void afterProjectsRead(final MavenSession session) throws MavenExecutionException { super.afterProjectsRead(session); - // Any plugin which is part of the project goals needs to be retained. - List pluginsToRetain = new ArrayList<>(session.getGoals().size()); + // Build a whitelist of plugin (executions) that should remain while running on master. + // The key of the map is the plugin key, the value is a collection of specific executions of that plugin + // to retain (where an empty collection denotes that all executions should be retained). + final Map> pluginWhitelist = new HashMap<>(); + // First load the default whitelist + DEFAULT_PLUGIN_WHITELIST.forEach(plugin -> pluginWhitelist.put(plugin, Collections.emptyList())); + + // Then determine which plugin(s) are activated through commandline supplied goals List goals = session.getGoals(); for (String goal : goals) { int delimiter = goal.indexOf(":"); if (delimiter != -1) { String prefix = goal.substring(0, delimiter); try { - pluginsToRetain.add(descriptorCreator.findPluginForPrefix(prefix, session)); + pluginWhitelist.put(descriptorCreator.findPluginForPrefix(prefix, session).getKey(), Collections.emptyList()); } catch (NoPluginFoundForPrefixException ex) { logger.warn("gitflow-helper-maven-plugin: Unable to resolve project plugin for prefix: " + prefix + " for goal: " + goal); } } } - // Build up a map of plugins to remove from projects, if we're on the master branch. - Map> pluginsToDrop = new HashMap<>(); - - final List configuredPluginsToRetain; + // Finally parse the configured plugin (executions) to retain if (this.retainPlugins != null) { - configuredPluginsToRetain = this.retainPlugins; - } else { - configuredPluginsToRetain = Collections.emptyList(); + for (String retainPlugin : retainPlugins) { + String[] elements = retainPlugin.split(":"); + if (elements.length != 2 && elements.length != 3) { + throw new MavenExecutionException( + "Expected syntax for retainPlugin: groupId:artifactId[:execution-id] but found " + retainPlugin, + session.getRequest().getPom() + ); + } + final String pluginKey = Plugin.constructKey(elements[0], elements[1]); + if (elements.length == 2) { + pluginWhitelist.put(pluginKey, Collections.emptyList()); + } else { + final Collection executionsToRetain; + if (pluginWhitelist.containsKey(pluginKey)) { + executionsToRetain = pluginWhitelist.get(pluginKey); + } else { + executionsToRetain = new HashSet<>(); + pluginWhitelist.put(pluginKey, executionsToRetain); + } + executionsToRetain.add(elements[2]); + } + } } - for (MavenProject project : session.getProjects()) { - - // 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); - } if (pluginFound) { boolean pruneBuild = false; @@ -95,12 +107,25 @@ public void afterProjectsRead(final MavenSession session) throws MavenExecutionE } if (pruneBuild) { + logger.info("The following plugin (execution) whitelist will be applied: " + pluginWhitelist); for (MavenProject project : session.getProjects()) { - // Drop all the plugins from the build except for the gitflow-helper-maven-plugin, or plugins we - // invoked goals for which could be mapped back to plugins in our project build. - // Goals invoked from the commandline which cannot be mapped back to our project, will get warnings, but should still execute. - // If someone is on 'master' and starts executing goals, we need to allow them to do that. - project.getModel().getBuild().getPlugins().removeAll(pluginsToDrop.get(project)); + // Using the pluginWhiteList, determine which plugin (executions) are allowed to stay. + final Iterator iterator = project.getModel().getBuild().getPlugins().iterator(); + while (iterator.hasNext()) { + Plugin plugin = iterator.next(); + if (pluginWhitelist.containsKey(plugin.getKey())) { + // If the plugin key is present in the whitelist, either all executions must be retained + // (in case of an empty collection), or only those mentioned in the collection. + final Collection executionToRetain = pluginWhitelist.get(plugin.getKey()); + if (!executionToRetain.isEmpty()) { + plugin.getExecutions() + .removeIf(pluginExecution -> !executionToRetain.contains(pluginExecution.getId())); + } + } else { + // If the plugin's key is not present in the whitelist, it can be dropped + iterator.remove(); + } + } } } } 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 e34f89c..c5688e7 100644 --- a/src/test/java/com/e_gineering/maven/gitflowhelper/MasterSupportBranchIT.java +++ b/src/test/java/com/e_gineering/maven/gitflowhelper/MasterSupportBranchIT.java @@ -11,6 +11,7 @@ @RunWith(BlockJUnit4ClassRunner.class) public class MasterSupportBranchIT extends AbstractIntegrationTest { private static final String PROMOTION_FAILED_MESSAGE = "Promotion Deploy from origin/master allowed something to Compile."; + public static final String FILTERED_EXECUTION = "Filtered execution should not be executing"; @Test public void releaseVersionSuccess() throws Exception { @@ -163,6 +164,15 @@ public void dontPruneExplicitlyConfiguredPlugins() throws Exception { } verifier.verifyTextInLog("Generating flattened POM of project"); // This should still be there. + verifier.verifyTextInLog("This execution must be retained"); // This should also still be there + try { + verifier.verifyTextInLog("This execution must be filtered"); // This should not be here + throw new VerificationException(FILTERED_EXECUTION); + } catch (VerificationException ve) { + if (ve.getMessage().equals(FILTERED_EXECUTION)) { + 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]"); diff --git a/src/test/resources/project-stub/pom.xml b/src/test/resources/project-stub/pom.xml index 2b9054b..252d508 100644 --- a/src/test/resources/project-stub/pom.xml +++ b/src/test/resources/project-stub/pom.xml @@ -131,6 +131,37 @@ + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + retain + + run + + validate + + + This execution must be retained + + + + + filter + + run + + validate + + + This execution must be filtered + + + + + @@ -147,6 +178,7 @@ org.codehaus.mojo:flatten-maven-plugin + org.apache.maven.plugins:maven-antrun-plugin:retain