diff --git a/README.md b/README.md index c9d59ee..68a456a 100644 --- a/README.md +++ b/README.md @@ -453,6 +453,41 @@ build lifecycle (plugins, goals, etc) altered. Any plugin other than the gitflow 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. +Elements of the `retainPlugins` can address a complete Maven plugin (format `:`) or a single execution (format `:@`). + +#### Example: + +```xml + + + com.e-gineering + gitflow-helper-maven-plugin + + + + org.apache.maven.plugins:maven-antrun-plugin@do-on-master + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + do-on-master + + run + + deploy + + + + + + +``` + The `promote-master` goal executes when the `gitBranchExpression` resolves to a value matching the `masterBranchPattern` or `supportBranchPattern` regular expression. This goal resolves (and downloads) the artifacts matching the current `${project.version}` from the stage repository, then attaches them to the diff --git a/src/main/java/com/e_gineering/maven/gitflowhelper/AbstractGitflowBasedRepositoryMojo.java b/src/main/java/com/e_gineering/maven/gitflowhelper/AbstractGitflowBasedRepositoryMojo.java index 9acc985..ae4ece3 100644 --- a/src/main/java/com/e_gineering/maven/gitflowhelper/AbstractGitflowBasedRepositoryMojo.java +++ b/src/main/java/com/e_gineering/maven/gitflowhelper/AbstractGitflowBasedRepositoryMojo.java @@ -91,11 +91,21 @@ private static PrintWriter newPrintWriter(File catalog) throws FileNotFoundExcep * @throws MojoFailureException if the repository id is not defined. */ ArtifactRepository getDeploymentRepository(final String id) throws MojoFailureException { + return getDeploymentRepositoryOpt(id).orElseThrow(() -> new MojoFailureException("No Repository with id `" + id + "` is defined.")); + } + + /** + * Get an repository by the repository ID. + * + * @param id the repository identifier + * @return optional to the repository (never @Code{null}) + */ + Optional getDeploymentRepositoryOpt(String id) { Objects.requireNonNull(id, "A repository id must be specified."); Optional repo = project.getRemoteArtifactRepositories().stream().filter(r -> r.getId().equals(id)).findFirst(); if (repo.isPresent()) { - return repo.get(); + return repo; } Optional mirroredRepo = project.getRemoteArtifactRepositories().stream() @@ -105,9 +115,10 @@ ArtifactRepository getDeploymentRepository(final String id) throws MojoFailureEx if(artifactRepository.getAuthentication() == null) { artifactRepository.setAuthentication(getAuthentication(artifactRepository)); } - return artifactRepository; + return Optional.of(artifactRepository); } - throw new MojoFailureException("No Repository with id `" + id + "` is defined."); + + return Optional.empty(); } /** @@ -210,10 +221,11 @@ private void catalogArtifact(PrintWriter writer, Artifact artifact) { * @throws MojoExecutionException for any unhandled maven exception */ void attachExistingArtifacts(@Nullable final String sourceRepository, final boolean disableLocal) - throws MojoExecutionException, MojoFailureException { + throws MojoExecutionException { List remoteArtifactRepositories = new ArrayList<>(); - Optional repo = project.getRemoteArtifactRepositories().stream().filter(r -> r.getId().equals(sourceRepository)).findFirst(); + + Optional repo = getDeploymentRepositoryOpt(sourceRepository); if (repo.isPresent()) { remoteArtifactRepositories.add(repo.get()); } else { 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 0c92413..9a1e1fa 100644 --- a/src/main/java/com/e_gineering/maven/gitflowhelper/MasterPromoteExtension.java +++ b/src/main/java/com/e_gineering/maven/gitflowhelper/MasterPromoteExtension.java @@ -1,14 +1,5 @@ package com.e_gineering.maven.gitflowhelper; -import org.apache.maven.AbstractMavenLifecycleParticipant; -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.Arrays; import java.util.Collection; import java.util.Collections; @@ -18,8 +9,17 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.maven.AbstractMavenLifecycleParticipant; +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.apache.maven.shared.utils.StringUtils; +import org.codehaus.plexus.component.annotations.Component; /** * Maven extension which removes (skips) undesired plugins from the build reactor when running on a master branch. @@ -37,18 +37,18 @@ public class MasterPromoteExtension extends AbstractBranchDetectingExtension { ) ) ); - + @Override public void afterProjectsRead(final MavenSession session) throws MavenExecutionException { super.afterProjectsRead(session); // 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<>(); + // The key of the map is the plugin key, the value is a set of specific executions of that plugin + // to retain (where an '*' 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())); + DEFAULT_PLUGIN_WHITELIST.forEach(plugin -> pluginWhitelist.computeIfAbsent(plugin, k -> new HashSet<>()).add("*")); // Then determine which plugin(s) are activated through commandline supplied goals List goals = session.getGoals(); @@ -57,7 +57,9 @@ public void afterProjectsRead(final MavenSession session) throws MavenExecutionE if (delimiter != -1) { String prefix = goal.substring(0, delimiter); try { - pluginWhitelist.put(descriptorCreator.findPluginForPrefix(prefix, session).getKey(), Collections.emptyList()); + String pluginKey = descriptorCreator.findPluginForPrefix(prefix, session).getKey(); + pluginWhitelist.computeIfAbsent(pluginKey, k -> new HashSet<>()).add("*"); + logger.debug("Retain plugin " + pluginKey + ", it was supplied on the command line (goal=" + goal +")"); } catch (NoPluginFoundForPrefixException ex) { logger.warn("gitflow-helper-maven-plugin: Unable to resolve project plugin for prefix: " + prefix + " for goal: " + goal); } @@ -66,26 +68,28 @@ public void afterProjectsRead(final MavenSession session) throws MavenExecutionE // Finally parse the configured plugin (executions) to retain if (this.retainPlugins != null) { + + Pattern pattern = Pattern.compile("(?[^:]+):(?[^@]+)(@(?.+))?"); 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); + Matcher matcher = pattern.matcher(retainPlugin); + if(matcher.matches()) { + + final String pluginKey = Plugin.constructKey(matcher.group("groupId"), matcher.group("artifactId")); + + Set executionWhiteList = pluginWhitelist.computeIfAbsent(pluginKey, (k) -> new HashSet<>()); + + String executionId = matcher.group("executionId"); + + if(StringUtils.isBlank(executionId)) { + executionWhiteList.add("*"); } else { - executionsToRetain = new HashSet<>(); - pluginWhitelist.put(pluginKey, executionsToRetain); + executionWhiteList.add(executionId.trim()); } - executionsToRetain.add(elements[2]); + + } else { + throw new MavenExecutionException( + "Expected syntax for retainPlugin: groupId:artifactId[@execution-id] but found " + retainPlugin, + session.getRequest().getPom()); } } } @@ -113,17 +117,28 @@ public void afterProjectsRead(final MavenSession session) throws MavenExecutionE 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 + final Collection executionToRetain = pluginWhitelist.get(plugin.getKey()); + if(executionToRetain == null) { iterator.remove(); + continue; + } + if(executionToRetain.contains("*")) { + logger.debug("Retain all executions of plugin " + plugin.getKey()); + continue; + } + + Iterator executionIterator = plugin.getExecutions().iterator(); + while(executionIterator.hasNext()) { + PluginExecution execution = executionIterator.next(); + + if(executionToRetain.remove(execution.getId())) { + logger.debug("Retain execution "+ plugin.getKey() + "@" + execution.getId()); + } else { + executionIterator.remove(); + } + } + if(!executionToRetain.isEmpty()) { + logger.warn("Found unknown executions " + executionToRetain + " for plugin " + plugin.getKey() + " on the retainPlugins"); } } }