6 Commits

Author SHA1 Message Date
cf2fdf42fa v1.1.1 pom 2023-05-25 15:24:41 -04:00
3d7d8bb769 Merge branch 'develop' into stable 2023-05-25 15:24:08 -04:00
d22f657f4e added remote config import support 2023-05-25 15:23:54 -04:00
01768754f0 v1.1.0 pom 2023-05-25 13:22:00 -04:00
e241137209 Merge branch 'develop' into stable 2023-05-25 13:19:36 -04:00
e02be65780 added purge goal 2023-05-25 13:19:24 -04:00
10 changed files with 658 additions and 175 deletions

View File

@@ -31,13 +31,14 @@ This is a maven plugin that allows for developers and organizations to ban Maven
</artifact>
<artifact>com.inteligr8:ban-maven-plugin:[,1.0.0)</artifact>
<artifact>log4j:log4j</artifact>
<artifact>org\.springframework.*::[,4.0.0.RELEASE)</artifact>
<artifact>org\.springframe.+::[,4.0.0.RELEASE)</artifact>
</includes>
<excludes>
<artifact>
....
</artifact>
</excludes>
<import>https://domain:port/path/file</import>
</configuration>
</plugin>
...
@@ -61,5 +62,3 @@ The `version` element supports the standard Maven specification. You can match
There is nothing stopping you from specifying two `artifact` elements with the exact same values. So you can ban multiple version ranges of the same artifact by using multiple `artifact` elements.
If you *include* all versions by omitting the `version` element, you can still *exclude* (unban) certain versions, like `[1.2.17,)`.
It is recommended that you look into [Maven Tiles](https://github.com/repaint-io/maven-tiles) so you can use a tile to define your banned artifacts and side load them into all your projects.

View File

@@ -7,7 +7,7 @@
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>1.0.0</version>
<version>1.1.1</version>
<packaging>maven-plugin</packaging>
<name>Ban Dependencies Maven Plugin</name>
@@ -146,7 +146,6 @@
<configuration>
<projectsDirectory>${basedir}/src/it</projectsDirectory>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
<localRepositoryPath>${project.build.directory}/it-repo</localRepositoryPath>
<mavenHome>${env.MAVEN_HOME}</mavenHome>
<debug>true</debug>
<ignoreFailures>true</ignoreFailures>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin-log4j-old</artifactId>
<version>@pom.version@</version>
<packaging>jar</packaging>
<name>Log4j Ban Plugin Tests</name>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>@pom.version@</version>
<extensions>true</extensions>
<configuration>
<includes>
<artifact>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</artifact>
</includes>
<excludes>
<artifact>log4j:log.+:[1.2.17,)</artifact>
</excludes>
</configuration>
<executions>
<execution>
<id>purge</id>
<phase>prepare-package</phase>
<goals><goal>purge-repo</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,130 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.maven.ban;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.inteligr8.maven.model.ArtifactFilter;
public class AbstractBanConfiguration implements BanConfiguration {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Pattern artifactPattern = Pattern.compile("^([^:]+):([^:]+)(:([^:]+))?$");
private final Pattern notRegexPattern = Pattern.compile("^[A-Za-z0-9_\\.]*$");
private final List<ArtifactFilter> includeArtifacts = new LinkedList<>();
private final List<ArtifactFilter> excludeArtifacts = new LinkedList<>();
public void init(Xpp3Dom rootDom) throws IOException, MojoFailureException {
if (rootDom == null)
return;
Xpp3Dom importDom = rootDom.getChild("import");
if (importDom != null) {
String url = StringUtils.trimToNull(importDom.getValue());
BanConfigurationDownloader downloader = new BanConfigurationDownloader(url);
this.includeArtifacts.addAll(downloader.getIncludeArtifacts());
this.excludeArtifacts.addAll(downloader.getExcludeArtifacts());
}
Xpp3Dom includesDom = rootDom.getChild("includes");
if (includesDom != null)
this.includeArtifacts.addAll(this.parseArtifacts(includesDom));
Xpp3Dom excludesDom = rootDom.getChild("excludes");
if (excludesDom != null)
this.excludeArtifacts.addAll(this.parseArtifacts(excludesDom));
this.logger.debug("Include artifacts: {}", this.includeArtifacts);
this.logger.debug("Exclude artifacts: {}", this.excludeArtifacts);
}
public List<ArtifactFilter> getIncludeArtifacts() {
return this.includeArtifacts;
}
public List<ArtifactFilter> getExcludeArtifacts() {
return this.excludeArtifacts;
}
private List<ArtifactFilter> parseArtifacts(Xpp3Dom artifactsDom) {
List<ArtifactFilter> filters = new LinkedList<>();
for (Xpp3Dom artifactDom : artifactsDom.getChildren("artifact")) {
ArtifactFilter filter = new ArtifactFilter();
String versionSpec = null;
if (artifactDom.getChildCount() == 0) {
Matcher matcher = this.artifactPattern.matcher(artifactDom.getValue());
if (!matcher.matches()) {
this.logger.warn("The artifact format '{}' does not match the expected regular expression: {}; ignoring ...", artifactDom.getValue(), this.artifactPattern.pattern());
continue;
}
if (this.notRegexPattern.matcher(matcher.group(1)).matches()) {
filter.setGroupId(StringUtils.trimToNull(matcher.group(1)));
} else {
filter.setGroupIdRegex(StringUtils.trimToNull(matcher.group(1)));
}
if (this.notRegexPattern.matcher(matcher.group(2)).matches()) {
filter.setArtifactId(StringUtils.trimToNull(matcher.group(2)));
} else {
filter.setArtifactIdRegex(StringUtils.trimToNull(matcher.group(2)));
}
versionSpec = StringUtils.trimToNull(matcher.group(4));
} else {
filter.setGroupId(this.getChildValue(artifactDom, "groupId"));
filter.setGroupIdRegex(this.getChildValue(artifactDom, "groupIdRegex"));
filter.setArtifactId(this.getChildValue(artifactDom, "artifactId"));
filter.setArtifactIdRegex(this.getChildValue(artifactDom, "artifactIdRegex"));
versionSpec = this.getChildValue(artifactDom, "version");
}
if (versionSpec != null) {
try {
VersionRange versionRange = VersionRange.createFromVersionSpec(versionSpec);
filter.setVersionRange(versionRange);
} catch (InvalidVersionSpecificationException ivse) {
this.logger.warn("The artifact '{}' has an invalid version specification; the artifact element will be ignored: {}", ivse.getMessage());
continue;
}
}
filters.add(filter);
}
return filters;
}
private String getChildValue(Xpp3Dom dom, String child) {
Xpp3Dom childDom = dom.getChild(child);
return childDom == null ? null : StringUtils.trimToNull(childDom.getValue());
}
}

View File

@@ -0,0 +1,27 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.maven.ban;
import java.util.List;
import com.inteligr8.maven.model.ArtifactFilter;
public interface BanConfiguration {
List<ArtifactFilter> getIncludeArtifacts();
List<ArtifactFilter> getExcludeArtifacts();
}

View File

@@ -0,0 +1,54 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.maven.ban;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BanConfigurationDownloader extends AbstractBanConfiguration {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public BanConfigurationDownloader(String url) throws IOException, MojoFailureException {
try {
Xpp3Dom rootDom = this.load(new URL(url));
this.init(rootDom);
} catch (XmlPullParserException xppe) {
throw new MojoFailureException(xppe.getMessage(), xppe);
}
}
private Xpp3Dom load(URL url) throws IOException, XmlPullParserException {
InputStream istream = url.openStream();
BufferedInputStream bistream = new BufferedInputStream(istream, 16384);
try {
this.logger.debug("Downloading configuration: {}", url);
return Xpp3DomBuilder.build(bistream, "utf-8");
} finally {
bistream.close();
}
}
}

View File

@@ -14,172 +14,17 @@
*/
package com.inteligr8.maven.ban;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.IOException;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.inteligr8.maven.model.ArtifactFilter;
public class BanConfigurationParser implements DependencyFilter {
public class BanConfigurationParser extends AbstractBanConfiguration {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Pattern artifactPattern = Pattern.compile("^([^:]+):([^:]+)(:([^:]+))?$");
private final Pattern notRegexPattern = Pattern.compile("^[A-Za-z0-9_\\.]*$");
private final List<ArtifactFilter> includeArtifacts;
private final List<ArtifactFilter> excludeArtifacts;
private boolean failFast = false;
public BanConfigurationParser(Plugin plugin) {
public BanConfigurationParser(Plugin plugin) throws IOException, MojoFailureException {
Xpp3Dom rootDom = (Xpp3Dom) plugin.getConfiguration();
if (rootDom == null) {
this.includeArtifacts = Collections.emptyList();
this.excludeArtifacts = Collections.emptyList();
return;
}
Xpp3Dom includesDom = rootDom.getChild("includes");
if (includesDom != null) {
this.includeArtifacts = this.parseArtifacts(includesDom);
this.logger.debug("Include artifacts: {}", this.includeArtifacts);
} else {
this.includeArtifacts = Collections.emptyList();
}
Xpp3Dom excludesDom = rootDom.getChild("excludes");
if (excludesDom != null) {
this.excludeArtifacts = this.parseArtifacts(excludesDom);
this.logger.debug("Exclude artifacts: {}", this.excludeArtifacts);
} else {
this.excludeArtifacts = Collections.emptyList();
}
}
public void setFailFast(boolean failFast) {
this.failFast = failFast;
}
private List<ArtifactFilter> parseArtifacts(Xpp3Dom artifactsDom) {
List<ArtifactFilter> filters = new LinkedList<>();
for (Xpp3Dom artifactDom : artifactsDom.getChildren("artifact")) {
ArtifactFilter filter = new ArtifactFilter();
String versionSpec = null;
if (artifactDom.getChildCount() == 0) {
Matcher matcher = this.artifactPattern.matcher(artifactDom.getValue());
if (!matcher.matches()) {
this.logger.warn("The artifact format '{}' does not match the expected regular expression: {}; ignoring ...", artifactDom.getValue(), this.artifactPattern.pattern());
continue;
}
if (this.notRegexPattern.matcher(matcher.group(1)).matches()) {
filter.setGroupId(StringUtils.trimToNull(matcher.group(1)));
} else {
filter.setGroupIdRegex(StringUtils.trimToNull(matcher.group(1)));
}
if (this.notRegexPattern.matcher(matcher.group(1)).matches()) {
filter.setArtifactId(StringUtils.trimToNull(matcher.group(2)));
} else {
filter.setArtifactIdRegex(StringUtils.trimToNull(matcher.group(2)));
}
versionSpec = StringUtils.trimToNull(matcher.group(4));
} else {
filter.setGroupId(this.getChildValue(artifactDom, "groupId"));
filter.setGroupIdRegex(this.getChildValue(artifactDom, "groupIdRegex"));
filter.setArtifactId(this.getChildValue(artifactDom, "artifactId"));
filter.setArtifactIdRegex(this.getChildValue(artifactDom, "artifactIdRegex"));
versionSpec = this.getChildValue(artifactDom, "version");
}
if (versionSpec != null) {
try {
VersionRange versionRange = VersionRange.createFromVersionSpec(versionSpec);
filter.setVersionRange(versionRange);
} catch (InvalidVersionSpecificationException ivse) {
this.logger.warn("The artifact '{}' has an invalid version specification; the artifact element will be ignored: {}", ivse.getMessage());
continue;
}
}
filters.add(filter);
}
return filters;
}
private String getChildValue(Xpp3Dom dom, String child) {
Xpp3Dom childDom = dom.getChild(child);
return childDom == null ? null : StringUtils.trimToNull(childDom.getValue());
}
@Override
public boolean accept(DependencyNode node, List<DependencyNode> parents) {
this.logger.debug("Evaluating dependency '{}'", node);
boolean ban = false;
for (ArtifactFilter afilter : this.includeArtifacts) {
Artifact depArtifact = node.getArtifact();
if (this.matches(afilter.getGroupId(), afilter.getGroupIdRegex(), depArtifact.getGroupId()) &&
this.matches(afilter.getArtifactId(), afilter.getArtifactIdRegex(), depArtifact.getArtifactId()) &&
this.withinRange(afilter.getVersionRange(), depArtifact.getVersion())) {
this.logger.debug("The dependency '{}' matches the ban inclusion filter", depArtifact);
ban = true;
break;
}
}
if (!ban)
return false;
for (ArtifactFilter afilter : this.excludeArtifacts) {
Artifact depArtifact = node.getArtifact();
if (this.matches(afilter.getGroupId(), afilter.getGroupIdRegex(), depArtifact.getGroupId()) &&
this.matches(afilter.getArtifactId(), afilter.getArtifactIdRegex(), depArtifact.getArtifactId()) &&
this.withinRange(afilter.getVersionRange(), depArtifact.getVersion())) {
this.logger.debug("The dependency '{}' matches the ban exlusion filter", depArtifact);
return false;
}
}
if (this.failFast) {
// plugin resolution downloads banned dependencies unless we fail now; not later
throw new RuntimeException("Banned dependency detected: " + node + " => " + parents);
} else {
return true;
}
}
private boolean matches(String exactFilter, String regexFilter, String value) {
if (exactFilter == null && regexFilter == null) {
return true;
} else if (exactFilter != null) {
return exactFilter.equals(value);
} else {
Pattern filterPattern = Pattern.compile(regexFilter);
Matcher matcher = filterPattern.matcher(value);
return matcher.matches();
}
}
private boolean withinRange(VersionRange versionRange, String version) {
return versionRange == null || versionRange.containsVersion(new DefaultArtifactVersion(version));
this.init(rootDom);
}
}

View File

@@ -0,0 +1,102 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.maven.ban;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.inteligr8.maven.model.ArtifactFilter;
public class BanDependencyFilter implements DependencyFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final List<ArtifactFilter> includeArtifacts;
private final List<ArtifactFilter> excludeArtifacts;
private boolean failFast = false;
public BanDependencyFilter(List<ArtifactFilter> includeArtifacts, List<ArtifactFilter> excludeArtifacts) {
this.includeArtifacts = includeArtifacts;
this.excludeArtifacts = excludeArtifacts;
}
public void setFailFast(boolean failFast) {
this.failFast = failFast;
}
@Override
public boolean accept(DependencyNode node, List<DependencyNode> parents) {
this.logger.debug("Evaluating dependency '{}'", node);
Artifact depArtifact = node.getArtifact();
boolean ban = false;
for (ArtifactFilter afilter : this.includeArtifacts) {
if (this.matches(afilter.getGroupId(), afilter.getGroupIdRegex(), depArtifact.getGroupId()) &&
this.matches(afilter.getArtifactId(), afilter.getArtifactIdRegex(), depArtifact.getArtifactId()) &&
this.withinRange(afilter.getVersionRange(), depArtifact.getVersion())) {
this.logger.debug("The dependency '{}' matches the ban inclusion filter", depArtifact);
ban = true;
break;
}
}
if (!ban)
return false;
for (ArtifactFilter afilter : this.excludeArtifacts) {
if (this.matches(afilter.getGroupId(), afilter.getGroupIdRegex(), depArtifact.getGroupId()) &&
this.matches(afilter.getArtifactId(), afilter.getArtifactIdRegex(), depArtifact.getArtifactId()) &&
this.withinRange(afilter.getVersionRange(), depArtifact.getVersion())) {
this.logger.debug("The dependency '{}' matches the ban exlusion filter", depArtifact);
return false;
}
}
if (this.failFast) {
// plugin resolution downloads banned dependencies unless we fail now; not later
throw new RuntimeException("Banned dependency detected: " + node + " => " + parents);
} else {
return true;
}
}
private boolean matches(String exactFilter, String regexFilter, String value) {
if (exactFilter == null && regexFilter == null) {
return true;
} else if (exactFilter != null) {
return exactFilter.equals(value);
} else {
Pattern filterPattern = Pattern.compile(regexFilter);
Matcher matcher = filterPattern.matcher(value);
return matcher.matches();
}
}
private boolean withinRange(VersionRange versionRange, String version) {
boolean within = versionRange == null || versionRange.containsVersion(new DefaultArtifactVersion(version));
this.logger.debug("Tested version range: {} <=> {}: {}", versionRange, version, within);
return within;
}
}

View File

@@ -14,6 +14,7 @@
*/
package com.inteligr8.maven.ban;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
@@ -25,6 +26,7 @@ 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.plugin.MojoFailureException;
import org.apache.maven.plugin.PluginResolutionException;
import org.apache.maven.plugin.internal.PluginDependenciesResolver;
import org.apache.maven.project.DefaultDependencyResolutionRequest;
@@ -44,9 +46,9 @@ import org.slf4j.LoggerFactory;
@Singleton
public class BanExtension extends AbstractMavenLifecycleParticipant {
private static final String THIS_PLUGIN_GROUP_ID = "com.inteligr8";
private static final String THIS_PLUGIN_ARTIFACT_ID = "ban-maven-plugin";
private static final String THIS_PLUGIN_KEY = THIS_PLUGIN_GROUP_ID + ":" + THIS_PLUGIN_ARTIFACT_ID;
public static final String THIS_PLUGIN_GROUP_ID = "com.inteligr8";
public static final String THIS_PLUGIN_ARTIFACT_ID = "ban-maven-plugin";
public static final String THIS_PLUGIN_KEY = THIS_PLUGIN_GROUP_ID + ":" + THIS_PLUGIN_ARTIFACT_ID;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -59,18 +61,20 @@ public class BanExtension extends AbstractMavenLifecycleParticipant {
@Override
public void afterProjectsRead(MavenSession session) throws MavenExecutionException {
MavenProject project = session.getCurrentProject();
BanConfigurationParser config = this.getConfiguration(project);
BanConfiguration config = this.getConfiguration(project);
if (config == null)
return;
config.setFailFast(true);
BanDependencyFilter depFilter = new BanDependencyFilter(config.getIncludeArtifacts(), config.getExcludeArtifacts());
depFilter.setFailFast(true);
try {
for (Plugin plugin : project.getBuildPlugins()) {
this.logger.debug("Evaluating plugin dependencies: {}", plugin);
Artifact artifact = new DefaultArtifact(plugin.getId());
DependencyNode depNodeRoot = this.pluginDepResolver.resolve(plugin, artifact, config, project.getRemotePluginRepositories(), session.getRepositorySession());
List<Dependency> bannedDependencies = this.crawlDependencyTree(depNodeRoot, config);
DependencyNode depNodeRoot = this.pluginDepResolver.resolve(plugin, artifact, depFilter, project.getRemotePluginRepositories(), session.getRepositorySession());
List<Dependency> bannedDependencies = this.crawlDependencyTree(depNodeRoot, depFilter);
if (!bannedDependencies.isEmpty())
throw new MavenExecutionException("Banned dependencies were detected in plugin '" + plugin + "': " + bannedDependencies, project.getFile());
}
@@ -78,10 +82,10 @@ public class BanExtension extends AbstractMavenLifecycleParticipant {
throw new MavenExecutionException(pre.getMessage(), pre);
}
config.setFailFast(false);
depFilter.setFailFast(false);
DefaultDependencyResolutionRequest request = new DefaultDependencyResolutionRequest(project, session.getRepositorySession());
request.setResolutionFilter(config);
request.setResolutionFilter(depFilter);
try {
DependencyResolutionResult result = this.projDepResolver.resolve(request);
@@ -102,7 +106,11 @@ public class BanExtension extends AbstractMavenLifecycleParticipant {
this.logger.warn("The '{}' plugin must be defined with '<extensions>true</extensions>'; ignoring plugin", plugin.getId());
return null;
} else {
return new BanConfigurationParser(plugin);
try {
return new BanConfigurationParser(plugin);
} catch (IOException | MojoFailureException e) {
throw new MavenExecutionException(e.getMessage(), project.getFile());
}
}
}

View File

@@ -0,0 +1,263 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.maven.ban;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
import com.inteligr8.maven.model.ArtifactFilter;
@Mojo( name = "purge-repo", threadSafe = true )
@Component( role = org.apache.maven.plugin.Mojo.class )
public class PurgeRepoMojo extends AbstractMojo {
@Inject
private MavenSession session;
@Parameter(name = "skip", defaultValue = "false")
private boolean skip = false;
@Parameter(name = "dryRun", defaultValue = "false")
private boolean dryRun = false;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (this.skip) {
this.getLog().debug("Skipping purge of banned artifacts");
} else {
this.getLog().info("Purging banned artifacts from local repository: " + this.session.getLocalRepository().getBasedir());
try {
this.purge();
} catch (IOException ie) {
throw new MojoFailureException(ie.getMessage(), ie);
}
}
}
private void purge() throws MojoFailureException, IOException {
List<Path> includePaths = new LinkedList<>();
BanConfigurationParser config = this.getConfiguration(this.session.getCurrentProject());
for (ArtifactFilter afilter : config.getIncludeArtifacts()) {
if (afilter.getGroupId() == null) {
this.getLog().warn("The purge does not support wildcard group ID specifications, so skipping it: " + afilter);
continue;
}
Path groupPath = this.getGroupPath(afilter);
List<Path> artifactPaths = this.getArtifactPaths(groupPath, afilter);
for (Path artifactPath : artifactPaths)
includePaths.addAll(this.getVersionPaths(artifactPath, afilter.getVersionRange()));
}
this.getLog().debug("May be purging all files in " + includePaths.size() + " paths");
String regexDirectorySeparator = String.valueOf(File.separatorChar);
if (File.separatorChar == '\\')
regexDirectorySeparator += "\\";
for (ArtifactFilter afilter : config.getExcludeArtifacts()) {
StringBuilder regex = new StringBuilder();
if (afilter.getGroupId() != null) {
regex.append('^').append(this.getGroupPath(afilter));
} else if (afilter.getGroupIdRegex() != null) {
regex.append(afilter.getGroupIdRegex().replace("\\.", regexDirectorySeparator));
if (regex.charAt(0) != '^')
regex.insert(0, '^');
} else {
regex.append("^.+");
}
regex.append(regexDirectorySeparator);
if (afilter.getArtifactId() != null) {
regex.append(afilter.getArtifactId());
} else if (afilter.getArtifactIdRegex() != null) {
regex.append(afilter.getArtifactIdRegex());
} else {
regex.append("[^").append(regexDirectorySeparator).append("]+");
}
Pattern pattern = Pattern.compile(regex.toString());
Iterator<Path> i = includePaths.iterator();
while (i.hasNext()) {
Path path = i.next();
Matcher matcher = pattern.matcher(path.toString());
if (!matcher.find())
continue;
// group/artifact match; now for version
if (afilter.getVersionRange() == null) {
i.remove();
} else {
String version = path.getFileName().toString();
if (afilter.getVersionRange().containsVersion(new DefaultArtifactVersion(version)))
i.remove();
}
}
}
Path repoPath = this.getRepositoryPath();
if (this.dryRun) {
this.getLog().info("DRYRUN: Would have deleted certain paths from local Maven cache: " + repoPath);
this.getLog().info("DRYRUN: Would have deleted these paths: " + includePaths);
} else {
for (Path path : includePaths) {
Path fullpath = repoPath.resolve(path);
if (Files.exists(fullpath)) {
this.getLog().info("Deleting version from Maven cache: " + path);
Files.walkFileTree(fullpath, new DeleteDirectoryVisitor());
} else {
// this will probably never happen
this.getLog().debug("Maven cache does not exist: " + path);
}
}
}
}
private BanConfigurationParser getConfiguration(MavenProject project) throws MojoFailureException, IOException {
Plugin plugin = project.getPlugin(BanExtension.THIS_PLUGIN_KEY);
if (plugin == null)
throw new MojoFailureException("The plugin is executing but it cannot be found");
return new BanConfigurationParser(plugin);
}
private Path getGroupPath(ArtifactFilter afilter) {
String[] pathElements = afilter.getGroupId().split("\\.");
Path groupPath = Paths.get("");
for (String pathElement : pathElements)
groupPath = groupPath.resolve(pathElement);
return groupPath;
}
private List<Path> getArtifactPaths(Path groupPath, ArtifactFilter afilter) throws IOException {
if (afilter.getArtifactId() != null)
return Arrays.asList(groupPath.resolve(afilter.getArtifactId()));
Pattern artifactPattern = afilter.getArtifactIdRegex() == null ? null : Pattern.compile(afilter.getArtifactIdRegex());
Path repoPath = this.getRepositoryPath();
List<Path> paths = new LinkedList<>();
if (artifactPattern == null)
this.getLog().debug("All artifact directories in '" + groupPath + "' qualify as included");
Files.list(repoPath.resolve(groupPath)).forEach(new Consumer<Path>() {
@Override
public void accept(Path t) {
if (artifactPattern == null) {
paths.add(repoPath.relativize(t));
} else {
Matcher matcher = artifactPattern.matcher(t.getFileName().toString());
if (matcher.matches()) {
getLog().debug("The artifact directory '" + t.getFileName() + "' qualifies as included");
paths.add(repoPath.relativize(t));
}
}
}
});
return paths;
}
private List<Path> getVersionPaths(Path artifactPath, VersionRange versionRange) throws IOException {
Path repoPath = this.getRepositoryPath();
List<Path> paths = new LinkedList<>();
if (versionRange == null)
this.getLog().debug("All artifact version directories in '" + artifactPath + "' qualify as included");
Files.list(repoPath.resolve(artifactPath)).forEach(new Consumer<Path>() {
@Override
public void accept(Path t) {
if (versionRange == null) {
paths.add(repoPath.relativize(t));
} else {
ArtifactVersion artifactVersion = new DefaultArtifactVersion(t.getFileName().toString());
if (versionRange.containsVersion(artifactVersion)) {
getLog().debug("The artifact version directory '" + t.getFileName() + "' qualifies as included");
paths.add(repoPath.relativize(t));
}
}
}
});
return paths;
}
private Path getRepositoryPath() {
return new File(this.session.getLocalRepository().getBasedir()).toPath();
}
private class DeleteDirectoryVisitor implements FileVisitor<Path> {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
}
}