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

[#767] display update information for ranges #823

Merged
merged 1 commit into from Nov 26, 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
Expand Up @@ -20,11 +20,19 @@
*/

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.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.DefaultArtifactVersion;
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 All @@ -47,6 +55,10 @@ public abstract class AbstractVersionDetails
implements VersionDetails
{

private static final Pattern PREVIEW_PATTERN =
Pattern.compile( "(?i)(?:.*[-.](alpha|a|beta|b|milestone|m|preview|rc)"
+ "[-.]?(\\d{0,2}[a-z]?|\\d{6}\\.\\d{4})|\\d{8}(?:\\.?\\d{6})?)$" );

/**
* The current version. Guarded by {@link #currentVersionLock}.
*
Expand All @@ -61,6 +73,8 @@ public abstract class AbstractVersionDetails
*/
private boolean includeSnapshots = false;

protected boolean verboseDetail = true;

/**
* Not sure if we need to be thread safe, but there's no harm being careful, after all we could be invoked from an
* IDE.
Expand All @@ -77,6 +91,24 @@ protected AbstractVersionDetails()
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 );
}
}

final ArtifactVersion currentVersion = highestLowerBound;
ArtifactVersion nextVersion = scope
.filter( s -> s.isMajorTo( SUBINCREMENTAL ) )
.map( s -> (ArtifactVersion)
Expand Down Expand Up @@ -480,4 +512,87 @@ public boolean isVersionInRestriction( Restriction restriction, ArtifactVersion
}
return ( includeLower || lower != 0 ) && ( includeUpper || upper != 0 );
}

/**
* Returns the latest version newer than the specified current version, and within the specified update scope,
* or <code>null</code> if no such version exists.
* @param updateScope the scope of updates to include.
* @return the newest version after currentVersion within the specified update scope,
* or <code>null</code> if no version is available.
*/
public final ArtifactVersion getReportNewestUpdate( Optional<Segment> updateScope )
{
return getArtifactVersionStream( updateScope )
.min( Collections.reverseOrder( getVersionComparator() ) ).orElse( null );
}

/**
* Returns all versions newer than the specified current version, and within the specified update scope.
* @param updateScope the scope of updates to include.
* @return all versions after currentVersion within the specified update scope.
*/
public final ArtifactVersion[] getReportUpdates( Optional<Segment> updateScope )
{
TreeSet<ArtifactVersion> versions = getArtifactVersionStream( updateScope )
.collect( Collectors.toCollection( () -> new TreeSet<>( getVersionComparator() ) ) );
// filter out intermediate minor versions.
if ( !verboseDetail )
{
int major = 0;
int minor = 0;
boolean needOneMore = false;
for ( Iterator<ArtifactVersion> it = versions.descendingIterator(); it.hasNext(); )
{
ArtifactVersion version = it.next();
boolean isPreview = PREVIEW_PATTERN.matcher( version.toString() ).matches();

// encountered a version in same Major.Minor version, remove it.
if ( version.getMajorVersion() == major && version.getMinorVersion() == minor )
{
if ( needOneMore && !isPreview )
{
needOneMore = false;
continue;
}
it.remove();
continue;
}

// encountered a new Major.Minor version, keep it.
major = version.getMajorVersion();
minor = version.getMinorVersion();

// if version is a pre-release, also search for the last release.
needOneMore = isPreview;

}
}
return versions.toArray( new ArtifactVersion[0] );
}

/**
* Returns all versions newer than the specified current version, and within the specified update scope.
* @param updateScope the scope of updates to include.
* @return all versions after currentVersion within the specified update scope.
*/
private Stream<ArtifactVersion> getArtifactVersionStream( Optional<Segment> updateScope )
{
if ( isCurrentVersionDefined() )
{
try
{
Restriction restriction = restrictionFor( updateScope );

return Arrays.stream( getVersions() ).filter(
candidate -> ( isIncludeSnapshots() || !ArtifactUtils.isSnapshot( candidate.toString() ) )
&& isVersionInRestriction( restriction, candidate ) );
}
catch ( InvalidSegmentException ignored )
{
ignored.printStackTrace( System.err );
}
}
return Stream.empty();
}

}
@@ -0,0 +1,2 @@
invoker.goals.1=${project.groupId}:${project.artifactId}:${project.version}:property-updates-report
invoker.goals.2=${project.groupId}:${project.artifactId}:${project.version}:dependency-updates-report
@@ -0,0 +1,22 @@
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>localhost</groupId>
<artifactId>it-823-ranges-update-report</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>ranges-update-report</name>
<url>http://localhost/</url>

<properties>
<verion.dummy-api>[1.1.2,3.0]</verion.dummy-api>
</properties>
<dependencies>
<dependency>
<groupId>localhost</groupId>
<artifactId>dummy-api</artifactId>
<version>${verion.dummy-api}</version>
</dependency>
</dependencies>

</project>
@@ -0,0 +1,26 @@

propertyUpdatesReport = new File( basedir, "target/site/property-updates-report.html" ).text
.replaceAll( '<[^>]+>', ' ' )
.replaceAll( '&[^;]+;', ' ' )
.replaceAll( '\\s+', ' ' )

assert ! ( propertyUpdatesReport =~ /\b1\.1\.0-2\b/ )
assert ! ( propertyUpdatesReport =~ /\b1\.1\.1-2\b/ )
// Summary
assert propertyUpdatesReport =~ / \[1\.1\.2,3\.0\] 1\.1\.3 1\.3 3/
// Detail
assert propertyUpdatesReport =~ /Newer versions 1\.1\.3 Latest Incremental/
assert propertyUpdatesReport =~ /\b1\.2\.2 1\.3 Latest Minor 2\.0 2\.1 3\.0\b/

dependencyUpdatesReport = new File( basedir, "target/site/dependency-updates-report.html" ).text
.replaceAll( '<[^>]+>', ' ' )
.replaceAll( '&[^;]+;', ' ' )
.replaceAll( '\\s+', ' ' )

assert ! ( dependencyUpdatesReport =~ /\b1\.1\.0-2\b/ )
assert ! ( dependencyUpdatesReport =~ /\b1\.1\.1-2\b/ )
// Summary
assert propertyUpdatesReport =~ / \[1\.1\.2,3\.0\] 1\.1\.3 1\.3 3/
// Detail
assert propertyUpdatesReport =~ /Newer versions 1\.1\.3 Latest Incremental/
assert propertyUpdatesReport =~ /\b1\.2\.2 1\.3 Latest Minor 2\.0 2\.1 3\.0\b/
@@ -0,0 +1,2 @@
invoker.goals.1=${project.groupId}:${project.artifactId}:${project.version}:property-updates-report
invoker.goals.2=${project.groupId}:${project.artifactId}:${project.version}:dependency-updates-report
@@ -0,0 +1,22 @@
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>localhost</groupId>
<artifactId>it-823-ranges-update-report</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>ranges-update-report</name>
<url>http://localhost/</url>

<properties>
<verion.dummy-api>[1.1.2,3.0)</verion.dummy-api>
</properties>
<dependencies>
<dependency>
<groupId>localhost</groupId>
<artifactId>dummy-api</artifactId>
<version>${verion.dummy-api}</version>
</dependency>
</dependencies>

</project>
@@ -0,0 +1,26 @@

propertyUpdatesReport = new File( basedir, "target/site/property-updates-report.html" ).text
.replaceAll( '<[^>]+>', ' ' )
.replaceAll( '&[^;]+;', ' ' )
.replaceAll( '\\s+', ' ' )

assert ! ( propertyUpdatesReport =~ /\b1\.1\.0-2\b/ )
assert ! ( propertyUpdatesReport =~ /\b1\.1\.1-2\b/ )
// Summary
assert propertyUpdatesReport =~ / \[1\.1\.2,3\.0\) 1\.1\.3 1\.3 3/
// Detail
assert propertyUpdatesReport =~ /Newer versions 1\.1\.3 Latest Incremental/
assert propertyUpdatesReport =~ /\b1\.2\.2 1\.3 Latest Minor 2\.0 2\.1 \* 3\.0\b/

dependencyUpdatesReport = new File( basedir, "target/site/dependency-updates-report.html" ).text
.replaceAll( '<[^>]+>', ' ' )
.replaceAll( '&[^;]+;', ' ' )
.replaceAll( '\\s+', ' ' )

assert ! ( dependencyUpdatesReport =~ /\b1\.1\.0-2\b/ )
assert ! ( dependencyUpdatesReport =~ /\b1\.1\.1-2\b/ )
// Summary
assert propertyUpdatesReport =~ / \[1\.1\.2,3\.0\) 1\.1\.3 1\.3 3/
// Detail
assert propertyUpdatesReport =~ /Newer versions 1\.1\.3 Latest Incremental/
assert propertyUpdatesReport =~ /\b1\.2\.2 1\.3 Latest Minor 2\.0 2\.1 \* 3\.0\b/
Expand Up @@ -62,11 +62,11 @@ public abstract class AbstractVersionsReportRenderer<T> extends VersionsReportRe
*/
protected T model;

protected ArtifactVersionsCache newestUpdateCache
= new ArtifactVersionsCache( AbstractVersionDetails::getNewestUpdate );
protected final ArtifactVersionsCache newestUpdateCache
= new ArtifactVersionsCache( AbstractVersionDetails::getReportNewestUpdate );

protected ArtifactVersionsCache allUpdatesCache
= new ArtifactVersionsCache( AbstractVersionDetails::getAllUpdates );
protected final ArtifactVersionsCache allUpdatesCache
= new ArtifactVersionsCache( AbstractVersionDetails::getReportUpdates );

protected final SinkEventAttributes headerAttributes
= new SinkEventAttributeSet( SinkEventAttributes.WIDTH, "30%" );
Expand Down Expand Up @@ -231,6 +231,7 @@ protected void renderSummaryTableHeader( boolean hasScope, boolean hasType )

protected void renderSummaryTableRow( Dependency artifact, ArtifactVersions details, boolean includeScope )
{
details.setCurrentVersion( artifact.getVersion() );
ArtifactVersion[] allUpdates = allUpdatesCache.get( details, empty() );
boolean upToDate = allUpdates == null || allUpdates.length == 0;

Expand Down Expand Up @@ -263,7 +264,6 @@ protected void renderNewestVersions( AbstractVersionDetails details )
renderBoldCell( newestUpdateCache.get( details, of( MAJOR ) ) );
}

@SuppressWarnings( "checkstyle:MethodLength" )
protected void renderDependencyDetailTable( Dependency artifact, ArtifactVersions details, boolean includeScope )
{
ArtifactVersion[] allUpdates = allUpdatesCache.get( details, empty() );
Expand Down Expand Up @@ -363,6 +363,12 @@ else if ( newestUpdateCache.get( details, of( MAJOR ) ) != null )
}
}

/**
* Builds the list of restrictions for the given artifact or property, based on its version range.
* used to determine if a candidate version is outside the range, and if it should be displayed with a star.
* @param details the artifact or property for which to render the versions.
* @return the list of restrictions for the spec versions range.
*/
private List<Restriction> getArtifactVersionRange( AbstractVersionDetails details )
{
try
Expand All @@ -381,7 +387,7 @@ private List<Restriction> getArtifactVersionRange( AbstractVersionDetails detail
/**
* Renders the list of versions that are available for the given artifact or property.
* @param allUpdates the list of all updates available.
* @param details TODO.
* @param details the versions details for the given artifact or property.
*/
protected void renderVersions( ArtifactVersion[] allUpdates, AbstractVersionDetails details )
{
Expand Down
Expand Up @@ -145,18 +145,22 @@ protected void renderSummaryTableRow( Dependency artifact, PluginUpdatesDetails
boolean upToDate = !details.isUpdateAvailable();

sink.tableRow();
sink.tableCell();

sink.tableCell();
renderIcon( upToDate );
sink.tableCell_();

renderCells( artifact.getGroupId(), artifact.getArtifactId() );
renderBoldCell( upToDate, artifact.getVersion() );
renderNewestVersions( details );

sink.tableCell();
renderIcon( !details.isDependencyUpdateAvailable() );
sink.tableCell_();

sink.tableRow_();
}

@SuppressWarnings( "checkstyle:MethodLength" )
private void renderPluginDetail( Dependency artifact, PluginUpdatesDetails details )
{
sink.section2();
Expand Down
Expand Up @@ -119,7 +119,6 @@ private void renderPropertySummaryTableRow( Property property, PropertyVersions
sink.tableRow_();
}

@SuppressWarnings( "checkstyle:MethodLength" )
protected void renderPropertyDetailTable( Property property, PropertyVersions details )
{
ArtifactVersion[] allUpdates = allUpdatesCache.get( details, empty() );
Expand Down