Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 99 additions & 88 deletions src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,109 +17,153 @@
*/
package org.apache.ivy.ant;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

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;

import org.apache.tools.ant.BuildException;

import static org.apache.ivy.util.StringUtils.splitToArray;

public class IvyDependencyUpdateChecker extends IvyPostResolveTask {

private String revisionToCheck = "latest.integration";

private boolean download = false;

private boolean checkIfChanged = false;

private boolean showTransitive = false;

private String revisionToCheck = "latest.integration";

public boolean isDownload() {
return download;
}

public void setDownload(boolean download) {
this.download = download;
}

public boolean isCheckIfChanged() {
return checkIfChanged;
}

public void setCheckIfChanged(boolean checkIfChanged) {
this.checkIfChanged = checkIfChanged;
}

public boolean isShowTransitive() {
return showTransitive;
}

public void setShowTransitive(boolean showTransitive) {
this.showTransitive = showTransitive;
}

public String getRevisionToCheck() {
return revisionToCheck;
}

public void setRevisionToCheck(String revisionToCheck) {
this.revisionToCheck = revisionToCheck;
}

//--------------------------------------------------------------------------

@Override
public void doExecute() throws BuildException {
prepareAndCheck();

ModuleDescriptor originalModuleDescriptor = getResolvedReport().getModuleDescriptor();
// clone module descriptor
DefaultModuleDescriptor latestModuleDescriptor = new DefaultModuleDescriptor(
originalModuleDescriptor.getModuleRevisionId(),
originalModuleDescriptor.getStatus(), originalModuleDescriptor.getPublicationDate());
// copy configurations
for (Configuration configuration : originalModuleDescriptor.getConfigurations()) {
latestModuleDescriptor.addConfiguration(configuration);
}
// clone dependency and add new one with the requested revisionToCheck
for (DependencyDescriptor dependencyDescriptor : originalModuleDescriptor.getDependencies()) {
ModuleRevisionId upToDateMrid = ModuleRevisionId.newInstance(
dependencyDescriptor.getDependencyRevisionId(), revisionToCheck);
latestModuleDescriptor.addDependency(dependencyDescriptor.clone(upToDateMrid));
}

// resolve
ResolveOptions resolveOptions = new ResolveOptions();
resolveOptions.setCheckIfChanged(checkIfChanged);
resolveOptions.setConfs(splitToArray(getConf()));
resolveOptions.setDownload(isDownload());
resolveOptions.setLog(getLog());
resolveOptions.setConfs(splitToArray(getConf()));
resolveOptions.setCheckIfChanged(checkIfChanged);

ResolveReport latestReport;
try {
latestReport = getIvyInstance().getResolveEngine().resolve(latestModuleDescriptor,
resolveOptions);
ResolveReport latestReport = getIvyInstance().getResolveEngine()
.resolve(createModuleDescriptorForRevisionToCheck(), resolveOptions);

displayDependencyUpdates(getResolvedReport(), latestReport);
if (showTransitive) {
displayNewDependencyOnLatest(getResolvedReport(), latestReport);
displayMissingDependencyOnLatest(getResolvedReport(), latestReport);
}

} catch (ParseException | IOException e) {
} catch (Exception e) {
throw new BuildException("impossible to resolve dependencies:\n\t" + e, e);
}
}

private ModuleDescriptor createModuleDescriptorForRevisionToCheck() {
ModuleDescriptor moduleDescriptor = getResolvedReport().getModuleDescriptor();

// clone module descriptor
DefaultModuleDescriptor latestModuleDescriptor = new DefaultModuleDescriptor(
moduleDescriptor.getModuleRevisionId(), moduleDescriptor.getStatus(), moduleDescriptor.getPublicationDate());

// copy configurations
for (Configuration configuration : moduleDescriptor.getConfigurations()) {
latestModuleDescriptor.addConfiguration(configuration);
}

// clone direct dependencies with the requested revisionToCheck
for (DependencyDescriptor dependencyDescriptor : moduleDescriptor.getDependencies()) {
ModuleRevisionId upToDateMrid = ModuleRevisionId.newInstance(
dependencyDescriptor.getDependencyRevisionId(), revisionToCheck);
latestModuleDescriptor.addDependency(dependencyDescriptor.clone(upToDateMrid));
}

return latestModuleDescriptor;
}

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 (originalDependency.getModuleId().equals(latest.getModuleId())) {
if (!originalDependency.getResolvedId().getRevision()
.equals(latest.getResolvedId().getRevision())) {
// 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;
}
Map<String, List<String>> updatesByConf = new LinkedHashMap<>();

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<IvyNode> iter = oldReport.getNodes(latest.getModuleId());
if (iter == null) continue;
for (IvyNode node : iter) {
boolean revisionNE = !node.getResolvedId().getRevision().equals(latest.getRevision());
boolean transitive = (node.getDependencyDescriptor(node.getRoot()) == null);
if (revisionNE && (!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());
updatesByConf.computeIfAbsent(conf, k -> new ArrayList<>()).add(update);
}

}
}
}
if (!dependencyUpdateDetected) {
log("\tAll dependencies are up to date");

log("Dependencies updates available :");
if (updatesByConf.isEmpty()) {
log("All dependencies are up to date");
} else {
updatesByConf.forEach((conf, updates) -> {
log("\tconf: " + conf);
updates.forEach(this::log);
});
}
}

private void displayMissingDependencyOnLatest(ResolveReport originalReport,
ResolveReport latestReport) {
private void displayMissingDependencyOnLatest(ResolveReport originalReport, ResolveReport latestReport) {
List<ModuleRevisionId> listOfMissingDependencyOnLatest = new ArrayList<>();
for (IvyNode originalDependency : originalReport.getDependencies()) {
boolean dependencyFound = false;
Expand All @@ -141,8 +185,7 @@ private void displayMissingDependencyOnLatest(ResolveReport originalReport,
}
}

private void displayNewDependencyOnLatest(ResolveReport originalReport,
ResolveReport latestReport) {
private void displayNewDependencyOnLatest(ResolveReport originalReport, ResolveReport latestReport) {
List<ModuleRevisionId> listOfNewDependencyOnLatest = new ArrayList<>();
for (IvyNode latest : latestReport.getDependencies()) {
boolean dependencyFound = false;
Expand All @@ -162,36 +205,4 @@ private void displayNewDependencyOnLatest(ResolveReport originalReport,
}
}
}

public String getRevisionToCheck() {
return revisionToCheck;
}

public void setRevisionToCheck(String revisionToCheck) {
this.revisionToCheck = revisionToCheck;
}

public boolean isDownload() {
return download;
}

public void setDownload(boolean download) {
this.download = download;
}

public boolean isShowTransitive() {
return showTransitive;
}

public void setShowTransitive(boolean showTransitive) {
this.showTransitive = showTransitive;
}

public boolean isCheckIfChanged() {
return checkIfChanged;
}

public void setCheckIfChanged(boolean checkIfChanged) {
this.checkIfChanged = checkIfChanged;
}
}
69 changes: 32 additions & 37 deletions test/java/org/apache/ivy/ant/IvyDependencyUpdateCheckerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ public void setUp() {
dependencyUpdateChecker.setProject(project);
}

@After
public void tearDown() {
@After
public void tearDown() {
TestHelper.cleanCache();
}

Expand Down Expand Up @@ -137,8 +137,7 @@ public void testInlineForNonExistingModule() {
*/
@Test(expected = BuildException.class)
public void testFailure() {
dependencyUpdateChecker
.setFile(new File("test/java/org/apache/ivy/ant/ivy-failure.xml"));
dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-failure.xml"));
dependencyUpdateChecker.execute();
}

Expand All @@ -150,8 +149,7 @@ public void testFailureWithMissingConfigurations() {
expExc.expect(BuildException.class);
expExc.expectMessage("unknown");

dependencyUpdateChecker
.setFile(new File("test/java/org/apache/ivy/ant/ivy-simple.xml"));
dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-simple.xml"));
dependencyUpdateChecker.setConf("default,unknown");
dependencyUpdateChecker.execute();
}
Expand All @@ -161,8 +159,7 @@ public void testFailureWithMissingConfigurations() {
*/
@Test(expected = BuildException.class)
public void testFailureOnBadDependencyIvyFile() {
dependencyUpdateChecker.setFile(new File(
"test/java/org/apache/ivy/ant/ivy-failure2.xml"));
dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-failure2.xml"));
dependencyUpdateChecker.execute();
}

Expand All @@ -171,15 +168,13 @@ public void testFailureOnBadDependencyIvyFile() {
*/
@Test(expected = BuildException.class)
public void testFailureOnBadStatusInDependencyIvyFile() {
dependencyUpdateChecker.setFile(new File(
"test/java/org/apache/ivy/ant/ivy-failure3.xml"));
dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-failure3.xml"));
dependencyUpdateChecker.execute();
}

@Test
public void testHaltOnFailure() {
dependencyUpdateChecker
.setFile(new File("test/java/org/apache/ivy/ant/ivy-failure.xml"));
dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-failure.xml"));
dependencyUpdateChecker.setHaltonfailure(false);
dependencyUpdateChecker.execute();
}
Expand All @@ -190,10 +185,8 @@ public void testExcludedConf() {
dependencyUpdateChecker.setConf("*,!default");
dependencyUpdateChecker.execute();

// assertTrue(getIvyFileInCache(ModuleRevisionId.newInstance("org1", "mod1.1", "2.0"))
// .exists());
// assertFalse(getIvyFileInCache(ModuleRevisionId.newInstance("org1", "mod1.2", "2.0"))
// .exists());
// assertTrue(getIvyFileInCache(ModuleRevisionId.newInstance("org1", "mod1.1", "2.0")).exists());
// assertFalse(getIvyFileInCache(ModuleRevisionId.newInstance("org1", "mod1.2", "2.0")).exists());

assertLogContaining("Dependencies updates available :");
assertLogContaining("All dependencies are up to date");
Expand All @@ -213,8 +206,7 @@ public void testResolveWithAbsoluteFile() {
dependencyUpdateChecker.getProject().setProperty("ivy.dep.file", ivyFile.getAbsolutePath());
dependencyUpdateChecker.execute();

// assertTrue(getResolvedIvyFileInCache(
// ModuleRevisionId.newInstance("apache", "resolve-simple", "1.0")).exists());
// assertTrue(getResolvedIvyFileInCache(ModuleRevisionId.newInstance("apache", "resolve-simple", "1.0")).exists());
}

/**
Expand All @@ -228,35 +220,38 @@ public void testResolveWithRelativeFile() {
"test/java/org/apache/ivy/ant/ivy-simple.xml");
dependencyUpdateChecker.execute();

// assertTrue(getResolvedIvyFileInCache(
// ModuleRevisionId.newInstance("apache", "resolve-simple", "1.0")).exists());
// assertTrue(getResolvedIvyFileInCache(ModuleRevisionId.newInstance("apache", "resolve-simple", "1.0")).exists());

assertLogContaining("Dependencies updates available :");
assertLogContaining("org1#mod1.2\t2.0 -> 2.2");
}

@Test
public void testSimpleExtends() {
dependencyUpdateChecker.setFile(new File(
"test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml"));
dependencyUpdateChecker.setFile(new File("test/java/org/apache/ivy/ant/ivy-extends-multiconf.xml"));
dependencyUpdateChecker.execute();
assertEquals("1", dependencyUpdateChecker.getProject().getProperty("ivy.parents.count"));
assertEquals("apache",
dependencyUpdateChecker.getProject().getProperty("ivy.parent[0].organisation"));
assertEquals("resolve-simple",
dependencyUpdateChecker.getProject().getProperty("ivy.parent[0].module"));
assertEquals("1.0",
dependencyUpdateChecker.getProject().getProperty("ivy.parent[0].revision"));
assertNull(dependencyUpdateChecker.getProject().getProperty("ivy.parent[0].branch"));

assertLogContaining("Dependencies updates available :");
Project project = dependencyUpdateChecker.getProject();
assertEquals("1", project.getProperty("ivy.parents.count"));
assertEquals("apache", project.getProperty("ivy.parent[0].organisation"));
assertEquals("resolve-simple", project.getProperty("ivy.parent[0].module"));
assertEquals("1.0", project.getProperty("ivy.parent[0].revision"));
assertNull(project.getProperty("ivy.parent[0].branch"));

assertLogContaining("Dependencies updates available");
// ivy-extends-multiconf.xml declares org1:mod1.1:1.1
assertLogContaining("org1#mod1.1\t1.1 -> 2.0");
assertLogContaining("org1#mod1.1\t1.0 -> 2.0");
assertLogContaining("org1#mod1.2\t2.1 -> 2.2");
assertLogContaining("org2#mod2.1\t0.3 -> 0.7");

// inherited from parent
// ivy-multiconf.xml declares org1:mod1.1:2.0 -- no update available
assertLogNotContaining("org1#mod1.1\t2.0");
// ivy-multiconf.xml declares org1:mod1.2:2.0
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 -- 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");
}

}
Loading