From 8f7935d584e596705c08edac040142b28e0a818b Mon Sep 17 00:00:00 2001 From: Leonid Shalupov Date: Wed, 15 Jan 2025 13:03:43 -0400 Subject: [PATCH] IJPL-175157 Changing version of a Maven library in JPS project download JARs to the wrong location GitOrigin-RevId: 4b911dc871a0565926218b00a9bb8cfdf26e34fd --- .../jarRepository/JarRepositoryManager.java | 3 +- .../maven/utils/library/RepositoryUtils.java | 29 ++++++++++- .../utils/library/RepositoryUtilsTest.java | 50 +++++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/java/idea-ui/src/com/intellij/jarRepository/JarRepositoryManager.java b/java/idea-ui/src/com/intellij/jarRepository/JarRepositoryManager.java index d326eff33665..4f31ceb887b5 100644 --- a/java/idea-ui/src/com/intellij/jarRepository/JarRepositoryManager.java +++ b/java/idea-ui/src/com/intellij/jarRepository/JarRepositoryManager.java @@ -216,7 +216,8 @@ public final class JarRepositoryManager { } @TestOnly - static void setLocalRepositoryPath(File localRepo) { + @ApiStatus.Internal + public static void setLocalRepositoryPath(File localRepo) { ourLocalRepositoryPath = localRepo; } diff --git a/java/idea-ui/src/org/jetbrains/idea/maven/utils/library/RepositoryUtils.java b/java/idea-ui/src/org/jetbrains/idea/maven/utils/library/RepositoryUtils.java index 1e10c0f8891a..5898cbe63890 100644 --- a/java/idea-ui/src/org/jetbrains/idea/maven/utils/library/RepositoryUtils.java +++ b/java/idea-ui/src/org/jetbrains/idea/maven/utils/library/RepositoryUtils.java @@ -18,7 +18,6 @@ import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor; import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VfsUtilCore; @@ -31,6 +30,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.concurrency.AsyncPromise; import org.jetbrains.concurrency.Promise; +import java.io.File; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @@ -83,11 +83,36 @@ public final class RepositoryUtils { String firstPath = getOnDiskParentPath(urls[0]); for (String root : urls) { - if (!StringUtil.equals(firstPath, getOnDiskParentPath(root))) { + if (!FileUtil.pathsEqual(firstPath, getOnDiskParentPath(root))) { return null; } } + if (urls.length == 1) { + // IJPL-175157 Only one file in the library, so we can't decide on storage root without looking into cache location + // It's worse with symlinks where we may have a non-canonical path in `firstPath` + // and canonical path in JarRepositoryManager.getLocalRepositoryPath + var localRepositoryPath = JarRepositoryManager.getLocalRepositoryPath(); + + // happy case, no symlinks, so canonical localRepositoryPath is the same is firstPath + if (FileUtil.startsWith(firstPath, localRepositoryPath.getPath())) { + return null; + } + + // non-happy case, symlinks, let's try to get as much canonical as we can. + // covered by tests + try { + var canonicalFirstPath = new File(firstPath).getCanonicalPath(); + var canonicalLocalRepositoryPath = localRepositoryPath.getCanonicalPath(); + if (FileUtil.startsWith(canonicalFirstPath, canonicalLocalRepositoryPath)) { + return null; + } + } + catch (IOException ignored) { + // IOError, can't decide + } + } + return firstPath; } diff --git a/java/idea-ui/testSrc/org/jetbrains/idea/maven/utils/library/RepositoryUtilsTest.java b/java/idea-ui/testSrc/org/jetbrains/idea/maven/utils/library/RepositoryUtilsTest.java index f1fb71cdcfc4..ab993542e34a 100644 --- a/java/idea-ui/testSrc/org/jetbrains/idea/maven/utils/library/RepositoryUtilsTest.java +++ b/java/idea-ui/testSrc/org/jetbrains/idea/maven/utils/library/RepositoryUtilsTest.java @@ -5,10 +5,12 @@ import com.intellij.jarRepository.*; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.IoTestUtil; import com.intellij.testFramework.ServiceContainerUtil; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.library.JpsMavenRepositoryLibraryDescriptor; +import org.jetbrains.jps.util.JpsPathUtil; import java.io.IOException; import java.nio.file.Files; @@ -138,6 +140,54 @@ public class RepositoryUtilsTest extends LibraryTest { ); } + public void testGetStorageRootWithSymlinks() throws IOException { + IoTestUtil.assumeSymLinkCreationIsSupported(); + + var temp = Files.createTempDirectory("storage-root").toRealPath(); + var dir = temp.resolve("dir"); + var dir2 = temp.resolve("dir2"); + var symlinkToDir = temp.resolve("symlink"); + + Files.createDirectory(dir); + Files.createSymbolicLink(symlinkToDir, dir); + + var oldLocalRepositoryPath = JarRepositoryManager.getLocalRepositoryPath(); + JarRepositoryManager.setLocalRepositoryPath(dir.toFile()); + + try { + // IJPL-175157 one url, should return null since it's under local repository, request symlinks resolve + assertNull( + RepositoryUtils.getStorageRoot( + new String[]{ + JpsPathUtil.pathToUrl(symlinkToDir.toString()) + "/com/fasterxml/jackson/jr/jackson-jr-objects/2.17.2/jackson-jr-objects-2.17.2.jar", + } + ) + ); + + // IJPL-175157 one url, should return null since it's under local repository + assertNull( + RepositoryUtils.getStorageRoot( + new String[]{ + JpsPathUtil.pathToUrl(dir.toString()) + "/com/fasterxml/jackson/jr/jackson-jr-objects/2.17.2/jackson-jr-objects-2.17.2.jar", + } + ) + ); + + // Another directory; should return it + assertEquals( + FileUtil.toSystemDependentName(dir2 + "/com/fasterxml/jackson/jr/jackson-jr-objects/2.17.2"), + RepositoryUtils.getStorageRoot( + new String[]{ + JpsPathUtil.pathToUrl(dir2.toString()) + "/com/fasterxml/jackson/jr/jackson-jr-objects/2.17.2/jackson-jr-objects-2.17.2.jar", + } + ) + ); + } finally { + JarRepositoryManager.setLocalRepositoryPath(oldLocalRepositoryPath); + FileUtil.deleteRecursively(temp); + } + } + private static String fileContent(Path path) { try { return Files.readAllLines(path).get(0);