
org.codehaus.mojo.versions.DisplayDependencyUpdatesMojo Maven / Gradle / Ivy
Show all versions of versions-maven-plugin Show documentation
package org.codehaus.mojo.versions;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.Restriction;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.wagon.Wagon;
import org.codehaus.mojo.versions.api.ArtifactVersions;
import org.codehaus.mojo.versions.api.Segment;
import org.codehaus.mojo.versions.api.VersionRetrievalException;
import org.codehaus.mojo.versions.api.recording.ChangeRecorder;
import org.codehaus.mojo.versions.filtering.WildcardMatcher;
import org.codehaus.mojo.versions.ordering.InvalidSegmentException;
import org.codehaus.mojo.versions.rewriting.MutableXMLStreamReader;
import org.codehaus.mojo.versions.utils.DependencyComparator;
import org.codehaus.mojo.versions.utils.SegmentUtils;
import org.eclipse.aether.RepositorySystem;
import static java.util.Collections.emptySet;
import static java.util.Optional.empty;
import static org.codehaus.mojo.versions.filtering.DependencyFilter.filterDependencies;
import static org.codehaus.mojo.versions.utils.MavenProjectUtils.extractDependenciesFromDependencyManagement;
import static org.codehaus.mojo.versions.utils.MavenProjectUtils.extractDependenciesFromPlugins;
import static org.codehaus.mojo.versions.utils.MavenProjectUtils.extractPluginDependenciesFromPluginsInPluginManagement;
/**
* Displays all dependencies that have newer versions available.
* It will also display dependencies which are used by a plugin or
* defined in the plugin within a pluginManagement.
*
* @author Stephen Connolly
* @since 1.0-alpha-1
*/
@Mojo(name = "display-dependency-updates", threadSafe = true)
public class DisplayDependencyUpdatesMojo extends AbstractVersionsDisplayMojo {
// ------------------------------ FIELDS ------------------------------
/**
* The width to pad info messages.
*
* @since 1.0-alpha-1
*/
private static final int INFO_PAD_SIZE = 72;
/**
* Whether to process the dependencyManagement section of the project.
*
* @since 1.2
*/
@Parameter(property = "processDependencyManagement", defaultValue = "true")
private boolean processDependencyManagement;
/**
* Whether to process the dependencyManagement part transitive or not.
* In case of <type>pom</type>
and
* <scope>import</scope>
this means
* by default to report also the imported dependencies.
* If processTransitive is set to false
the report will only show
* updates of the imported pom it self.
*
* @since 2.11
*/
@Parameter(property = "processDependencyManagementTransitive", defaultValue = "true")
private boolean processDependencyManagementTransitive;
/**
* Only take these artifacts into consideration.
*
* Comma-separated list of extended GAV patterns.
*
*
* Extended GAV: groupId:artifactId:version:type:classifier:scope
*
*
* The wildcard "*" can be used as the only, first, last or both characters in each token.
* The version token does support version ranges.
*
*
*
* Example: {@code "mygroup:artifact:*,*:*:*:*:*:compile"}
*
*
* @since 2.12.0
*/
@Parameter(property = "dependencyManagementIncludes", defaultValue = WildcardMatcher.WILDCARD)
private List dependencyManagementIncludes;
/**
* Exclude these artifacts from consideration.
*
* Comma-separated list of extended GAV patterns.
*
*
* Extended GAV: groupId:artifactId:version:type:classifier:scope
*
*
* The wildcard "*" can be used as the only, first, last or both characters in each token.
* The version token does support version ranges.
*
*
*
* Example: {@code "mygroup:artifact:*,*:*:*:*:*:provided,*:*:*:*:*:system"}
*
*
* @since 2.12.0
*/
@Parameter(property = "dependencyManagementExcludes")
private List dependencyManagementExcludes;
/**
* Whether to process the dependencies section of the project.
*
* @since 1.2
*/
@Parameter(property = "processDependencies", defaultValue = "true")
private boolean processDependencies;
/**
* Only take the specified input dependencies into account.
* Note: even if a version is specified, it will refer to the input dependency version.
* To filter output versions, please use {@link #ruleSet} or {@link #ignoredVersions}.
*
* Comma-separated list of extended GAV patterns.
*
*
* Extended GAV: groupId:artifactId:version:type:classifier:scope
*
*
* The wildcard "*" can be used as the only, first, last or both characters in each token.
* The version token does support version ranges.
*
*
*
* Example: {@code "mygroup:artifact:*,*:*:*:*:*:compile"}
*
*
* @since 2.12.0
*/
@Parameter(property = "dependencyIncludes", defaultValue = WildcardMatcher.WILDCARD)
private List dependencyIncludes;
/**
* Do not take the specified input dependencies into account.
* Note: even if a version is specified, it will refer to the input dependency version.
* To filter output versions, please use {@link #ruleSet} or {@link #ignoredVersions}.
*
* Comma-separated list of extended GAV patterns.
*
*
* Extended GAV: groupId:artifactId:version:type:classifier:scope
*
*
* The wildcaNote:rd "*" can be used as the only, first, last or both characters in each token.
* The version token does support version ranges.
*
*
*
* Example: {@code "mygroup:artifact:*,*:*:*:*:*:provided,*:*:*:*:*:system"}
*
*
* @since 2.12.0
*/
@Parameter(property = "dependencyExcludes")
private List dependencyExcludes;
/**
* Whether to process the dependencies sections of plugins.
*
* @since 2.5
*/
@Parameter(property = "processPluginDependencies", defaultValue = "true")
private boolean processPluginDependencies;
/**
* Whether to process the dependencies sections of plugins which are defined in pluginManagement.
*
* @since 2.5
*/
@Parameter(property = "processPluginDependenciesInPluginManagement", defaultValue = "true")
private boolean processPluginDependenciesInPluginManagement;
/**
* Whether to allow the major version number to be changed.
*
* @since 2.5
*/
@Parameter(property = "allowMajorUpdates", defaultValue = "true")
private boolean allowMajorUpdates = true;
/**
* Whether to allow the minor version number to be changed.
*
* Note: {@code false} also implies {@linkplain #allowMajorUpdates} to be {@code false}
*
* @since 2.5
*/
@Parameter(property = "allowMinorUpdates", defaultValue = "true")
private boolean allowMinorUpdates = true;
/**
* Whether to allow the incremental version number to be changed.
*
* Note: {@code false} also implies {@linkplain #allowMajorUpdates}
* and {@linkplain #allowMinorUpdates} to be {@code false}
*
* @since 2.5
*/
@Parameter(property = "allowIncrementalUpdates", defaultValue = "true")
private boolean allowIncrementalUpdates = true;
/**
* Whether to show additional information such as dependencies that do not need updating. Defaults to false.
*
* @since 2.1
*/
@Parameter(property = "verbose", defaultValue = "false")
private boolean verbose;
/**
* Only take these artifacts into consideration:
* Comma-separated list of {@code groupId:[artifactId[:version]]} patterns
*
*
* The wildcard "*" can be used as the only, first, last or both characters in each token.
* The version token does support version ranges.
*
*
*
* Example: {@code "mygroup:artifact:*,othergroup:*,anothergroup"}
*
*
* @since 2.12.0
*/
@Parameter(property = "pluginDependencyIncludes", defaultValue = WildcardMatcher.WILDCARD)
private List pluginDependencyIncludes;
/**
* Exclude these artifacts into consideration:
* Comma-separated list of {@code groupId:[artifactId[:version]]} patterns
*
*
* The wildcard "*" can be used as the only, first, last or both characters in each token.
* The version token does support version ranges.
*
*
*
* Example: {@code "mygroup:artifact:*,othergroup:*,anothergroup"}
*
*
* @since 2.12.0
*/
@Parameter(property = "pluginDependencyExcludes")
private List pluginDependencyExcludes;
/**
* Only take these artifacts into consideration:
* Comma-separated list of {@code groupId:[artifactId[:version]]} patterns
*
* The wildcard "*" can be used as the only, first, last or both characters in each token.
* The version token does support version ranges.
*
*
*
* Example: {@code "mygroup:artifact:*,othergroup:*,anothergroup"}
*
*
* @since 2.12.0
*/
@Parameter(property = "pluginManagementDependencyIncludes", defaultValue = WildcardMatcher.WILDCARD)
private List pluginManagementDependencyIncludes;
/**
* Exclude these artifacts into consideration:
* Comma-separated list of {@code groupId:[artifactId[:version]]} patterns
*
*
* The wildcard "*" can be used as the only, first, last or both characters in each token.
* The version token does support version ranges.
*
*
*
* Example: {@code "mygroup:artifact:*,othergroup:*,anothergroup"}
*
*
* @since 2.12.0
*/
@Parameter(property = "pluginManagementDependencyExcludes")
private List pluginManagementDependencyExcludes;
// --------------------- GETTER / SETTER METHODS ---------------------
@Inject
public DisplayDependencyUpdatesMojo(
ArtifactHandlerManager artifactHandlerManager,
RepositorySystem repositorySystem,
Map wagonMap,
Map changeRecorders) {
super(artifactHandlerManager, repositorySystem, wagonMap, changeRecorders);
}
// open for tests
protected static boolean dependenciesMatch(Dependency dependency, Dependency managedDependency) {
if (!managedDependency.getGroupId().equals(dependency.getGroupId())) {
return false;
}
if (!managedDependency.getArtifactId().equals(dependency.getArtifactId())) {
return false;
}
if (managedDependency.getScope() == null
|| Objects.equals(managedDependency.getScope(), dependency.getScope())) {
return false;
}
if (managedDependency.getClassifier() == null
|| Objects.equals(managedDependency.getClassifier(), dependency.getClassifier())) {
return false;
}
return dependency.getVersion() == null
|| managedDependency.getVersion() == null
|| Objects.equals(managedDependency.getVersion(), dependency.getVersion());
}
public boolean isProcessingDependencyManagement() {
return processDependencyManagement;
}
public boolean isProcessingDependencies() {
return processDependencies;
}
public boolean isProcessingPluginDependencies() {
return processPluginDependencies;
}
public boolean isProcessPluginDependenciesInDependencyManagement() {
return processPluginDependenciesInPluginManagement;
}
public boolean isVerbose() {
return verbose;
}
// ------------------------ INTERFACE METHODS ------------------------
// --------------------- Interface Mojo ---------------------
/**
* @throws org.apache.maven.plugin.MojoExecutionException when things go wrong
* @throws org.apache.maven.plugin.MojoFailureException when things go wrong in a very bad way
* @see org.codehaus.mojo.versions.AbstractVersionsUpdaterMojo#execute()
* @since 1.0-alpha-1
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
logInit();
validateInput();
Set dependencyManagement = emptySet();
try {
if (isProcessingDependencyManagement()) {
dependencyManagement = filterDependencies(
extractDependenciesFromDependencyManagement(
getProject(), processDependencyManagementTransitive, getLog()),
dependencyManagementIncludes,
dependencyManagementExcludes,
"Dependecy Management",
getLog());
logUpdates(
getHelper()
.lookupDependenciesUpdates(
dependencyManagement.stream().filter(d -> d.getVersion() != null),
false,
allowSnapshots),
"Dependency Management");
}
if (isProcessingDependencies()) {
Set finalDependencyManagement = dependencyManagement;
logUpdates(
getHelper()
.lookupDependenciesUpdates(
filterDependencies(
getProject().getDependencies().stream()
.filter(dep -> finalDependencyManagement.stream()
.noneMatch(depMan ->
dependenciesMatch(dep, depMan)))
.collect(
() -> new TreeSet<>(
DependencyComparator.INSTANCE),
Set::add,
Set::addAll),
dependencyIncludes,
dependencyExcludes,
"Dependencies",
getLog())
.stream()
.filter(d -> d.getVersion() != null),
false,
allowSnapshots),
"Dependencies");
}
if (isProcessPluginDependenciesInDependencyManagement()) {
logUpdates(
getHelper()
.lookupDependenciesUpdates(
filterDependencies(
extractPluginDependenciesFromPluginsInPluginManagement(
getProject()),
pluginManagementDependencyIncludes,
pluginManagementDependencyExcludes,
"Plugin Management Dependencies",
getLog())
.stream()
.filter(d -> d.getVersion() != null),
false,
allowSnapshots),
"pluginManagement of plugins");
}
if (isProcessingPluginDependencies()) {
logUpdates(
getHelper()
.lookupDependenciesUpdates(
filterDependencies(
extractDependenciesFromPlugins(getProject()),
pluginDependencyIncludes,
pluginDependencyExcludes,
"Plugin Dependencies",
getLog())
.stream()
.filter(d -> d.getVersion() != null),
false,
allowSnapshots),
"Plugin Dependencies");
}
} catch (VersionRetrievalException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
@Override
protected void validateInput() throws MojoExecutionException {
validateGAVList(dependencyIncludes, 6, "dependencyIncludes");
validateGAVList(dependencyExcludes, 6, "dependencyExcludes");
validateGAVList(dependencyManagementIncludes, 6, "dependencyManagementIncludes");
validateGAVList(dependencyManagementExcludes, 6, "dependencyManagementExcludes");
validateGAVList(pluginDependencyIncludes, 3, "pluginDependencyIncludes");
validateGAVList(pluginDependencyExcludes, 3, "pluginDependencyExcludes");
validateGAVList(pluginManagementDependencyIncludes, 3, "pluginManagementDependencyIncludes");
validateGAVList(pluginManagementDependencyExcludes, 3, "pluginManagementDependencyExcludes");
}
/**
* Validates a list of GAV strings
* @param gavList list of the input GAV strings
* @param numSections number of sections in the GAV to verify against
* @param argumentName argument name to indicate in the exception
* @throws MojoExecutionException if the argument is invalid
*/
static void validateGAVList(List gavList, int numSections, String argumentName)
throws MojoExecutionException {
if (gavList != null && gavList.stream().anyMatch(gav -> StringUtils.countMatches(gav, ":") >= numSections)) {
throw new MojoExecutionException(argumentName + " should not contain more than 6 segments");
}
}
private void logUpdates(Map updates, String section) {
List withUpdates = new ArrayList<>();
List usingCurrent = new ArrayList<>();
for (ArtifactVersions versions : updates.values()) {
String left = " " + ArtifactUtils.versionlessKey(versions.getArtifact()) + " ";
String currentVersion;
Optional latestVersion;
Optional unchangedSegment = SegmentUtils.determineUnchangedSegment(
allowMajorUpdates, allowMinorUpdates, allowIncrementalUpdates, getLog());
if (versions.getCurrentVersion() != null) {
currentVersion = versions.getCurrentVersion().toString();
try {
latestVersion = versions.getNewestVersion(currentVersion, unchangedSegment, allowSnapshots, false);
} catch (InvalidSegmentException e) {
latestVersion = empty();
}
} else {
currentVersion = versions.getArtifact().getVersionRange().toString();
ArtifactVersion actualVersion =
versions.getNewestVersion(versions.getArtifact().getVersionRange(), allowSnapshots);
Restriction newVersionRestriction;
try {
Restriction segmentRestriction =
versions.restrictionForUnchangedSegment(actualVersion, unchangedSegment, false);
newVersionRestriction = new Restriction(
actualVersion,
false,
segmentRestriction.getUpperBound(),
segmentRestriction.isUpperBoundInclusive());
} catch (InvalidSegmentException e) {
throw new RuntimeException(e);
}
latestVersion = Optional.of(newVersionRestriction)
.map(restriction -> versions.getNewestVersion(restriction, allowSnapshots));
}
String right =
" " + latestVersion.map(v -> currentVersion + " -> " + v).orElse(currentVersion);
List t = latestVersion.isPresent() ? withUpdates : usingCurrent;
if (right.length() + left.length() + 3 > INFO_PAD_SIZE + getOutputLineWidthOffset()) {
t.add(left + "...");
t.add(StringUtils.leftPad(right, INFO_PAD_SIZE + getOutputLineWidthOffset()));
} else {
t.add(StringUtils.rightPad(left, INFO_PAD_SIZE + getOutputLineWidthOffset() - right.length(), ".")
+ right);
}
}
if (isVerbose()) {
if (usingCurrent.isEmpty()) {
if (!withUpdates.isEmpty()) {
logLine(false, "No dependencies in " + section + " are using the newest version.");
logLine(false, "");
}
} else {
logLine(false, "The following dependencies in " + section + " are using the newest version:");
for (String s : usingCurrent) {
logLine(false, s);
}
logLine(false, "");
}
}
if (withUpdates.isEmpty()) {
if (!usingCurrent.isEmpty()) {
logLine(false, "No dependencies in " + section + " have newer versions.");
logLine(false, "");
}
} else {
logLine(false, "The following dependencies in " + section + " have newer versions:");
for (String withUpdate : withUpdates) {
logLine(false, withUpdate);
}
logLine(false, "");
}
}
/**
* @param pom the pom to update.
* @see org.codehaus.mojo.versions.AbstractVersionsUpdaterMojo#update(MutableXMLStreamReader)
* @since 1.0-alpha-1
*/
@Override
protected void update(MutableXMLStreamReader pom) {
// do nothing
}
}