Skip to content

Commit

Permalink
Limit to only artifacts that have updates and are in the dependency g…
Browse files Browse the repository at this point in the history
…raph (#589)

Added `onlyUpgradable`, which will restrict the reports to only show upgradable artifacts, and `onlyProjectDependencies`, `onlyProjectPlugins`, which will restrict the graph of demendencyManagement/pluginManagement artifacts to the graph used in the project.

Additionally, some refactoring and improvements.
  • Loading branch information
jarmoniuk authored and slawekjaranowski committed Aug 20, 2022
1 parent fbe667f commit cd94151
Show file tree
Hide file tree
Showing 6 changed files with 679 additions and 173 deletions.
Expand Up @@ -57,30 +57,13 @@
public abstract class AbstractVersionsReport
extends AbstractMavenReport
{

/**
* Doxia Site Renderer component.
*
* @since 1.0-alpha-3
*/
@Component
private Renderer siteRenderer;

/**
* Internationalization component.
*
* @since 1.0-alpha-3
*/
@Component
private I18N i18n;

/**
* The Maven Project.
*
* @since 1.0-alpha-3
*/
@Parameter( defaultValue = "${project}", required = true, readonly = true )
private MavenProject project;
protected I18N i18n;

@Component
protected RepositorySystem repositorySystem;
Expand All @@ -91,16 +74,6 @@ public abstract class AbstractVersionsReport
@Component
private ArtifactResolver resolver;

/**
* The output directory for the report. Note that this parameter is only evaluated if the goal is run directly from
* the command line. If the goal is run indirectly as part of a site generation, the output directory configured in
* the Maven Site Plugin is used instead.
*
* @since 1.0-alpha-3
*/
@Parameter( defaultValue = "${project.reporting.outputDirectory}", required = true )
private File outputDirectory;

/**
* Skip entire check.
*
Expand Down
181 changes: 96 additions & 85 deletions src/main/java/org/codehaus/mojo/versions/DependencyUpdatesReport.java
Expand Up @@ -20,8 +20,6 @@
*/

import java.io.File;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
Expand All @@ -36,46 +34,64 @@
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.mojo.versions.api.ArtifactVersions;
import org.codehaus.mojo.versions.utils.DependencyComparator;
import org.codehaus.plexus.util.StringUtils;

import static java.util.Collections.EMPTY_MAP;
import static org.codehaus.mojo.versions.utils.MiscUtils.filter;

/**
* Generates a report of available updates for the dependencies of a project.
*
* @author Stephen Connolly
* @since 1.0-beta-1
*/
@Mojo( name = "dependency-updates-report", requiresProject = true, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
public class DependencyUpdatesReport
extends AbstractVersionsReport
@Mojo( name = "dependency-updates-report",
requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true )
public class DependencyUpdatesReport extends AbstractVersionsReport
{

/**
* Whether to process the <code>dependencyManagement</code> in pom or not.
*
*
* @since 2.5
*/
@Parameter( property = "processDependencyManagement", defaultValue = "true" )
private boolean processDependencyManagement;
protected boolean processDependencyManagement;

/**
* Whether to process the depdendencyManagement part transitive or not.
* In case of <code>&lt;type&gt;pom&lt;/type&gt;</code>and
* <code>&lt;scope&gt;import&lt;/scope&gt;</code> this means
* by default to report also the imported dependencies.
* by default to report also the imported dependencies.
* If processTransitive is set to <code>false</code> the report will only show
* updates of the imported pom it self.
*
* updates of the imported pom itself.
*
* @since 2.5 Note: Currently in experimental state.
*/
@Parameter( property = "processDependencyManagementTransitive", defaultValue = "true" )
private boolean processDependencyManagementTransitive;
protected boolean processDependencyManagementTransitive;

/**
* Report formats (html and/or xml). HTML by default.
*
*/
@Parameter( property = "dependencyUpdatesReportFormats", defaultValue = "html" )
private String[] formats = new String[] { "html" };
protected String[] formats = new String[] {"html"};

/**
* If <code>true</code>, only shows the subsection of the <code>dependencyManagement</code> artifacts that
* are actually used in the project's <code>dependency</code> graph. <code>false</code> by default.
*
* @since 2.12
*/
@Parameter( property = "onlyProjectDependencies", defaultValue = "false" )
protected boolean onlyProjectDependencies;

/**
* If <code>true</code>, only shows upgradable dependencies in the report. <code>false</code> by default.
*
* @since 2.12
*/
@Parameter( property = "onlyUpgradable", defaultValue = "false" )
protected boolean onlyUpgradable;

/**
* {@inheritDoc}
Expand All @@ -97,82 +113,101 @@ public boolean canGenerateReport()
* generates an empty report in case there are no sources to generate a report with
*
* @param locale the locale to generate the report for.
* @param sink the report formatting tool
* @param sink the report formatting tool
*/
protected void doGenerateReport( Locale locale, Sink sink )
throws MavenReportException
@SuppressWarnings( "deprecation" )
protected void doGenerateReport( Locale locale, Sink sink ) throws MavenReportException
{
Set<Dependency> dependencies = new TreeSet<>( new DependencyComparator() );
dependencies.addAll( getProject().getDependencies() );

Set<Dependency> dependencyManagement = new TreeSet<>( new DependencyComparator() );

if ( processDependencyManagementTransitive )
if ( processDependencyManagement )
{
if ( getProject().getDependencyManagement() != null
&& getProject().getDependencyManagement().getDependencies() != null )
if ( processDependencyManagementTransitive )
{
for ( Dependency dep : getProject().getDependencyManagement().getDependencies() )
if ( getProject().getDependencyManagement() != null
&& getProject().getDependencyManagement().getDependencies() != null )
{
getLog().debug( "Dpmg: " + dep.getGroupId() + ":" + dep.getArtifactId() + ":" + dep.getVersion()
+ ":" + dep.getType() + ":" + dep.getScope() );
for ( Dependency dep : getProject().getDependencyManagement().getDependencies() )
{
getLog().debug(
"Dpmg: " + dep.getGroupId() + ":" + dep.getArtifactId() + ":" + dep.getVersion() + ":"
+ dep.getType() + ":" + dep.getScope() );
}
dependencyManagement.addAll( getProject().getDependencyManagement().getDependencies() );
}
dependencyManagement.addAll( getProject().getDependencyManagement().getDependencies() );
}
}
else
{
if ( getProject().getOriginalModel().getDependencyManagement() != null
&& getProject().getOriginalModel().getDependencyManagement().getDependencies() != null )
else
{
// Using the original model to get the original dependencyManagement entries and
// not the interpolated model.
// TODO: I'm not 100% sure if this will work correctly in all cases.
for ( Dependency dep : getProject().getOriginalModel().getDependencyManagement().getDependencies() )
if ( getProject().getOriginalModel().getDependencyManagement() != null
&& getProject().getOriginalModel().getDependencyManagement().getDependencies() != null )
{
getLog().debug( "Original Dpmg: " + dep.getGroupId() + ":" + dep.getArtifactId() + ":"
+ dep.getVersion() + ":" + dep.getType() + ":" + dep.getScope() );
// Using the original model to get the original dependencyManagement entries and
// not the interpolated model.
// TODO: I'm not 100% sure if this will work correctly in all cases.
for ( Dependency dep : getProject().getOriginalModel().getDependencyManagement().getDependencies() )
{
getLog().debug( "Original Dpmg: " + dep.getGroupId() + ":" + dep.getArtifactId() + ":"
+ dep.getVersion() + ":" + dep.getType() + ":" + dep.getScope() );
}
dependencyManagement.addAll(
getProject().getOriginalModel().getDependencyManagement().getDependencies() );
}
dependencyManagement.addAll( getProject().getOriginalModel().getDependencyManagement().getDependencies() );
}
}

if ( processDependencyManagement )
{
dependencies = removeDependencyManagment( dependencies, dependencyManagement );
if ( !onlyProjectDependencies )
{
// Retains only dependencies not present in dependencyManagement
dependencies.removeIf( dep -> dependencyManagement.stream().anyMatch( dmDep -> match( dep, dmDep ) ) );
}
else
{
// Retain only dependencies in dependencyManagement that are also present in dependencies
dependencyManagement.removeIf( dep -> dependencies.stream().noneMatch( dmDep -> match( dep, dmDep ) ) );
}
}

try
{
Map<Dependency, ArtifactVersions> dependencyUpdates =
getHelper().lookupDependenciesUpdates( dependencies, false );
getHelper().lookupDependenciesUpdates( dependencies, false );

Map<Dependency, ArtifactVersions> dependencyManagementUpdates = Collections.emptyMap();
if ( processDependencyManagement )
Map<Dependency, ArtifactVersions> dependencyManagementUpdates =
processDependencyManagement ? getHelper().lookupDependenciesUpdates( dependencyManagement, false ) :
EMPTY_MAP;

if ( onlyUpgradable )
{
dependencyManagementUpdates = getHelper().lookupDependenciesUpdates( dependencyManagement, false );
dependencyUpdates = filter( dependencyUpdates, e -> e.getVersions().length > 1 );
dependencyManagementUpdates = filter( dependencyManagementUpdates, e -> e.getVersions().length > 1 );
}

for ( String format : formats )
{
if ( "html".equals( format ) )
{
DependencyUpdatesRenderer renderer =
new DependencyUpdatesRenderer( sink, getI18n(), getOutputName(), locale, dependencyUpdates,
dependencyManagementUpdates );
new DependencyUpdatesRenderer( sink, getI18n(), getOutputName(), locale, dependencyUpdates,
dependencyManagementUpdates );
renderer.render();

}
else if ( "xml".equals( format ) )
{
File outputDir = new File(getProject().getBuild().getDirectory());
if (!outputDir.exists())
File outputDir = new File( getProject().getBuild().getDirectory() );
if ( !outputDir.exists() )
{
outputDir.mkdirs();
if ( !outputDir.mkdirs() )
{
throw new MavenReportException( "Could not create output directory" );
}
}
String outputFile =
outputDir.getAbsolutePath() + File.separator + getOutputName() + ".xml";
String outputFile = outputDir.getAbsolutePath() + File.separator + getOutputName() + ".xml";
DependencyUpdatesXmlRenderer xmlGenerator =
new DependencyUpdatesXmlRenderer( dependencyUpdates, dependencyManagementUpdates, outputFile );
new DependencyUpdatesXmlRenderer( dependencyUpdates, dependencyManagementUpdates,
outputFile );
xmlGenerator.render();
}
}
Expand All @@ -184,42 +219,18 @@ else if ( "xml".equals( format ) )
}

/**
* Returns a set of dependencies where the dependencies which are defined in the dependency management section have
* been filtered out.
* Compares two dependencies with each other
*
* @param dependencies The set of dependencies.
* @param dependencyManagement The set of dependencies from the dependency management section.
* @return A new set of dependencies which are from the set of dependencies but not from the set of dependency
* management dependencies.
* @since 1.0-beta-1
* @return true if the two dependencies match
*/
private static Set<Dependency> removeDependencyManagment( Set<Dependency> dependencies, Set<Dependency> dependencyManagement )
private boolean match( Dependency dep, Dependency dmDep )
{
Set<Dependency> result = new TreeSet<>( new DependencyComparator() );
for ( Dependency c : dependencies )
{
boolean matched = false;
Iterator<Dependency> j = dependencyManagement.iterator();
while ( !matched && j.hasNext() )
{
Dependency t = j.next();
if ( StringUtils.equals( t.getGroupId(), c.getGroupId() )
&& StringUtils.equals( t.getArtifactId(), c.getArtifactId() )
&& ( t.getScope() == null || StringUtils.equals( t.getScope(), c.getScope() ) )
&& ( t.getClassifier() == null || StringUtils.equals( t.getClassifier(), c.getClassifier() ) )
&& ( c.getVersion() == null || t.getVersion() == null
|| StringUtils.equals( t.getVersion(), c.getVersion() ) ) )
{
matched = true;
break;
}
}
if ( !matched )
{
result.add( c );
}
}
return result;
return dmDep.getGroupId().equals( dep.getGroupId() )
&& dmDep.getArtifactId().equals( dep.getArtifactId() )
&& ( dmDep.getScope() == null || dmDep.getScope().equals( dep.getScope() ) )
&& ( dmDep.getClassifier() == null || dmDep.getClassifier().equals( dep.getClassifier() ) )
&& ( dep.getVersion() == null || dmDep.getVersion() == null || dmDep.getVersion()
.equals( dep.getVersion() ) );
}

/**
Expand Down

0 comments on commit cd94151

Please sign in to comment.