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: display-dependency-updates only shows updates from the most major allowed segment #966

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,13 @@
* under the License.
*/

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeSet;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.Restriction;
import org.apache.maven.artifact.versioning.VersionRange;
import org.codehaus.mojo.versions.ordering.BoundArtifactVersion;
Expand Down Expand Up @@ -64,51 +58,93 @@ public abstract class AbstractVersionDetails implements VersionDetails {
*/
private ArtifactVersion currentVersion = null;

private VersionRange currentVersionRange = null;

protected boolean verboseDetail = true;

protected AbstractVersionDetails() {}

@Override
public Restriction restrictionFor(Optional<Segment> scope) throws InvalidSegmentException {
// one range spec can have multiple restrictions, and multiple 'lower bound', we want the highest one.
// [1.0,2.0),[3.0,4.0) -> 3.0
ArtifactVersion highestLowerBound = currentVersion;
if (currentVersion != null) {
try {
highestLowerBound =
VersionRange.createFromVersionSpec(currentVersion.toString()).getRestrictions().stream()
.map(Restriction::getLowerBound)
.filter(Objects::nonNull)
.max(getVersionComparator())
.orElse(currentVersion);
} catch (InvalidVersionSpecificationException ignored) {
ignored.printStackTrace(System.err);
}
}
/**
* If a version is a version range consisting of one or more version ranges, returns the highest <u>lower</u>
* bound. If a single version range is present, returns its value.
* @param lowerBoundVersion actual version used
* @return highest lower bound of the given version range or {@link #getCurrentVersion()} if there's no lower bound
*/
protected ArtifactVersion getHighestLowerBound(ArtifactVersion lowerBoundVersion) {
return getCurrentVersionRange().getRestrictions().stream()
.map(Restriction::getLowerBound)
.filter(Objects::nonNull)
.max(getVersionComparator())
.orElse(lowerBoundVersion);
}

final ArtifactVersion currentVersion = highestLowerBound;
ArtifactVersion nextVersion = scope.filter(s -> s.isMajorTo(SUBINCREMENTAL))
.map(s -> (ArtifactVersion) new BoundArtifactVersion(currentVersion, Segment.of(s.value() + 1)))
.orElse(currentVersion);
/**
* If the artifact is bound by one or more version ranges, returns the restriction that constitutes
* the version range containing the selected actual version.
* If there are no version ranges, returns the provided version.
* @param selectedVersion actual version used
* @return restriction containing the version range selected by the given version,
* or {@link Optional#empty()} if there are no ranges
*/
protected Optional<Restriction> getSelectedRestriction(ArtifactVersion selectedVersion) {
return Optional.ofNullable(getCurrentVersionRange())
.map(VersionRange::getRestrictions)
.flatMap(r -> r.stream()
.filter(rr -> rr.containsVersion(selectedVersion))
.findAny());
}

@Override
public Restriction restrictionForSelectedSegment(ArtifactVersion lowerBound, Optional<Segment> selectedSegment) {
ArtifactVersion highestLowerBound = getHighestLowerBound(lowerBound);
ArtifactVersion nextVersion = selectedSegment
.filter(s -> s.isMajorTo(SUBINCREMENTAL))
.map(Segment::minorTo)
.map(s -> (ArtifactVersion) new BoundArtifactVersion(highestLowerBound, s))
.orElse(highestLowerBound);
return new Restriction(
nextVersion,
false,
scope.filter(MAJOR::isMajorTo)
.map(s -> (ArtifactVersion) new BoundArtifactVersion(currentVersion, s))
selectedSegment
.filter(MAJOR::isMajorTo)
.map(s -> (ArtifactVersion) new BoundArtifactVersion(highestLowerBound, 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);
public Restriction restrictionForUnchangedSegment(
ArtifactVersion actualVersion, Optional<Segment> unchangedSegment, boolean allowDowngrade)
throws InvalidSegmentException {
Optional<Restriction> selectedRestriction = getSelectedRestriction(actualVersion);
ArtifactVersion selectedRestrictionUpperBound =
selectedRestriction.map(Restriction::getUpperBound).orElse(actualVersion);
ArtifactVersion lowerBound = allowDowngrade
? getLowerBound(selectedRestrictionUpperBound, unchangedSegment)
.map(DefaultArtifactVersionCache::of)
.orElse(null)
: selectedRestrictionUpperBound;
ArtifactVersion upperBound = unchangedSegment
.map(s -> (ArtifactVersion) new BoundArtifactVersion(
selectedRestrictionUpperBound, s.isMajorTo(SUBINCREMENTAL) ? Segment.minorTo(s) : s))
.orElse(null);
return new Restriction(
lowerBound,
allowDowngrade
|| selectedRestriction
.map(Restriction::isUpperBoundInclusive)
.map(b -> !b)
.orElse(false),
upperBound,
allowDowngrade);
}

@Override
public final boolean isCurrentVersionDefined() {
return getCurrentVersion() != null;
public Restriction restrictionForIgnoreScope(ArtifactVersion lowerBound, Optional<Segment> ignored) {
ArtifactVersion highestLowerBound = getHighestLowerBound(lowerBound);
ArtifactVersion nextVersion = ignored.map(s -> (ArtifactVersion) new BoundArtifactVersion(highestLowerBound, s))
.orElse(highestLowerBound);
return new Restriction(nextVersion, false, null, false);
}

@Override
Expand All @@ -122,27 +158,23 @@ public final void setCurrentVersion(ArtifactVersion currentVersion) {
}

@Override
public final void setCurrentVersion(String currentVersion) {
setCurrentVersion(currentVersion == null ? null : DefaultArtifactVersionCache.of(currentVersion));
public final VersionRange getCurrentVersionRange() {
return currentVersionRange;
}

@Override
public final ArtifactVersion[] getVersions(VersionRange versionRange, boolean includeSnapshots) {
return getVersions(versionRange, null, includeSnapshots);
public final void setCurrentVersionRange(VersionRange versionRange) {
currentVersionRange = versionRange;
}

@Override
public final ArtifactVersion[] getVersions(
ArtifactVersion lowerBound, ArtifactVersion upperBound, boolean includeSnapshots) {
Restriction restriction = new Restriction(lowerBound, false, upperBound, false);
return getVersions(restriction, includeSnapshots);
public final void setCurrentVersion(String currentVersion) {
setCurrentVersion(currentVersion == null ? null : DefaultArtifactVersionCache.of(currentVersion));
}

@Override
public final ArtifactVersion getNewestVersion(
ArtifactVersion lowerBound, ArtifactVersion upperBound, boolean includeSnapshots) {
Restriction restriction = new Restriction(lowerBound, false, upperBound, false);
return getNewestVersion(restriction, includeSnapshots);
public final ArtifactVersion[] getVersions(VersionRange versionRange, boolean includeSnapshots) {
return getVersions(versionRange, null, includeSnapshots);
}

@Override
Expand Down Expand Up @@ -187,24 +219,6 @@ public final boolean containsVersion(String version) {
return false;
}

private ArtifactVersion[] getNewerVersions(ArtifactVersion version, boolean includeSnapshots) {
Restriction restriction = new Restriction(version, false, null, false);
return getVersions(restriction, includeSnapshots);
}

@Override
public final ArtifactVersion[] getNewerVersions(String version, boolean includeSnapshots) {
return getNewerVersions(DefaultArtifactVersionCache.of(version), includeSnapshots);
}

@Deprecated
@Override
public final ArtifactVersion[] getNewerVersions(
String version, Optional<Segment> upperBoundSegment, boolean includeSnapshots)
throws InvalidSegmentException {
return getNewerVersions(version, upperBoundSegment, includeSnapshots, false);
}

@Override
public final ArtifactVersion[] getNewerVersions(
String versionString, Optional<Segment> unchangedSegment, boolean includeSnapshots, boolean allowDowngrade)
Expand All @@ -216,8 +230,8 @@ public final ArtifactVersion[] getNewerVersions(
.orElse(null)
: currentVersion;
ArtifactVersion upperBound = unchangedSegment
.map(s -> (ArtifactVersion) new BoundArtifactVersion(
currentVersion, s.isMajorTo(SUBINCREMENTAL) ? Segment.of(s.value() + 1) : s))
.map(s -> (ArtifactVersion)
new BoundArtifactVersion(currentVersion, s.isMajorTo(SUBINCREMENTAL) ? Segment.minorTo(s) : s))
.orElse(null);

Restriction restriction = new Restriction(lowerBound, allowDowngrade, upperBound, allowDowngrade);
Expand All @@ -226,22 +240,21 @@ public final ArtifactVersion[] getNewerVersions(

@Override
public Optional<ArtifactVersion> getNewestVersion(
String versionString, Optional<Segment> upperBoundSegment, boolean includeSnapshots, boolean allowDowngrade)
String actualVersion, Optional<Segment> unchangedSegment, boolean includeSnapshots, boolean allowDowngrade)
throws InvalidSegmentException {
ArtifactVersion currentVersion = DefaultArtifactVersionCache.of(versionString);
ArtifactVersion lowerBound = allowDowngrade
? getLowerBound(currentVersion, upperBoundSegment)
.map(DefaultArtifactVersionCache::of)
.orElse(null)
: currentVersion;
ArtifactVersion upperBound = upperBoundSegment
.map(s -> (ArtifactVersion) new BoundArtifactVersion(
currentVersion, s.isMajorTo(SUBINCREMENTAL) ? Segment.of(s.value() + 1) : s))
.orElse(null);

Restriction restriction = new Restriction(lowerBound, allowDowngrade, upperBound, allowDowngrade);
Restriction segmentRestriction = restrictionForUnchangedSegment(
DefaultArtifactVersionCache.of(actualVersion), unchangedSegment, allowDowngrade);
Restriction lookupRestriction;
if (!allowDowngrade
&& Optional.ofNullable(currentVersion)
.map(v -> v.compareTo(segmentRestriction.getLowerBound()) > 0)
.orElse(false)) {
lookupRestriction = new Restriction(currentVersion, false, null, false);
} else {
lookupRestriction = segmentRestriction;
}
return Arrays.stream(getVersions(includeSnapshots))
.filter(candidate -> isVersionInRestriction(restriction, candidate))
.filter(candidate -> isVersionInRestriction(lookupRestriction, candidate))
.filter(candidate -> includeSnapshots || !ArtifactUtils.isSnapshot(candidate.toString()))
.max(getVersionComparator());
}
Expand All @@ -264,36 +277,28 @@ public final ArtifactVersion[] getVersions(
}

@Override
public final ArtifactVersion getNewestUpdate(
public final ArtifactVersion getNewestUpdateWithinSegment(
ArtifactVersion currentVersion, Optional<Segment> updateScope, boolean includeSnapshots) {
try {
return getNewestVersion(restrictionFor(updateScope), includeSnapshots);
} catch (InvalidSegmentException e) {
return null;
}
return getNewestVersion(restrictionForSelectedSegment(currentVersion, updateScope), includeSnapshots);
}

@Override
public final ArtifactVersion[] getAllUpdates(
ArtifactVersion currentVersion, Optional<Segment> updateScope, boolean includeSnapshots) {
try {
return getVersions(restrictionFor(updateScope), includeSnapshots);
} catch (InvalidSegmentException e) {
return null;
}
return getVersions(restrictionForSelectedSegment(currentVersion, updateScope), includeSnapshots);
}

@Override
public final ArtifactVersion getNewestUpdate(Optional<Segment> updateScope, boolean includeSnapshots) {
if (isCurrentVersionDefined()) {
return getNewestUpdate(getCurrentVersion(), updateScope, includeSnapshots);
public final ArtifactVersion getNewestUpdateWithinSegment(Optional<Segment> updateScope, boolean includeSnapshots) {
if (getCurrentVersion() != null) {
return getNewestUpdateWithinSegment(getCurrentVersion(), updateScope, includeSnapshots);
}
return null;
}

@Override
public final ArtifactVersion[] getAllUpdates(Optional<Segment> updateScope, boolean includeSnapshots) {
if (isCurrentVersionDefined()) {
if (getCurrentVersion() != null) {
return getAllUpdates(getCurrentVersion(), updateScope, includeSnapshots);
}
return null;
Expand Down Expand Up @@ -433,15 +438,11 @@ public final ArtifactVersion[] getReportUpdates(Optional<Segment> updateScope, b
* @return all versions after currentVersion within the specified update scope.
*/
private Stream<ArtifactVersion> getArtifactVersionStream(Optional<Segment> updateScope, boolean includeSnapshots) {
if (isCurrentVersionDefined()) {
try {
Restriction restriction = restrictionFor(updateScope);

return Arrays.stream(getVersions(includeSnapshots))
.filter(candidate -> isVersionInRestriction(restriction, candidate));
} catch (InvalidSegmentException ignored) {
ignored.printStackTrace(System.err);
}
if (getCurrentVersion() != null) {
Restriction restriction = restrictionForSelectedSegment(getCurrentVersion(), updateScope);

return Arrays.stream(getVersions(includeSnapshots))
.filter(candidate -> isVersionInRestriction(restriction, candidate));
}
return Stream.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,8 @@ public ArtifactVersions(Artifact artifact, List<ArtifactVersion> versions, Versi
this.versionComparator = versionComparator;
this.versions = new TreeSet<>(versionComparator);
this.versions.addAll(versions);
// DefaultArtifact objects are often built from raw model, without a version set
// (where the actual version is taken from parent or dependency/plugin management)
// this probably isn't the case in an actual Maven execution
if (artifact.getVersion() != null) {
setCurrentVersion(artifact.getVersion());
}
setCurrentVersion(artifact.getVersion());
setCurrentVersionRange(artifact.getVersionRange());
}

/**
Expand All @@ -94,6 +90,7 @@ public ArtifactVersions(ArtifactVersions other) {
versionComparator = other.versionComparator;
versions = other.versions;
setCurrentVersion(other.getCurrentVersion());
setCurrentVersionRange(other.getCurrentVersionRange());
}

@SuppressWarnings("checkstyle:InnerAssignment")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import org.apache.commons.lang3.tuple.Triple;

/**
* Utility providing a cached {@link ArtifactVersions#getNewestUpdate(Optional, boolean)} API
* Utility providing a cached {@link ArtifactVersions#getNewestUpdateWithinSegment(Optional, boolean)} API
*/
public class ArtifactVersionsCache {
private TriFunction<AbstractVersionDetails, Optional<Segment>, Boolean, ?> cachedFunction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.Restriction;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.execution.MavenSession;
Expand Down Expand Up @@ -688,11 +689,10 @@ public Map<Property, PropertyVersions> getVersionPropertiesMap(VersionProperties
if (dependencies != null) {
for (Dependency dependency : dependencies) {
getLog().debug("Property ${" + property.getName() + "}: Adding association to " + dependency);
builder.addAssociation(this.createDependencyArtifact(dependency), false);
builder.withAssociation(this.createDependencyArtifact(dependency), false);
}
}
try {
final PropertyVersions versions = builder.newPropertyVersions();
if (property.isAutoLinkDependencies()
&& StringUtils.isEmpty(property.getVersion())
&& !StringUtils.isEmpty(builder.getVersionRange())) {
Expand All @@ -702,8 +702,17 @@ public Map<Property, PropertyVersions> getVersionPropertiesMap(VersionProperties
}
final String currentVersion =
request.getMavenProject().getProperties().getProperty(property.getName());
versions.setCurrentVersion(currentVersion);
property.setValue(currentVersion);
final PropertyVersions versions;
try {
if (currentVersion != null) {
builder.withCurrentVersion(DefaultArtifactVersionCache.of(currentVersion))
.withCurrentVersionRange(VersionRange.createFromVersionSpec(currentVersion));
}
} catch (InvalidVersionSpecificationException e) {
throw new RuntimeException(e);
}
versions = builder.build();
propertyVersions.put(property, versions);
} catch (VersionRetrievalException e) {
throw new MojoExecutionException(e.getMessage(), e);
Expand Down
Loading