IVY-1662: collect updates by conf and check transitive on old dependency

This commit is contained in:
Eric Milles 2026-07-01 09:16:49 -05:00
parent 489da1e2fc
commit 6edbe1907f
Failed to extract signature
2 changed files with 45 additions and 31 deletions

View File

@ -18,13 +18,16 @@
package org.apache.ivy.ant; package org.apache.ivy.ant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import org.apache.ivy.core.module.descriptor.Configuration; import org.apache.ivy.core.module.descriptor.Configuration;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor; import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor; import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor; import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId; import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.report.ConfigurationResolveReport;
import org.apache.ivy.core.report.ResolveReport; import org.apache.ivy.core.report.ResolveReport;
import org.apache.ivy.core.resolve.IvyNode; import org.apache.ivy.core.resolve.IvyNode;
import org.apache.ivy.core.resolve.ResolveOptions; import org.apache.ivy.core.resolve.ResolveOptions;
@ -127,34 +130,43 @@ public class IvyDependencyUpdateChecker extends IvyPostResolveTask {
} }
private void displayDependencyUpdates(ResolveReport originalReport, ResolveReport latestReport) { private void displayDependencyUpdates(ResolveReport originalReport, ResolveReport latestReport) {
log("Dependencies updates available :"); Set<String> updates = new LinkedHashSet<>();
boolean dependencyUpdateDetected = false;
for (IvyNode latest : latestReport.getDependencies()) { for (String conf : latestReport.getConfigurations()) {
for (IvyNode originalDependency : originalReport.getDependencies()) { ConfigurationResolveReport newReport = latestReport.getConfigurationReport(conf);
if (latest.getModuleId().equals(originalDependency.getModuleId())) { ConfigurationResolveReport oldReport = originalReport.getConfigurationReport(conf);
ArtifactInfo in1 = toArtifactInfo(latest);
ArtifactInfo in2 = toArtifactInfo(originalDependency); // NOTE: getModuleRevisionIds() filters evicted and problem deps
ArtifactInfo out = getLatestStrategy(originalDependency).findLatest(new ArtifactInfo[]{in1, in2}, null); for (ModuleRevisionId latest : newReport.getModuleRevisionIds()) {
if (out == in1) { Iterable<IvyNode> iter = oldReport.getNodes(latest.getModuleId());
// is this dependency a transitive or a direct dependency? if (iter == null) {
// (unfortunately .isTransitive() methods do not have the same meaning) continue;
boolean isTransitiveDependency = latest.getDependencyDescriptor(latest }
.getRoot()) == null; for (IvyNode node : iter) {
if (!isTransitiveDependency || showTransitive) { ArtifactInfo in1 = toArtifactInfo(latest.getRevision());
log(String.format("\t%s#%s%s\t%s -> %s", ArtifactInfo in2 = toArtifactInfo(node.getResolvedId().getRevision());
originalDependency.getResolvedId().getOrganisation(), ArtifactInfo out = getLatestStrategy(node).findLatest(new ArtifactInfo[]{in1, in2}, null);
originalDependency.getResolvedId().getName(),
isTransitiveDependency ? " (transitive)" : "", boolean revisionGT = (out == in1);
originalDependency.getResolvedId().getRevision(), boolean transitive = (node.getDependencyDescriptor(node.getRoot()) == null);
latest.getResolvedId().getRevision())); if (revisionGT && (!transitive || showTransitive)) {
dependencyUpdateDetected = true; String update = String.format("\t%s#%s%s\t%s -> %s",
} node.getResolvedId().getOrganisation(),
node.getResolvedId().getName(),
transitive ? " (transitive)" : "",
node.getResolvedId().getRevision(),
latest.getRevision());
updates.add(update);
} }
} }
} }
} }
if (!dependencyUpdateDetected) {
log("\tAll dependencies are up to date"); log("Dependencies updates available :");
if (updates.isEmpty()) {
log("All dependencies are up to date");
} else {
updates.forEach(this::log);
} }
} }
@ -211,15 +223,15 @@ public class IvyDependencyUpdateChecker extends IvyPostResolveTask {
return getSettings().getDefaultLatestStrategy(); return getSettings().getDefaultLatestStrategy();
} }
private static ArtifactInfo toArtifactInfo(IvyNode node) { private static ArtifactInfo toArtifactInfo(String revision) {
return new ArtifactInfo() { return new ArtifactInfo() {
@Override @Override
public String getRevision() { public String getRevision() {
return node.getResolvedId().getRevision(); return revision;
} }
@Override @Override
public long getLastModified() { public long getLastModified() {
return node.getLastModified(); return 0;
} }
}; };
} }

View File

@ -261,9 +261,11 @@ public class IvyDependencyUpdateCheckerTest extends AntTaskTestCase {
assertLogContaining("org1#mod1.2\t2.0 -> 2.2"); assertLogContaining("org1#mod1.2\t2.0 -> 2.2");
// ivy-extends-multiconf.xml declares org2:mod2.1:0.3 // ivy-extends-multiconf.xml declares org2:mod2.1:0.3
assertLogContaining("org2#mod2.1\t0.3 -> 0.7"); assertLogContaining("org2#mod2.1\t0.3 -> 0.7");
// org2:mod2.1:0.3 ivy.xml declares org1:mod1.1:1.0 // org2:mod2.1:0.3 ivy.xml declares org1:mod1.1:1.0 -- but showTransitives is false
assertLogContaining("org1#mod1.1\t1.0 -> 2.0"); assertLogNotContaining("org1#mod1.1\t1.0 -> 2.0");
// org1:mod1.1:2.0 ivy.xml declares org1:mod1.2:2.1 assertLogNotContaining("org1#mod1.1 (transitive)\t1.0 -> 2.0");
assertLogContaining("org1#mod1.2\t2.1 -> 2.2"); // org1:mod1.1:2.0 ivy.xml declares org1:mod1.2:2.1 -- it evicted direct dependency
assertLogNotContaining("org1#mod1.2\t2.1 -> 2.2");
assertLogNotContaining("org1#mod1.2 (transitive)\t2.1 -> 2.2");
} }
} }