Skip to content

Commit

Permalink
Resolves mojohaus#387: Provide an enforcer rule to specify a maximum …
Browse files Browse the repository at this point in the history
…number of allowed dependency updates
  • Loading branch information
jarmoniuk committed Nov 13, 2022
1 parent 540f3ae commit a553b84
Show file tree
Hide file tree
Showing 33 changed files with 1,711 additions and 301 deletions.
88 changes: 88 additions & 0 deletions enforcer/pom.xml
@@ -0,0 +1,88 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>versions</artifactId>
<groupId>org.codehaus.mojo.versions</groupId>
<version>2.14.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>enforcer</artifactId>

<name>Versions Enforcer rules</name>
<description>maven-enforcer-plugin rules</description>

<dependencies>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.codehaus.mojo.versions</groupId>
<artifactId>test-utils</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.apache.maven.enforcer</groupId>
<artifactId>enforcer-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-compat</artifactId>
<version>${mavenVersion}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.apache.maven.plugin-testing</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

</project>
12 changes: 12 additions & 0 deletions pom.xml
Expand Up @@ -100,6 +100,8 @@
<module>model-ruleset</module>
<module>model-report</module>
<module>versions-common</module>
<module>versions-enforcer</module>
<module>versions-test</module>
</modules>

<scm>
Expand Down Expand Up @@ -137,6 +139,11 @@

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.maven.enforcer</groupId>
<artifactId>enforcer-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
Expand Down Expand Up @@ -194,6 +201,11 @@
<artifactId>slf4j-simple</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.9</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
Expand Up @@ -34,6 +34,7 @@
import static java.util.Collections.reverseOrder;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static org.codehaus.mojo.versions.api.Segment.MAJOR;
import static org.codehaus.mojo.versions.api.Segment.SUBINCREMENTAL;

/**
Expand Down Expand Up @@ -72,6 +73,29 @@ protected AbstractVersionDetails()
{
}

@Override
public Restriction restrictionFor( Optional<Segment> scope )
throws InvalidSegmentException
{
ArtifactVersion nextVersion = scope
.filter( s -> s.isMajorTo( SUBINCREMENTAL ) )
.map( s -> (ArtifactVersion)
new BoundArtifactVersion( currentVersion, Segment.of( s.value() + 1 ) ) )
.orElse( currentVersion );
return new Restriction( nextVersion, false, scope.filter( MAJOR::isMajorTo )
.map( s -> (ArtifactVersion) new BoundArtifactVersion( currentVersion, s ) ).orElse( null ),
false );
}

@Override
public Restriction restrictionForIgnoreScope( Optional<Segment> ignored )
{
ArtifactVersion nextVersion = ignored
.map( s -> (ArtifactVersion) new BoundArtifactVersion( currentVersion, s ) )
.orElse( currentVersion );
return new Restriction( nextVersion, false, null, false );
}

@Override
public final boolean isCurrentVersionDefined()
{
Expand Down Expand Up @@ -307,7 +331,7 @@ public final ArtifactVersion getNewestUpdate( ArtifactVersion currentVersion, Op
{
try
{
return getNewestVersion( getVersionComparator().restrictionFor( currentVersion, updateScope ),
return getNewestVersion( restrictionFor( updateScope ),
includeSnapshots );
}
catch ( InvalidSegmentException e )
Expand All @@ -322,7 +346,7 @@ public final ArtifactVersion[] getAllUpdates( ArtifactVersion currentVersion, Op
{
try
{
return getVersions( getVersionComparator().restrictionFor( currentVersion, updateScope ),
return getVersions( restrictionFor( updateScope ),
includeSnapshots );
}
catch ( InvalidSegmentException e )
Expand Down
Expand Up @@ -893,24 +893,6 @@ else if ( !excludePropertiesList.isEmpty() && excludePropertiesList.contains( pr
return propertyVersions;
}

@Override
public Dependency interpolateVersion( final Dependency dependency, final MavenProject project )
{

// resolve version from model properties if necessary (e.g. "${mycomponent.myversion}"
if ( dependency.getVersion().startsWith( "${" ) )
{
final String resolvedVersion = project.getOriginalModel()
.getProperties().getProperty(
dependency.getVersion().substring( 2, dependency.getVersion().length() - 1 ) );
if ( resolvedVersion != null && !resolvedVersion.isEmpty() )
{
dependency.setVersion( resolvedVersion );
}
}
return dependency;
}

/**
* Builder class for {@linkplain DefaultVersionsHelper}
*/
Expand Down
Expand Up @@ -392,4 +392,28 @@ ArtifactVersion[] getAllUpdates( Optional<Segment> updateScope, boolean includeS
*/
ArtifactVersion[] getAllUpdates( VersionRange versionRange, boolean includeSnapshots );

/**
* <p>Returns a {@linkplain Restriction} object for computing version <em>upgrades</em>
* with the given segment allowing updates, with all more major segments locked in place.</p>
* <p>The resulting restriction could be thought of as one
* retaining the versions on positions up to the held position,
* the position right after the position held in place will be incremented by one,
* and on all positions which are more minor than that, the range would contain -&infin;
* for the bottom bound and +&infin; for the above bound.</p>
* <p>This will allow matching the required versions while not matching versions which are considered
* inferior than the zeroth version, i.e. versions with a qualifier.</p>
*
* @param scope most major segment where updates are allowed Optional.empty() for no restriction
* @return {@linkplain Restriction} object based on the arguments
*/
Restriction restrictionFor( Optional<Segment> scope ) throws InvalidSegmentException;


/**
* Returns the {@link Restriction} objects for a segemnt scope which is to be <b>ignored</b>.
*
* @param ignored most major segment where updates are to be ignored; Optional.empty() for no ignored segments
* @return {@linkplain Restriction} object based on the arguments
*/
Restriction restrictionForIgnoreScope( Optional<Segment> ignored );
}
Expand Up @@ -417,14 +417,4 @@ public VersionPropertiesMapRequest build()
*/
void resolveArtifact( Artifact artifact, boolean usePluginRepositories )
throws ArtifactResolutionException, ArtifactNotFoundException;

/**
* Attempts to interpolate the version from model properties.
*
* @param dependency the dependency
* @param project the maven project
* @return the dependency with interpolated property (as far as possible)
* @since 2.14.0
*/
Dependency interpolateVersion( Dependency dependency, MavenProject project );
}
Expand Up @@ -19,13 +19,15 @@
* under the License.
*/

import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.mojo.versions.utils.DependencyComparator;

public class DependencyFilter
Expand Down Expand Up @@ -62,12 +64,12 @@ public String toString()
return String.format( "%s{%s}", getClass().getSimpleName(), pattern );
}

public Set<Dependency> retainingIn( Set<Dependency> dependencies )
public Set<Dependency> retainingIn( Collection<Dependency> dependencies )
{
return filterBy( dependencies, this::matchersMatch );
}

public Set<Dependency> removingFrom( Set<Dependency> dependencies )
public Set<Dependency> removingFrom( Collection<Dependency> dependencies )
{
return filterBy( dependencies, not( this::matchersMatch ) );
}
Expand All @@ -77,10 +79,51 @@ private boolean matchersMatch( Dependency dependency )
return matchers.stream().anyMatch( m -> m.test( dependency ) );
}

private TreeSet<Dependency> filterBy( Set<Dependency> dependencies, Predicate<Dependency> predicate )
private TreeSet<Dependency> filterBy( Collection<Dependency> dependencies, Predicate<Dependency> predicate )
{
return dependencies.stream()
.filter( predicate )
.collect( Collectors.toCollection( () -> new TreeSet<>( DependencyComparator.INSTANCE ) ) );
}

/**
* Returns a set of dependencies filtered by the given include- and exclude filters.
* @param dependencies collection of dependencies to filter
* @param includes a list of dependency includes
* @param excludes a list of dependency excludes
* @param section if log is not null, dependency section name for the debug log
* @param log null or log to which debug information will be logged
* @return filtered set of dependencies
*/
public static Set<Dependency> filterDependencies(
Collection<Dependency> dependencies,
List<String> includes,
List<String> excludes,
String section,
Log log
)
{
DependencyFilter includeDeps = DependencyFilter.parseFrom( includes );
DependencyFilter excludeDeps = DependencyFilter.parseFrom( excludes );

Set<Dependency> filtered = includeDeps.retainingIn( dependencies );
filtered = excludeDeps.removingFrom( filtered );

if ( log != null && log.isDebugEnabled() )
{
log.debug( String.format( "parsed includes in %s: %s -> %s", section, includes, includeDeps ) );
log.debug( String.format( "parsed excludes in %s: %s -> %s", section, excludes, excludeDeps ) );
log.debug( String.format( "Unfiltered %s: ", section ) + output( dependencies ) );
log.debug( String.format( "Filtered %s: ", section ) + output( filtered ) );
}

return filtered;
}

private static String output( Collection<Dependency> dependencies )
{
return dependencies.stream()
.map( d -> String.format( "%s:%s:%s", d.getGroupId(), d.getArtifactId(), d.getVersion() ) )
.collect( Collectors.joining( ", " ) );
}
}
Expand Up @@ -29,14 +29,10 @@
public abstract class AbstractVersionComparator
implements VersionComparator
{
/**
* {@inheritDoc}
*/
@Override
public abstract int compare( ArtifactVersion o1, ArtifactVersion o2 );

/**
* {@inheritDoc}
*/
@Override
public final int getSegmentCount( ArtifactVersion v )
{
if ( v == null )
Expand All @@ -58,6 +54,7 @@ public final int getSegmentCount( ArtifactVersion v )
*
* @return the hash code.
*/
@Override
public int hashCode()
{
return getClass().hashCode();
Expand All @@ -71,6 +68,7 @@ public int hashCode()
* @see #hashCode()
* @see java.util.Hashtable
*/
@Override
public boolean equals( Object obj )
{
return obj == this || ( obj != null && getClass().equals( obj.getClass() ) );
Expand Down

0 comments on commit a553b84

Please sign in to comment.