com.atlassian.maven.enforcer.BanVersionDeps Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of maven-enforcer-rules Show documentation
Show all versions of maven-enforcer-rules Show documentation
The Loving Iron Fist of Maven™ extended with more enforcer rules.
package com.atlassian.maven.enforcer;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.enforcer.AbstractBanDependencies;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.artifact.filter.StrictPatternExcludesArtifactFilter;
import org.apache.maven.shared.artifact.filter.StrictPatternIncludesArtifactFilter;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@SuppressWarnings ("UnusedDeclaration")
public class BanVersionDeps extends AbstractBanDependencies
{
/**
* Dependencies with version strings that match are banned
*/
private String bannedDependencyVersionRegexp;
/**
* Will not fail if artifact being built has a version string that matches
*/
private String noFailReactorVersionRegexp;
/**
* Will not fail if artifact being built is a snapshot
*/
private boolean noFailSnapshots;
/**
* Ignore dependencies which have a scope of "test"
*/
private boolean ignoreTest;
/**
* Exclude these dependencies in the check
*/
private List excludes;
/**
* Include only these dependencies in the check
*/
private List includes;
Pattern bannedDependencyVersionPattern;
Pattern noFailReactorVersionPattern;
boolean failBuild;
/**
* Format is "::"
*/
Set> reactorGAVs;
@Override
public void execute(final EnforcerRuleHelper helper) throws EnforcerRuleException
{
final MavenProject reactorProject = retrieveReactorProject(helper);
// I know it sucks to use members to store state, but they're valid for the lifetime of this rule
// the alternative is reimplementing the private bits of {@{@link AbstractBanDependencies}
bannedDependencyVersionPattern = parseRegexp(bannedDependencyVersionRegexp, "bannedDependencyVersionRegexp", true);
noFailReactorVersionPattern = parseRegexp(noFailReactorVersionRegexp, "noFailReactorVersionRegexp", false);
failBuild = shouldFailBuild(reactorProject);
reactorGAVs = reactorProject.getProjectReferences().keySet();
decorateMessage();
super.execute(helper);
}
/**
* Check all resolved dependencies' version string against {@link #bannedDependencyVersionRegexp}. Any that match
* will be flagged as banned.
*
* The enforcer will fail unless the reactor matched noFailReactorVersionRegexp or noFailSnapshots is specified for
* a snapshot.
*
* @param dependencies mandatory
* @param log mandatory
* @return possibly empty
* @throws org.apache.maven.enforcer.rule.api.EnforcerRuleException on invalid or missing regexp
*/
@Override
protected Set checkDependencies(final Set dependencies, final Log log)
throws EnforcerRuleException
{
Set bannedArtifacts = new HashSet();
// check each dependency; transitives will not be included if searchTransitive is false
for (Artifact dependency : includeExclude(dependencies))
{
if (dependency.getVersion() != null)
{
// ignore test dependencies if configured
if (!ignoreTest || !Artifact.SCOPE_TEST.equals(dependency.getScope()))
{
// ban any dependencies which have a resolved version that matches
Matcher versionMatcher = bannedDependencyVersionPattern.matcher(dependency.getVersion());
if (versionMatcher.matches())
{
// don't check dependencies that are within this reactor
if (!reactorGAVs.contains(dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getVersion()))
{
bannedArtifacts.add(dependency);
}
}
}
}
}
if (failBuild)
{
// fail the build by returning the banned list
return bannedArtifacts;
}
else
{
// warn about the failures but don't fail the build
if (!bannedArtifacts.isEmpty())
{
StringBuilder buf = new StringBuilder();
buf.append("\n");
buf.append(getMessage());
buf.append("\n");
for (Artifact artifact : bannedArtifacts)
{
buf.append(getErrorMessage(artifact));
}
buf.append("Use 'mvn dependency:tree' to locate the source of the banned dependencies.");
log.warn(buf.toString());
}
return Collections.emptySet();
}
}
/**
* Filter input based on {@link #includes} and {@link #excludes} if they are present
*
* @param dependencies mandatory
* @return possibly empty
*/
Set includeExclude(final Set dependencies)
{
Set includedExcluded = new HashSet();
// build a filter with the includes and excldes, but only if they're present
AndArtifactFilter filter = new AndArtifactFilter();
if (includes != null)
{
filter.add(new StrictPatternIncludesArtifactFilter(includes));
}
if (excludes != null)
{
filter.add(new StrictPatternExcludesArtifactFilter(excludes));
}
// filter the dependencies; this will just pass through if there are no includes/excludes
for (Artifact dependency : dependencies)
{
if (filter.include(dependency))
{
includedExcluded.add(dependency);
}
}
return includedExcluded;
}
/**
* Build will fail on a banned dependency being found unless:
* noFailSnapshots is set and we are building a snapshot
* or
* noFailReactorVersionRegexp matchjes the reactor's version
*/
boolean shouldFailBuild(final MavenProject reactorProject)
{
// snapshots are allowed to pass
if (noFailSnapshots && reactorProject.getArtifact().isSnapshot())
{
return false;
}
// user allows certain versions to pass
if (noFailReactorVersionPattern != null && noFailReactorVersionPattern.matcher(reactorProject.getArtifact().getVersion()).matches())
{
return false;
}
// default to failing on finding a banned dependency
return true;
}
MavenProject retrieveReactorProject(final EnforcerRuleHelper helper) throws EnforcerRuleException
{
try
{
return ((MavenProject) helper.evaluate( "${project}" ));
}
catch ( ExpressionEvaluationException eee )
{
throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee );
}
}
Pattern parseRegexp(final String regexp, final String paramName, final boolean mandatory) throws EnforcerRuleException
{
if (mandatory && regexp == null)
{
throw new EnforcerRuleException("missing " + paramName);
}
if (regexp != null)
{
try
{
return Pattern.compile(regexp);
}
catch (PatternSyntaxException e)
{
throw new EnforcerRuleException("invalid " + paramName + ": '" + regexp + "'");
}
}
else
{
return null;
}
}
public String getBannedDependencyVersionRegexp()
{
return bannedDependencyVersionRegexp;
}
public void setBannedDependencyVersionRegexp(final String bannedDependencyVersionRegexp)
{
this.bannedDependencyVersionRegexp = bannedDependencyVersionRegexp;
}
public String getNoFailReactorVersionRegexp()
{
return noFailReactorVersionRegexp;
}
public void setNoFailReactorVersionRegexp(final String noFailReactorVersionRegexp)
{
this.noFailReactorVersionRegexp = noFailReactorVersionRegexp;
}
public boolean isNoFailSnapshots()
{
return noFailSnapshots;
}
public void setNoFailSnapshots(final boolean noFailSnapshots)
{
this.noFailSnapshots = noFailSnapshots;
}
public boolean isIgnoreTest()
{
return ignoreTest;
}
public void setIgnoreTest(final boolean ignoreTest)
{
this.ignoreTest = ignoreTest;
}
public List getExcludes()
{
return excludes;
}
public void setExcludes(final List excludes)
{
this.excludes = excludes;
}
public List getIncludes()
{
return includes;
}
public void setIncludes(final List includes)
{
this.includes = includes;
}
private void decorateMessage()
{
setMessage(" #######\n"
+ " ###########\n"
+ " ###############\n"
+ " #################\n"
+ " #################\n"
+ " ################# ##\n"
+ " ################# ######\n"
+ " ################# ##########\n"
+ " ################# ##############\n"
+ " ################# ##################\n"
+ " ################# ######################\n"
+ " ################# ##########################\n"
+ " ################# ##############################\n"
+ " ################# ##################################\n"
+ " ################# ######################################\n"
+ " ################ ##########################################\n"
+ " ############### ############################# ############\n"
+ " ############ ######################### ############\n"
+ " ######## ########################## #############\n"
+ " #### ############################## ########################\n"
+ " ############################## #########################\n"
+ " ############################ #########################\n"
+ " ##################### ######################### ####\n"
+ " ##################################################### ########\n"
+ " ################################################# ###########\n"
+ " ############################################## ##############\n"
+ " ############################################# ################\n"
+ " ############################################# #################\n"
+ " ############################################# #################\n"
+ " ############################################# #################\n"
+ " ############################################# #################\n"
+ " ###################### ##################### #################\n"
+ " ###################### ################# #################\n"
+ " ###################### ############# #################\n"
+ " ####################### ######### #################\n"
+ " ######################### ##### #################\n"
+ " ########################## # #################\n"
+ "########################## #################\n"
+ "######################## #################\n"
+ "###################### ###############\n"
+ "##################### ##########\n"
+ " ################### #######\n"
+ " ################\n"
+ " ############\n"
+ " ########\n"
+ getMessage());
}
}