[MNG-7741] Track missing files, plugin and parent pom dependencies (#1058)

Add more information when using `-Dmaven.repo.local.recordReverseTree=true`.

Signed-off-by: Grzegorz Grzybek <gr.grzybek@gmail.com>

---

https://issues.apache.org/jira/browse/MNG-7741
This commit is contained in:
Grzegorz Grzybek 2023-04-12 13:05:59 +02:00 committed by GitHub
parent 0f18470576
commit bc138dc3ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 145 additions and 35 deletions

View File

@ -18,15 +18,19 @@
*/ */
package org.apache.maven.internal.aether; package org.apache.maven.internal.aether;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Objects; import java.util.Objects;
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.Plugin;
import org.eclipse.aether.AbstractRepositoryListener; import org.eclipse.aether.AbstractRepositoryListener;
import org.eclipse.aether.RepositoryEvent; import org.eclipse.aether.RepositoryEvent;
import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RepositorySystemSession;
@ -35,6 +39,10 @@ import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.collection.CollectStepData; import org.eclipse.aether.collection.CollectStepData;
import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.util.artifact.ArtifactIdUtils;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
@ -49,23 +57,89 @@ class ReverseTreeRepositoryListener extends AbstractRepositoryListener {
public void artifactResolved(RepositoryEvent event) { public void artifactResolved(RepositoryEvent event) {
requireNonNull(event, "event cannot be null"); requireNonNull(event, "event cannot be null");
if (!isLocalRepositoryArtifact(event.getSession(), event.getArtifact())) { if (!isLocalRepositoryArtifactOrMissing(event.getSession(), event.getArtifact())) {
return; return;
} }
CollectStepData collectStepTrace = lookupCollectStepData(event.getTrace()); RequestTrace trace = event.getTrace();
if (collectStepTrace == null) {
CollectStepData collectStepTrace = null;
ArtifactRequest artifactRequest = null;
ArtifactDescriptorRequest artifactDescriptorRequest = null;
Plugin plugin = null;
while (trace != null) {
Object data = trace.getData();
if (data instanceof CollectStepData) {
collectStepTrace = (CollectStepData) data;
} else if (data instanceof ArtifactDescriptorRequest) {
artifactDescriptorRequest = (ArtifactDescriptorRequest) data;
} else if (data instanceof ArtifactRequest) {
artifactRequest = (ArtifactRequest) data;
} else if (data instanceof Plugin) {
plugin = (Plugin) data;
}
trace = trace.getParent();
}
Path trackingDir;
boolean missing = event.getFile() == null;
if (missing) {
// missing artifact - let's track the path anyway
File dir = event.getSession().getLocalRepository().getBasedir();
dir = new File(
dir, event.getSession().getLocalRepositoryManager().getPathForLocalArtifact(event.getArtifact()));
trackingDir = dir.getParentFile().toPath().resolve(".tracking");
} else {
trackingDir = event.getFile().getParentFile().toPath().resolve(".tracking");
}
String baseName;
String ext = missing ? ".miss" : ".dep";
Path trackingFile = null;
String indent = "";
ArrayList<String> trackingData = new ArrayList<>();
if (collectStepTrace == null && plugin != null) {
ext = ".plugin";
baseName = plugin.getGroupId() + "_" + plugin.getArtifactId() + "_" + plugin.getVersion();
trackingFile = trackingDir.resolve(baseName + ext);
if (Files.exists(trackingFile)) {
return;
}
if (event.getArtifact() != null) {
trackingData.add(indent + event.getArtifact());
indent += " ";
}
trackingData.add(indent + plugin.getGroupId() + ":" + plugin.getArtifactId() + ":" + plugin.getVersion());
indent += " ";
InputLocation location = plugin.getLocation("");
if (location != null && location.getSource() != null) {
trackingData.add(indent + location.getSource().getModelId() + " (implicit)");
indent += " ";
}
} else if (collectStepTrace != null) {
if (collectStepTrace.getPath().get(0).getArtifact() == null) {
return;
}
baseName = ArtifactIdUtils.toId(collectStepTrace.getPath().get(0).getArtifact())
.replace(":", "_");
trackingFile = trackingDir.resolve(baseName + ext);
if (Files.exists(trackingFile)) {
return; return;
} }
Artifact resolvedArtifact = event.getArtifact(); Artifact resolvedArtifact = event.getArtifact();
Artifact nodeArtifact = collectStepTrace.getNode().getArtifact(); Artifact nodeArtifact = collectStepTrace.getNode().getArtifact();
if (isInScope(resolvedArtifact, nodeArtifact)) { if (isInScope(resolvedArtifact, nodeArtifact) || "pom".equals(resolvedArtifact.getExtension())) {
Dependency node = collectStepTrace.getNode(); Dependency node = collectStepTrace.getNode();
ArrayList<String> trackingData = new ArrayList<>(); trackingData.add(resolvedArtifact.toString());
trackingData.add(node + " (" + collectStepTrace.getContext() + ")"); indent += " ";
String indent = ""; trackingData.add(indent + node + " (" + collectStepTrace.getContext() + ")");
ListIterator<DependencyNode> iter = collectStepTrace ListIterator<DependencyNode> iter = collectStepTrace
.getPath() .getPath()
.listIterator(collectStepTrace.getPath().size()); .listIterator(collectStepTrace.getPath().size());
@ -74,22 +148,42 @@ class ReverseTreeRepositoryListener extends AbstractRepositoryListener {
indent += " "; indent += " ";
trackingData.add(indent + curr + " (" + collectStepTrace.getContext() + ")"); trackingData.add(indent + curr + " (" + collectStepTrace.getContext() + ")");
} }
}
}
if (trackingFile == null) {
return;
}
try { try {
Path trackingDir =
resolvedArtifact.getFile().getParentFile().toPath().resolve(".tracking");
Files.createDirectories(trackingDir); Files.createDirectories(trackingDir);
Path trackingFile = trackingDir.resolve(collectStepTrace
.getPath() trackingData.add("");
.get(0) if (!missing) {
.getArtifact() if (event.getRepository() != null) {
.toString() trackingData.add("Repository: " + event.getRepository());
.replace(":", "_")); }
} else {
List<RemoteRepository> repositories = new ArrayList<>();
if (artifactRequest != null && artifactRequest.getRepositories() != null) {
repositories.addAll(artifactRequest.getRepositories());
} else if (artifactDescriptorRequest != null && artifactDescriptorRequest.getRepositories() != null) {
repositories.addAll(artifactDescriptorRequest.getRepositories());
}
if (!repositories.isEmpty()) {
trackingData.add("Configured repositories:");
for (RemoteRepository r : repositories) {
trackingData.add(" - " + r.getId() + " : " + r.getUrl());
}
} else {
trackingData.add("No repositories configured");
}
}
Files.write(trackingFile, trackingData, StandardCharsets.UTF_8); Files.write(trackingFile, trackingData, StandardCharsets.UTF_8);
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }
} }
}
/** /**
* Returns {@code true} if passed in artifact is originating from local repository. In other words, we want * Returns {@code true} if passed in artifact is originating from local repository. In other words, we want
@ -99,8 +193,9 @@ class ReverseTreeRepositoryListener extends AbstractRepositoryListener {
* <p> * <p>
* Visible for testing. * Visible for testing.
*/ */
static boolean isLocalRepositoryArtifact(RepositorySystemSession session, Artifact artifact) { static boolean isLocalRepositoryArtifactOrMissing(RepositorySystemSession session, Artifact artifact) {
return artifact.getFile() return artifact.getFile() == null
|| artifact.getFile()
.getPath() .getPath()
.startsWith(session.getLocalRepository().getBasedir().getPath()); .startsWith(session.getLocalRepository().getBasedir().getPath());
} }

View File

@ -52,13 +52,28 @@ public class ReverseTreeRepositoryListenerTest {
when(nonLocalReposioryArtifact.getFile()).thenReturn(new File("something/completely/different")); when(nonLocalReposioryArtifact.getFile()).thenReturn(new File("something/completely/different"));
assertThat( assertThat(
ReverseTreeRepositoryListener.isLocalRepositoryArtifact(session, localRepositoryArtifact), ReverseTreeRepositoryListener.isLocalRepositoryArtifactOrMissing(session, localRepositoryArtifact),
equalTo(true)); equalTo(true));
assertThat( assertThat(
ReverseTreeRepositoryListener.isLocalRepositoryArtifact(session, nonLocalReposioryArtifact), ReverseTreeRepositoryListener.isLocalRepositoryArtifactOrMissing(session, nonLocalReposioryArtifact),
equalTo(false)); equalTo(false));
} }
@Test
public void isMissingArtifactTest() {
File baseDir = new File("local/repository");
LocalRepository localRepository = new LocalRepository(baseDir);
RepositorySystemSession session = mock(RepositorySystemSession.class);
when(session.getLocalRepository()).thenReturn(localRepository);
Artifact localRepositoryArtifact = mock(Artifact.class);
when(localRepositoryArtifact.getFile()).thenReturn(null);
assertThat(
ReverseTreeRepositoryListener.isLocalRepositoryArtifactOrMissing(session, localRepositoryArtifact),
equalTo(true));
}
@Test @Test
public void lookupCollectStepDataTest() { public void lookupCollectStepDataTest() {
RequestTrace doesNotHaveIt = RequestTrace doesNotHaveIt =