Skip to content

Commit

Permalink
Resolves #960: displayDependencyUpdates should display updates from l…
Browse files Browse the repository at this point in the history
…esser segments

Resolves #299: allowAnyUpdates should be ignored with a warning message if any of: allowMajorUpdates, allowMinorUpdates, allowIncrementalUpdates is set to false
  • Loading branch information
jarmoniuk authored and slawekjaranowski committed Jun 1, 2023
1 parent 0fe821e commit 9b44a44
Show file tree
Hide file tree
Showing 37 changed files with 1,109 additions and 607 deletions.
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

0 comments on commit 9b44a44

Please sign in to comment.