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

Limit to only artifacts that have updates and are in the dependency graph (#589) #640

Merged
merged 1 commit into from Aug 20, 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 @@ -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