Skip to content

Commit

Permalink
Resolves mojohaus#960: displayDependencyUpdates should display update…
Browse files Browse the repository at this point in the history
…s from lesser segments

Resolves mojohaus#299: allowAnyUpdates should be ignored with a warning message if any of: allowMajorUpdates, allowMinorUpdates, allowIncrementalUpdates is set to false
  • Loading branch information
andrzejj0 committed May 27, 2023
1 parent 0fe821e commit 950dd80
Show file tree
Hide file tree
Showing 35 changed files with 1,002 additions and 371 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,45 +58,92 @@ 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);
}

/**
* 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());
}

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);
@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);
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 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);
}

Expand All @@ -121,6 +162,16 @@ public final void setCurrentVersion(ArtifactVersion currentVersion) {
this.currentVersion = currentVersion;
}

@Override
public final VersionRange getCurrentVersionRange() {
return currentVersionRange;
}

@Override
public final void setCurrentVersionRange(VersionRange versionRange) {
currentVersionRange = versionRange;
}

@Override
public final void setCurrentVersion(String currentVersion) {
setCurrentVersion(currentVersion == null ? null : DefaultArtifactVersionCache.of(currentVersion));
Expand Down Expand Up @@ -216,8 +267,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 +277,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,29 +314,21 @@ 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) {
public final ArtifactVersion getNewestUpdateWithinSegment(Optional<Segment> updateScope, boolean includeSnapshots) {
if (isCurrentVersionDefined()) {
return getNewestUpdate(getCurrentVersion(), updateScope, includeSnapshots);
return getNewestUpdateWithinSegment(getCurrentVersion(), updateScope, includeSnapshots);
}
return null;
}
Expand Down Expand Up @@ -434,14 +476,10 @@ public final ArtifactVersion[] getReportUpdates(Optional<Segment> updateScope, b
*/
private Stream<ArtifactVersion> getArtifactVersionStream(Optional<Segment> updateScope, boolean includeSnapshots) {
if (isCurrentVersionDefined()) {
try {
Restriction restriction = restrictionFor(updateScope);
Restriction restriction = restrictionForSelectedSegment(getCurrentVersion(), updateScope);

return Arrays.stream(getVersions(includeSnapshots))
.filter(candidate -> isVersionInRestriction(restriction, candidate));
} catch (InvalidSegmentException ignored) {
ignored.printStackTrace(System.err);
}
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
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,7 @@ private static void addPluginAssociations(
}
// might as well capture the current value
String evaluatedVersion = (String) expressionEvaluator.evaluate(plugin.getVersion());
property.addAssociation(
property.withAssociation(
helper.createPluginArtifact(groupId, artifactId, evaluatedVersion), true);
if (!propertyRef.equals(version)) {
addBounds(property, version, propertyRef);
Expand Down Expand Up @@ -1066,7 +1066,7 @@ private static void addReportPluginAssociations(
}
// might as well capture the current value
String versionEvaluated = (String) expressionEvaluator.evaluate(plugin.getVersion());
property.addAssociation(
property.withAssociation(
helper.createPluginArtifact(groupId, artifactId, versionEvaluated), true);
if (!propertyRef.equals(version)) {
addBounds(property, version, propertyRef);
Expand Down Expand Up @@ -1111,7 +1111,7 @@ private static void addDependencyAssocations(
}
// might as well capture the current value
String versionEvaluated = (String) expressionEvaluator.evaluate(dependency.getVersion());
property.addAssociation(
property.withAssociation(
helper.createDependencyArtifact(
groupId,
artifactId,
Expand All @@ -1138,15 +1138,15 @@ private static void addBounds(PropertyVersionsBuilder builder, String rawVersion
boolean includeLower = "[".equals(m.group(1));
String lowerLimit = m.group(2);
if (StringUtils.isNotEmpty(lowerLimit)) {
builder.addLowerBound(lowerLimit, includeLower);
builder.withLowerBound(lowerLimit, includeLower);
}
}
m = upperBound.matcher(rawVersionRange);
if (m.find()) {
boolean includeUpper = "[".equals(m.group(3));
String upperLimit = m.group(2);
if (StringUtils.isNotEmpty(upperLimit)) {
builder.addUpperBound(upperLimit, includeUpper);
builder.withUpperBound(upperLimit, includeUpper);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ public ArtifactVersion getNewestVersion(
? null
: upperBoundSegment
.map(s -> (ArtifactVersion) new BoundArtifactVersion(
currentVersion, s.isMajorTo(SUBINCREMENTAL) ? Segment.of(s.value() + 1) : s))
currentVersion, s.isMajorTo(SUBINCREMENTAL) ? Segment.minorTo(s) : s))
.orElse(null);
if (helper.getLog().isDebugEnabled()) {
helper.getLog().debug("Property ${" + property.getName() + "}: upperBound is: " + upperBound);
Expand Down
Loading

0 comments on commit 950dd80

Please sign in to comment.