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 (mojohaus#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 committed Aug 20, 2022
1 parent f69438e commit 74f5a83
Show file tree
Hide file tree
Showing 6 changed files with 634 additions and 172 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
178 changes: 94 additions & 84 deletions src/main/java/org/codehaus/mojo/versions/DependencyUpdatesReport.java
Expand Up @@ -20,13 +20,12 @@
*/

import java.io.File;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.model.Dependency;
Expand All @@ -36,46 +35,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.
*
*
* @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 +114,100 @@ 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
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,17 @@ 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 dependcies 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 StringUtils.equals( dmDep.getGroupId(), dep.getGroupId() ) && StringUtils.equals( dmDep.getArtifactId(),
dep.getArtifactId() ) && ( dmDep.getScope() == null || StringUtils.equals( dmDep.getScope(),
dep.getScope() ) ) && ( dmDep.getClassifier() == null || StringUtils.equals( dmDep.getClassifier(),
dep.getClassifier() ) ) && ( dep.getVersion() == null || dmDep.getVersion() == null
|| StringUtils.equals( dmDep.getVersion(), dep.getVersion() ) );
}

/**
Expand Down

0 comments on commit 74f5a83

Please sign in to comment.