From 6edbe1907fff72fbd2e5c7de135f1c97c0a8e50b Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Wed, 1 Jul 2026 09:16:49 -0500 Subject: [PATCH] IVY-1662: collect updates by conf and check transitive on old dependency --- .../ivy/ant/IvyDependencyUpdateChecker.java | 66 +++++++++++-------- .../ant/IvyDependencyUpdateCheckerTest.java | 10 +-- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java b/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java index 4b5a71385..5f19e8fa8 100644 --- a/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java +++ b/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java @@ -18,13 +18,16 @@ package org.apache.ivy.ant; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import org.apache.ivy.core.module.descriptor.Configuration; import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor; import org.apache.ivy.core.module.descriptor.DependencyDescriptor; import org.apache.ivy.core.module.descriptor.ModuleDescriptor; 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.resolve.IvyNode; import org.apache.ivy.core.resolve.ResolveOptions; @@ -127,34 +130,43 @@ private ModuleDescriptor createModuleDescriptorForRevisionToCheck() { } private void displayDependencyUpdates(ResolveReport originalReport, ResolveReport latestReport) { - log("Dependencies updates available :"); - boolean dependencyUpdateDetected = false; - for (IvyNode latest : latestReport.getDependencies()) { - for (IvyNode originalDependency : originalReport.getDependencies()) { - if (latest.getModuleId().equals(originalDependency.getModuleId())) { - ArtifactInfo in1 = toArtifactInfo(latest); - ArtifactInfo in2 = toArtifactInfo(originalDependency); - ArtifactInfo out = getLatestStrategy(originalDependency).findLatest(new ArtifactInfo[]{in1, in2}, null); - if (out == in1) { - // is this dependency a transitive or a direct dependency? - // (unfortunately .isTransitive() methods do not have the same meaning) - boolean isTransitiveDependency = latest.getDependencyDescriptor(latest - .getRoot()) == null; - if (!isTransitiveDependency || showTransitive) { - log(String.format("\t%s#%s%s\t%s -> %s", - originalDependency.getResolvedId().getOrganisation(), - originalDependency.getResolvedId().getName(), - isTransitiveDependency ? " (transitive)" : "", - originalDependency.getResolvedId().getRevision(), - latest.getResolvedId().getRevision())); - dependencyUpdateDetected = true; - } + Set updates = new LinkedHashSet<>(); + + for (String conf : latestReport.getConfigurations()) { + ConfigurationResolveReport newReport = latestReport.getConfigurationReport(conf); + ConfigurationResolveReport oldReport = originalReport.getConfigurationReport(conf); + + // NOTE: getModuleRevisionIds() filters evicted and problem deps + for (ModuleRevisionId latest : newReport.getModuleRevisionIds()) { + Iterable iter = oldReport.getNodes(latest.getModuleId()); + if (iter == null) { + continue; + } + for (IvyNode node : iter) { + ArtifactInfo in1 = toArtifactInfo(latest.getRevision()); + ArtifactInfo in2 = toArtifactInfo(node.getResolvedId().getRevision()); + ArtifactInfo out = getLatestStrategy(node).findLatest(new ArtifactInfo[]{in1, in2}, null); + + boolean revisionGT = (out == in1); + boolean transitive = (node.getDependencyDescriptor(node.getRoot()) == null); + if (revisionGT && (!transitive || showTransitive)) { + 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 @@ private LatestStrategy getLatestStrategy(IvyNode node) { return getSettings().getDefaultLatestStrategy(); } - private static ArtifactInfo toArtifactInfo(IvyNode node) { + private static ArtifactInfo toArtifactInfo(String revision) { return new ArtifactInfo() { @Override public String getRevision() { - return node.getResolvedId().getRevision(); + return revision; } @Override public long getLastModified() { - return node.getLastModified(); + return 0; } }; } diff --git a/test/java/org/apache/ivy/ant/IvyDependencyUpdateCheckerTest.java b/test/java/org/apache/ivy/ant/IvyDependencyUpdateCheckerTest.java index 48565de11..e4f35f2df 100644 --- a/test/java/org/apache/ivy/ant/IvyDependencyUpdateCheckerTest.java +++ b/test/java/org/apache/ivy/ant/IvyDependencyUpdateCheckerTest.java @@ -261,9 +261,11 @@ public void testSimpleExtends() { assertLogContaining("org1#mod1.2\t2.0 -> 2.2"); // ivy-extends-multiconf.xml declares org2:mod2.1:0.3 assertLogContaining("org2#mod2.1\t0.3 -> 0.7"); - // org2:mod2.1:0.3 ivy.xml declares org1:mod1.1:1.0 - assertLogContaining("org1#mod1.1\t1.0 -> 2.0"); - // org1:mod1.1:2.0 ivy.xml declares org1:mod1.2:2.1 - assertLogContaining("org1#mod1.2\t2.1 -> 2.2"); + // org2:mod2.1:0.3 ivy.xml declares org1:mod1.1:1.0 -- but showTransitives is false + assertLogNotContaining("org1#mod1.1\t1.0 -> 2.0"); + assertLogNotContaining("org1#mod1.1 (transitive)\t1.0 -> 2.0"); + // 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"); } }