Skip to content

Improve logging for retain plugins #142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<groupId>:<artifactId>`) or a single execution (format `<groupId>:<artifactId>@<executionId>`).

#### Example:

```xml
<plugins>
<plugin>
<groupId>com.e-gineering</groupId>
<artifactId>gitflow-helper-maven-plugin</artifactId>
<configuration>
<retainPlugins>
<!-- Execute the do-on-master execution of the maven-antrun-plugin on the master build -->
<retainPlugin>org.apache.maven.plugins:maven-antrun-plugin@do-on-master</retainPlugin>
</retainPlugins>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>do-on-master</id>
<goals>
<goal>run</goal>
</goals>
<phase>deploy</phase>
</execution>
<configuration>
</configuration>
</executions>
</plugin>
</plugins>
```

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ArtifactRepository> getDeploymentRepositoryOpt(String id) {
Objects.requireNonNull(id, "A repository id must be specified.");

Optional<ArtifactRepository> repo = project.getRemoteArtifactRepositories().stream().filter(r -> r.getId().equals(id)).findFirst();
if (repo.isPresent()) {
return repo.get();
return repo;
}

Optional<ArtifactRepository> mirroredRepo = project.getRemoteArtifactRepositories().stream()
Expand All @@ -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();
}

/**
Expand Down Expand Up @@ -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<ArtifactRepository> remoteArtifactRepositories = new ArrayList<>();
Optional<ArtifactRepository> repo = project.getRemoteArtifactRepositories().stream().filter(r -> r.getId().equals(sourceRepository)).findFirst();

Optional<ArtifactRepository> repo = getDeploymentRepositoryOpt(sourceRepository);
if (repo.isPresent()) {
remoteArtifactRepositories.add(repo.get());
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.
Expand All @@ -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<String, Collection<String>> 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<String, Set<String>> 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<String> goals = session.getGoals();
Expand All @@ -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);
}
Expand All @@ -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("(?<groupId>[^:]+):(?<artifactId>[^@]+)(@(?<executionId>.+))?");
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<String> 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<String> 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());
}
}
}
Expand Down Expand Up @@ -113,17 +117,28 @@ public void afterProjectsRead(final MavenSession session) throws MavenExecutionE
final Iterator<Plugin> 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<String> 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<String> 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<PluginExecution> 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");
}
}
}
Expand Down