Skip to content
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

Resolves #387: Provide an enforcer rule to specify the maximum number of allowed dependency updates #801

Merged
merged 1 commit into from Nov 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions pom.xml
Expand Up @@ -100,6 +100,8 @@
<module>model-ruleset</module>
<module>versions-api</module>
<module>versions-common</module>
<module>versions-enforcer</module>
<module>versions-test</module>
<module>versions-maven-plugin</module>
</modules>

Expand Down Expand Up @@ -139,6 +141,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 @@ -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 @@ -858,24 +858,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 @@ -414,14 +414,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
jarmoniuk marked this conversation as resolved.
Show resolved Hide resolved
)
{
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
Expand Up @@ -20,14 +20,8 @@
*/

import java.util.Comparator;
import java.util.Optional;

import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.Restriction;
import org.codehaus.mojo.versions.api.Segment;

import static org.codehaus.mojo.versions.api.Segment.MAJOR;
import static org.codehaus.mojo.versions.api.Segment.SUBINCREMENTAL;

/**
* A rule for comparing and manipulating versions.
Expand All @@ -43,31 +37,4 @@ public interface VersionComparator
* @since 1.0-beta-1
*/
int getSegmentCount( ArtifactVersion artifactVersion );

/**
* <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 currentVersion The current version.
* @param scope most major segment where updates are allowed Optional.empty() for no restriction
* @return {@linkplain Restriction} object based on the arguments
*/
default Restriction restrictionFor( ArtifactVersion currentVersion, 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 );
}
}