From f87b4b71aa92462a5ad6268a8b629bfc098f5a9b Mon Sep 17 00:00:00 2001 From: Ilija Stankovic Date: Mon, 24 Jul 2023 11:43:14 +0200 Subject: [PATCH 01/10] Made package.config path configurable from NuGet preferences --- src/NuGetForUnity.Tests/Assets/NuGet.config | 5 +- .../Assets/Tests/Editor/NuGetTests.cs | 29 +++++++--- .../Assets/Tests/Resources/NuGet.config | 5 +- src/NuGetForUnity/Editor/NugetConfigFile.cs | 40 ++++++++++++++ src/NuGetForUnity/Editor/NugetHelper.cs | 49 ++++++++++++----- .../Editor/NugetPackageAssetPostprocessor.cs | 8 ++- src/NuGetForUnity/Editor/NugetPreferences.cs | 32 ++++++++++- .../Editor/PackagesConfigFile.cs | 53 +++++++++++++++++-- 8 files changed, 192 insertions(+), 29 deletions(-) diff --git a/src/NuGetForUnity.Tests/Assets/NuGet.config b/src/NuGetForUnity.Tests/Assets/NuGet.config index 7bffa036..a948d6d1 100644 --- a/src/NuGetForUnity.Tests/Assets/NuGet.config +++ b/src/NuGetForUnity.Tests/Assets/NuGet.config @@ -1,4 +1,4 @@ - + @@ -12,6 +12,7 @@ + - + \ No newline at end of file diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs index 989bcef0..8bea2c4d 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs +++ b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs @@ -649,11 +649,11 @@ public void TestSerializeNugetPackageIdentifier(string version) public void TestPostprocessInstall(string packageId, string packageVersion) { var package = new NugetPackageIdentifier(packageId, packageVersion) { IsManuallyInstalled = true }; - var filepath = NugetHelper.PackagesConfigFilePath; + var filepath = NugetHelper.NugetConfigFile.PackagesConfigFilePath; var packagesConfigFile = new PackagesConfigFile(); packagesConfigFile.AddPackage(package); - packagesConfigFile.Save(filepath); + packagesConfigFile.Save(); Assert.IsFalse(NugetHelper.IsInstalled(package), "The package IS installed: {0} {1}", package.Id, package.Version); @@ -669,13 +669,13 @@ public void TestPostprocessInstall(string packageId, string packageVersion) public void TestPostprocessUninstall(string packageId, string packageVersion) { var package = new NugetPackageIdentifier(packageId, packageVersion) { IsManuallyInstalled = true }; - var filepath = NugetHelper.PackagesConfigFilePath; + var filepath = NugetHelper.NugetConfigFile.PackagesConfigFilePath; NugetHelper.InstallIdentifier(package); Assert.IsTrue(NugetHelper.IsInstalled(package), "The package was NOT installed: {0} {1}", package.Id, package.Version); var packagesConfigFile = new PackagesConfigFile(); - packagesConfigFile.Save(filepath); + packagesConfigFile.Save(); var assetsIndex = filepath.LastIndexOf("Assets", StringComparison.Ordinal); filepath = filepath.Substring(assetsIndex); @@ -691,14 +691,14 @@ public void TestPostprocessDifferentVersion(string packageId, string packageVers { var packageOld = new NugetPackageIdentifier(packageId, packageVersionOld) { IsManuallyInstalled = true }; var packageNew = new NugetPackageIdentifier(packageId, packageVersionNew) { IsManuallyInstalled = true }; - var filepath = NugetHelper.PackagesConfigFilePath; + var filepath = NugetHelper.NugetConfigFile.PackagesConfigFilePath; NugetHelper.InstallIdentifier(packageOld); Assert.IsTrue(NugetHelper.IsInstalled(packageOld), "The package was NOT installed: {0} {1}", packageOld.Id, packageOld.Version); var packagesConfigFile = new PackagesConfigFile(); packagesConfigFile.AddPackage(packageNew); - packagesConfigFile.Save(filepath); + packagesConfigFile.Save(); var assetsIndex = filepath.LastIndexOf("Assets", StringComparison.Ordinal); filepath = filepath.Substring(assetsIndex); @@ -718,4 +718,21 @@ private static void ConfigureNugetConfig(InstallMode installMode) installMode == InstallMode.ApiV2Only || installMode == InstallMode.ApiV2AllowCached; nugetConfigFile.InstallFromCache = installMode == InstallMode.ApiV2AllowCached; } + + [Test] + [TestCase("", "Assets")] + [TestCase("../", ".")] + [TestCase("/../../", null)] + [TestCase("a/b/c", "Assets/a/b/c")] + public void PathTest(string append, string expected) + { + var path = Path.GetFullPath(Path.Combine(Application.dataPath, append)); + var relativePath = NugetHelper.GetProjectRelativePath(path); + if (expected == null) + { + expected = path; + } + + Assert.That(relativePath, Is.EqualTo(expected)); + } } diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config b/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config index 9182d778..889800d9 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config +++ b/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config @@ -1,4 +1,4 @@ - + @@ -16,6 +16,7 @@ + - + \ No newline at end of file diff --git a/src/NuGetForUnity/Editor/NugetConfigFile.cs b/src/NuGetForUnity/Editor/NugetConfigFile.cs index cc9c8322..a23794a3 100644 --- a/src/NuGetForUnity/Editor/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/NugetConfigFile.cs @@ -74,6 +74,35 @@ public class NugetConfigFile /// public bool ReadOnlyPackageFiles { get; set; } + public string PackagesConfigPath; + + public string RelativePackagesConfigPath + { + get => NugetHelper.GetProjectRelativePath(PackagesConfigPath); + + private set + { + PackagesConfigPath = Path.GetFullPath(Environment.ExpandEnvironmentVariables(value)); + + var packageConfigRelativePath = NugetHelper.GetProjectRelativePath(PackagesConfigPath); + + // package config path is invalid (not under Assets), set to default + if (packageConfigRelativePath == PackagesConfigPath || packageConfigRelativePath == ".") + { + Debug.LogError("Path to packages.config in NuGet.config must be relative to the project root. Setting to default."); + PackagesConfigPath = Application.dataPath; + Save(NugetHelper.NugetConfigFilePath); + } + + PackagesConfigPath = PackagesConfigPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + } + } + + /// + /// The path to the packages.config file. + /// + public string PackagesConfigFilePath => Path.Combine(PackagesConfigPath, PackagesConfigFile.FileName); + /// /// Gets or sets the timeout in seconds used for all web requests to NuGet sources. /// @@ -147,6 +176,11 @@ public void Save(string filePath) addElement.Add(new XAttribute("value", savedRepositoryPath)); config.Add(addElement); + addElement = new XElement("add"); + addElement.Add(new XAttribute("key", "PackagesConfigPath")); + addElement.Add(new XAttribute("value", RelativePackagesConfigPath)); + config.Add(addElement); + // save the default push source if (DefaultPushSource != null) { @@ -233,6 +267,7 @@ public static NugetConfigFile Load(string filePath) configFile.PackageSources = new List(); configFile.InstallFromCache = true; configFile.ReadOnlyPackageFiles = false; + configFile.RelativePackagesConfigPath = "Assets"; var file = XDocument.Load(filePath); @@ -355,6 +390,10 @@ public static NugetConfigFile Load(string filePath) { configFile.LockPackagesOnRestore = bool.Parse(value); } + else if (string.Equals(key, "PackagesConfigPath", StringComparison.OrdinalIgnoreCase)) + { + configFile.RelativePackagesConfigPath = value; + } } } @@ -380,6 +419,7 @@ public static NugetConfigFile CreateDefaultFile(string filePath) + "; diff --git a/src/NuGetForUnity/Editor/NugetHelper.cs b/src/NuGetForUnity/Editor/NugetHelper.cs index d853f9bb..c039c4b4 100644 --- a/src/NuGetForUnity/Editor/NugetHelper.cs +++ b/src/NuGetForUnity/Editor/NugetHelper.cs @@ -38,14 +38,6 @@ public static class NugetHelper /// public static readonly string NugetConfigFilePath = Path.GetFullPath(Path.Combine(Application.dataPath, NugetConfigFile.FileName)); - /// - /// The path to the packages.config file. - /// - /// - /// . - /// - internal static readonly string PackagesConfigFilePath = Path.GetFullPath(Path.Combine(Application.dataPath, PackagesConfigFile.FileName)); - /// /// Gets the absolute path to the Unity-Project root directory. /// @@ -97,7 +89,7 @@ public static PackagesConfigFile PackagesConfigFile { if (packagesConfigFile == null) { - packagesConfigFile = PackagesConfigFile.Load(PackagesConfigFilePath); + packagesConfigFile = PackagesConfigFile.Load(); } return packagesConfigFile; @@ -756,7 +748,7 @@ public static void Uninstall(INugetPackageIdentifier package, bool refreshAssets // update the package.config file if (PackagesConfigFile.RemovePackage(foundPackage)) { - PackagesConfigFile.Save(PackagesConfigFilePath); + PackagesConfigFile.Save(); } var packageInstallDirectory = Path.Combine(NugetConfigFile.RepositoryPath, $"{foundPackage.Id}.{foundPackage.Version}"); @@ -855,7 +847,7 @@ public static void UpdateAll(IEnumerable updates, IEnumerable @@ -927,8 +919,7 @@ void AddPackageToInstalled(INugetPackage package) { PackagesConfigFile.SetManuallyInstalledFlag(rootPackage); } - - PackagesConfigFile.Save(PackagesConfigFilePath); + PackagesConfigFile.Save(); } } @@ -1283,7 +1274,7 @@ public static bool Install(INugetPackage package, bool refreshAssets = true, boo // update packages.config PackagesConfigFile.AddPackage(package); - PackagesConfigFile.Save(PackagesConfigFilePath); + PackagesConfigFile.Save(); var cachedPackagePath = Path.Combine(PackOutputDirectory, package.PackageFileName); if (NugetConfigFile.InstallFromCache && File.Exists(cachedPackagePath)) @@ -1589,5 +1580,35 @@ internal static bool IsInstalled(INugetPackageIdentifier package) return isInstalled; } + + /// + /// Returns the path relative to project directory, or path if it's not relative to project directory. + /// Should the project upgrade to .NET Standard 2.1, this will be removed and instead we will use Path.GetRelativePath(relativeTo, path); + /// + /// The destination path. + public static string GetProjectRelativePath(string absolutePath) + { + var projectFolder = (Path.GetDirectoryName(Application.dataPath) ?? string.Empty).Replace('\\', '/'); + + if (string.IsNullOrEmpty(projectFolder) || string.IsNullOrEmpty(absolutePath) || !Path.IsPathRooted(absolutePath)) + { + return absolutePath; + } + + var path = absolutePath.Replace('\\', '/'); + + if (!path.StartsWith(projectFolder, StringComparison.Ordinal)) + { + return absolutePath; + } + + var projectFolderLength = projectFolder.Length; + if (path.Length > projectFolderLength && path[projectFolderLength] == '/') + { + projectFolderLength++; + } + + return path.Length > projectFolderLength ? path.Substring(projectFolderLength) : "."; + } } } diff --git a/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs b/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs index ec41b897..e4f19fc6 100644 --- a/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs +++ b/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs @@ -52,7 +52,13 @@ internal static void OnPostprocessAllAssets( string[] movedAssets, string[] movedFromAssetPaths) { - var packagesConfigFilePath = Path.GetFullPath(NugetHelper.PackagesConfigFilePath); + // currently only importedAssets are important for us, so if there are none, we do nothing + if (importedAssets.Length == 0) + { + return; + } + + var packagesConfigFilePath = NugetHelper.NugetConfigFile.PackagesConfigFilePath; var foundPackagesConfigAsset = importedAssets.Any( importedAsset => Path.GetFullPath(importedAsset).Equals(packagesConfigFilePath, StringComparison.Ordinal)); diff --git a/src/NuGetForUnity/Editor/NugetPreferences.cs b/src/NuGetForUnity/Editor/NugetPreferences.cs index aeff974e..236a42c8 100644 --- a/src/NuGetForUnity/Editor/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/NugetPreferences.cs @@ -1,4 +1,6 @@ -using UnityEditor; +using System.IO; +using System.Linq; +using UnityEditor; using UnityEngine; namespace NugetForUnity @@ -72,6 +74,34 @@ public override void OnGUI(string searchContext) NugetHelper.NugetConfigFile.Verbose = verbose; } + EditorGUILayout.BeginHorizontal(); + { + var packagesConfigPath = NugetHelper.NugetConfigFile.RelativePackagesConfigPath; + EditorGUILayout.LabelField($"Packages Config path: {packagesConfigPath}"); + packagesConfigPath = Path.GetFullPath(packagesConfigPath); + if (GUILayout.Button("Browse")) + { + var newPath = EditorUtility.OpenFolderPanel("Select Folder", packagesConfigPath, ""); + + if (!string.IsNullOrEmpty(newPath) && newPath != packagesConfigPath) + { + var pathCheck = NugetHelper.GetProjectRelativePath(newPath); + + // make sure the path is within Assets directory + if (!pathCheck.StartsWith("Assets")) + { + Debug.LogError("packages.config path has to be within /Assets."); + } + else + { + PackagesConfigFile.Move(newPath); + preferencesChangedThisFrame = true; + } + } + } + } + EditorGUILayout.EndHorizontal(); + var requestTimeout = EditorGUILayout.IntField( new GUIContent( "Request Timeout in seconds", diff --git a/src/NuGetForUnity/Editor/PackagesConfigFile.cs b/src/NuGetForUnity/Editor/PackagesConfigFile.cs index 407108e3..1c11bfd7 100644 --- a/src/NuGetForUnity/Editor/PackagesConfigFile.cs +++ b/src/NuGetForUnity/Editor/PackagesConfigFile.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Xml.Linq; +using UnityEditor; using UnityEngine; namespace NugetForUnity @@ -106,8 +107,9 @@ internal void SetManuallyInstalledFlag(INugetPackageIdentifier package) /// Loads a list of all currently installed packages by reading the packages.config file. /// /// A newly created . - public static PackagesConfigFile Load(string filePath) + public static PackagesConfigFile Load() { + var filePath = NugetHelper.NugetConfigFile.PackagesConfigFilePath; var configFile = new PackagesConfigFile { Packages = new List() }; // Create a package.config file, if there isn't already one in the project @@ -115,7 +117,7 @@ public static PackagesConfigFile Load(string filePath) { Debug.LogFormat("No packages.config file found. Creating default at {0}", filePath); - configFile.Save(filePath); + configFile.Save(); } var packagesFile = XDocument.Load(filePath); @@ -141,8 +143,9 @@ public static PackagesConfigFile Load(string filePath) /// Saves the packages.config file and populates it with given installed NugetPackages. /// /// The file-path to where this packages.config will be saved. - public void Save(string filePath) + public void Save() { + var filePath = NugetHelper.NugetConfigFile.PackagesConfigFilePath; if (contentIsSameAsInFilePath == filePath) { return; @@ -212,6 +215,50 @@ public void Save(string filePath) contentIsSameAsInFilePath = filePath; } + internal static void Move(string newPath) + { + var oldFilePath = NugetHelper.NugetConfigFile.PackagesConfigFilePath; + var oldPath = NugetHelper.NugetConfigFile.PackagesConfigPath; + NugetHelper.NugetConfigFile.PackagesConfigPath = newPath; + if (!File.Exists(oldFilePath)) + { + Debug.LogFormat("No packages.config file found. Creating default at {0}", newPath); + var configFile = new PackagesConfigFile { Packages = new List() }; + configFile.Save(); + AssetDatabase.Refresh(); + return; + } + + var newFilePath = Path.GetFullPath(Path.Combine(newPath, FileName)); + + // creating directory if it doesn't exist + if (!Directory.Exists(newPath)) + { + Directory.CreateDirectory(newPath); + } + + // moving config to the new path + File.Move(oldFilePath, newFilePath); + + // manually moving meta file to suppress Unity warning + if (File.Exists(oldFilePath + ".meta")) + { + File.Move(oldFilePath + ".meta", newFilePath + ".meta"); + } + + // if the old path is now an empty directory, delete it + if (!Directory.EnumerateFileSystemEntries(oldPath).Any()) + { + Directory.Delete(oldPath); + + // also delete its meta file if it exists + if (File.Exists(oldPath + ".meta")) + { + File.Delete(oldPath + ".meta"); + } + } + } + private void MarkAsModified() { contentIsSameAsInFilePath = null; From 20853a1029eedd985c012692cb8e73321deff427 Mon Sep 17 00:00:00 2001 From: Ilija Stankovic Date: Fri, 4 Aug 2023 13:40:07 +0200 Subject: [PATCH 02/10] Disabled saving when correcting package config path --- src/NuGetForUnity/Editor/NugetConfigFile.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NuGetForUnity/Editor/NugetConfigFile.cs b/src/NuGetForUnity/Editor/NugetConfigFile.cs index a23794a3..6b1cfd86 100644 --- a/src/NuGetForUnity/Editor/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/NugetConfigFile.cs @@ -91,7 +91,6 @@ private set { Debug.LogError("Path to packages.config in NuGet.config must be relative to the project root. Setting to default."); PackagesConfigPath = Application.dataPath; - Save(NugetHelper.NugetConfigFilePath); } PackagesConfigPath = PackagesConfigPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); From 7371f9b9403e52dbf9e2fcc9b530610ea465d39e Mon Sep 17 00:00:00 2001 From: Ilija Stankovic Date: Fri, 4 Aug 2023 15:15:32 +0200 Subject: [PATCH 03/10] Fixed relative path to packages config and brought back Save in case the user manually entered an invalid path in NuGet.config file --- src/NuGetForUnity/Editor/NugetConfigFile.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/NuGetForUnity/Editor/NugetConfigFile.cs b/src/NuGetForUnity/Editor/NugetConfigFile.cs index 6b1cfd86..56c0ebfd 100644 --- a/src/NuGetForUnity/Editor/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/NugetConfigFile.cs @@ -82,18 +82,21 @@ public string RelativePackagesConfigPath private set { - PackagesConfigPath = Path.GetFullPath(Environment.ExpandEnvironmentVariables(value)); - - var packageConfigRelativePath = NugetHelper.GetProjectRelativePath(PackagesConfigPath); - - // package config path is invalid (not under Assets), set to default - if (packageConfigRelativePath == PackagesConfigPath || packageConfigRelativePath == ".") + var tempValue = Environment.ExpandEnvironmentVariables(value); + if (tempValue == "Assets" || tempValue == "Assets/" || tempValue == "Assets\\") { - Debug.LogError("Path to packages.config in NuGet.config must be relative to the project root. Setting to default."); PackagesConfigPath = Application.dataPath; } - - PackagesConfigPath = PackagesConfigPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + else if (tempValue.StartsWith("Assets/") || tempValue.StartsWith("Assets\\")) + { + PackagesConfigPath = Path.Combine(Application.dataPath, tempValue.Substring("Assets/".Length)); + } + else + { + Debug.LogWarning($"Path to packages.config in NuGet.config must be relative to the project root (attempted path: {tempValue}). Setting to default."); + PackagesConfigPath = Application.dataPath; + Save(NugetHelper.NugetConfigFilePath); + } } } From ef4aaebef54763a0c30c92c69c4099961e62f8ef Mon Sep 17 00:00:00 2001 From: JoC0de <53140583+JoC0de@users.noreply.github.com> Date: Sun, 6 Aug 2023 19:02:01 +0200 Subject: [PATCH 04/10] GetRelativePaht with support for .. + make NugetConfigFile lazy (not null) --- .../Assets/Tests/Editor/NuGetTests.cs | 61 +++-- src/NuGetForUnity/Editor/NugetHelper.cs | 64 +++-- .../Editor/NugetPackageAssetPostprocessor.cs | 7 +- src/NuGetForUnity/Editor/PathHelper.cs | 223 ++++++++++++++++++ src/NuGetForUnity/Editor/PathHelper.cs.meta | 11 + .../Editor/UnityPreImportedLibraryResolver.cs | 5 - 6 files changed, 300 insertions(+), 71 deletions(-) create mode 100644 src/NuGetForUnity/Editor/PathHelper.cs create mode 100644 src/NuGetForUnity/Editor/PathHelper.cs.meta diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs index 8bea2c4d..043ffe9d 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs +++ b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs @@ -28,11 +28,6 @@ public void Setup() { stopwatch = Stopwatch.StartNew(); TestContext.Progress.WriteLine($"Test: {TestContext.CurrentContext.Test.FullName}"); - - if (NugetHelper.NugetConfigFile == null) - { - NugetHelper.LoadNugetConfigFile(); - } } [TearDown] @@ -290,7 +285,6 @@ public void InstallPrereleasPackage([Values] InstallMode installMode) [Test] public void InstallAndSearchLocalPackageSource([Values] bool hierarchical) { - NugetHelper.LoadNugetConfigFile(); // ensure 'NuGet.config' exists var package = new NugetPackageIdentifier("protobuf-net", "2.0.0.668") { IsManuallyInstalled = true }; var tempDirectoryPath = Path.GetFullPath(Path.Combine(TestContext.CurrentContext.TestDirectory, "TempUnitTestFolder")); Directory.CreateDirectory(tempDirectoryPath); @@ -582,8 +576,6 @@ public void TryGetBestTargetFrameworkForCurrentSettingsTest( [Test] public void TestUpgrading() { - NugetHelper.LoadNugetConfigFile(); - var componentModelAnnotation47 = new NugetPackageIdentifier("System.ComponentModel.Annotations", "4.7.0") { IsManuallyInstalled = true }; var componentModelAnnotation5 = new NugetPackageIdentifier("System.ComponentModel.Annotations", "5.0.0") { IsManuallyInstalled = true }; @@ -709,6 +701,42 @@ public void TestPostprocessDifferentVersion(string packageId, string packageVers Assert.IsTrue(NugetHelper.IsInstalled(packageNew), "The new package version was NOT installed: {0} {1}", packageNew.Id, packageNew.Version); } + [Test] + [TestCase("Assets", "Assets")] + [TestCase(".", ".")] + [TestCase("", ".")] + [TestCase("Assets/../../", "..")] + [TestCase("Assets/", "Assets/")] + [TestCase("a/b/c", "a/b/c")] + [TestCase("../../", "../..")] + [TestCase("../..", "../..")] + [TestCase("Assets/../../../", "../..")] + public void GetProjectRelativePathTest(string projectRelativePath, string expected) + { + // allow running tests on windows and linux + expected = expected.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + + var path = Path.GetFullPath(Path.Combine(NugetHelper.AbsoluteProjectPath, projectRelativePath)); + var relativePath = NugetHelper.GetProjectRelativePath(path); + + Assert.That(relativePath, Is.EqualTo(expected)); + } + + [Test] + [TestCase("Assets", "Assets")] + [TestCase("Assets/../../", "..")] + [TestCase("C:/Test", "C:/Test")] + [TestCase("./Assets", "Assets")] + [TestCase("C:/Test/", "C:/Test/")] + [TestCase("C:/Test/test.txt", "C:/Test/test.txt")] + public void GetRelativePathTest(string path, string expected) + { + // allow running tests on windows and linux + expected = expected.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + var relativePath = PathHelper.GetRelativePath(NugetHelper.AbsoluteProjectPath, path); + Assert.That(relativePath, Is.EqualTo(expected)); + } + private static void ConfigureNugetConfig(InstallMode installMode) { var nugetConfigFile = NugetHelper.NugetConfigFile; @@ -718,21 +746,4 @@ private static void ConfigureNugetConfig(InstallMode installMode) installMode == InstallMode.ApiV2Only || installMode == InstallMode.ApiV2AllowCached; nugetConfigFile.InstallFromCache = installMode == InstallMode.ApiV2AllowCached; } - - [Test] - [TestCase("", "Assets")] - [TestCase("../", ".")] - [TestCase("/../../", null)] - [TestCase("a/b/c", "Assets/a/b/c")] - public void PathTest(string append, string expected) - { - var path = Path.GetFullPath(Path.Combine(Application.dataPath, append)); - var relativePath = NugetHelper.GetProjectRelativePath(path); - if (expected == null) - { - expected = path; - } - - Assert.That(relativePath, Is.EqualTo(expected)); - } } diff --git a/src/NuGetForUnity/Editor/NugetHelper.cs b/src/NuGetForUnity/Editor/NugetHelper.cs index c039c4b4..8372da52 100644 --- a/src/NuGetForUnity/Editor/NugetHelper.cs +++ b/src/NuGetForUnity/Editor/NugetHelper.cs @@ -55,6 +55,11 @@ public static class NugetHelper /// private static PackagesConfigFile packagesConfigFile; + /// + /// Backing field for the NuGet.config file. + /// + private static NugetConfigFile nugetConfigFile; + /// /// The to use. /// @@ -78,7 +83,18 @@ static NugetHelper() /// /// Gets the loaded NuGet.config file that holds the settings for NuGet. /// - public static NugetConfigFile NugetConfigFile { get; private set; } + public static NugetConfigFile NugetConfigFile + { + get + { + if (nugetConfigFile is null) + { + LoadNugetConfigFile(); + } + + return nugetConfigFile; + } + } /// /// Gets the loaded packages.config file that hold the dependencies for the project. @@ -117,6 +133,16 @@ private static Dictionary InstalledPackagesDictionary } } + /// + /// Returns the path relative to project directory, or "." if it is the project directory. + /// + /// The path of witch we calculate the relative path of. + /// The path relative to project directory, or "." if it is the project directory. + public static string GetProjectRelativePath(string path) + { + return PathHelper.GetRelativePath(AbsoluteProjectPath, path); + } + /// /// Invalidates the currently loaded 'packages.config' so it is reloaded when it is accessed the next time. /// @@ -132,13 +158,13 @@ public static void LoadNugetConfigFile() { if (File.Exists(NugetConfigFilePath)) { - NugetConfigFile = NugetConfigFile.Load(NugetConfigFilePath); + nugetConfigFile = NugetConfigFile.Load(NugetConfigFilePath); } else { Debug.LogFormat("No NuGet.config file found. Creating default at {0}", NugetConfigFilePath); - NugetConfigFile = NugetConfigFile.CreateDefaultFile(NugetConfigFilePath); + nugetConfigFile = NugetConfigFile.CreateDefaultFile(NugetConfigFilePath); } // parse any command line arguments @@ -855,8 +881,6 @@ internal static void SetManuallyInstalledFlag(INugetPackageIdentifier package) /// public static void UpdateInstalledPackages() { - LoadNugetConfigFile(); - var stopwatch = new Stopwatch(); stopwatch.Start(); @@ -1580,35 +1604,5 @@ internal static bool IsInstalled(INugetPackageIdentifier package) return isInstalled; } - - /// - /// Returns the path relative to project directory, or path if it's not relative to project directory. - /// Should the project upgrade to .NET Standard 2.1, this will be removed and instead we will use Path.GetRelativePath(relativeTo, path); - /// - /// The destination path. - public static string GetProjectRelativePath(string absolutePath) - { - var projectFolder = (Path.GetDirectoryName(Application.dataPath) ?? string.Empty).Replace('\\', '/'); - - if (string.IsNullOrEmpty(projectFolder) || string.IsNullOrEmpty(absolutePath) || !Path.IsPathRooted(absolutePath)) - { - return absolutePath; - } - - var path = absolutePath.Replace('\\', '/'); - - if (!path.StartsWith(projectFolder, StringComparison.Ordinal)) - { - return absolutePath; - } - - var projectFolderLength = projectFolder.Length; - if (path.Length > projectFolderLength && path[projectFolderLength] == '/') - { - projectFolderLength++; - } - - return path.Length > projectFolderLength ? path.Substring(projectFolderLength) : "."; - } } } diff --git a/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs b/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs index e4f19fc6..1b4221bd 100644 --- a/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs +++ b/src/NuGetForUnity/Editor/NugetPackageAssetPostprocessor.cs @@ -175,7 +175,7 @@ private static string[] GetPathComponents(string path) private static bool AssetIsDllInsideNuGetRepository(string absoluteAssetPath, string absoluteRepositoryPath) { - return absoluteAssetPath.StartsWith(absoluteRepositoryPath, StringComparison.OrdinalIgnoreCase) && + return absoluteAssetPath.StartsWith(absoluteRepositoryPath, PathHelper.PathComparisonType) && absoluteAssetPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && File.Exists(absoluteAssetPath); } @@ -186,11 +186,6 @@ private static bool AssetIsDllInsideNuGetRepository(string absoluteAssetPath, st /// The absolute path where NuGetForUnity restores NuGet packages, with trailing directory separator. private static string GetNuGetRepositoryPath() { - if (NugetHelper.NugetConfigFile == null) - { - NugetHelper.LoadNugetConfigFile(); - } - return NugetHelper.NugetConfigFile.RepositoryPath + Path.DirectorySeparatorChar; } diff --git a/src/NuGetForUnity/Editor/PathHelper.cs b/src/NuGetForUnity/Editor/PathHelper.cs new file mode 100644 index 00000000..ed474346 --- /dev/null +++ b/src/NuGetForUnity/Editor/PathHelper.cs @@ -0,0 +1,223 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using UnityEngine; + +/// +/// Helper class to handle path operations. +/// +internal static class PathHelper +{ + /// + /// The path comparison type to use for the current platform the editor is running on. + /// + internal static readonly StringComparison PathComparisonType = + Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.OSXEditor ? + StringComparison.OrdinalIgnoreCase : + StringComparison.Ordinal; + + /// + /// Create a relative path from one path to another. Paths will be resolved before calculating the difference. + /// Default path comparison for the active platform will be used (OrdinalIgnoreCase for Windows or Mac, Ordinal for Unix). + /// + /// + /// Should the project upgrade to Unity version that has .NET Standard 2.1, this can be removed and instead we will use: + /// Path.GetRelativePath(relativeTo, path). + /// + /// . + /// + /// The source path the output should be relative to. This path is always considered to be a directory. + /// The destination path. + /// The relative path or if the paths don't share the same root. + internal static string GetRelativePath(string relativeTo, string path) + { + if (string.IsNullOrEmpty(relativeTo)) + { + throw new ArgumentNullException(nameof(relativeTo)); + } + + if (string.IsNullOrEmpty(path)) + { + return path; + } + + relativeTo = Path.GetFullPath(relativeTo); + path = Path.GetFullPath(path); + + // Need to check if the roots are different- if they are we need to return the "to" path. + if (!AreRootsEqual(relativeTo, path)) + { + return path; + } + + var commonLength = GetCommonPathLength(relativeTo, path, PathComparisonType == StringComparison.OrdinalIgnoreCase); + + // If there is nothing in common they can't share the same root, return the "to" path as is. + if (commonLength == 0) + { + return path; + } + + // Trailing separators aren't significant for comparison + var relativeToLength = relativeTo.Length; + if (EndsInDirectorySeparator(relativeTo)) + { + relativeToLength--; + } + + var pathEndsInSeparator = EndsInDirectorySeparator(path); + var pathLength = path.Length; + if (pathEndsInSeparator) + { + pathLength--; + } + + // If we have effectively the same path, return "." + if (relativeToLength == pathLength && commonLength >= relativeToLength) + { + return "."; + } + + // We have the same root, we need to calculate the difference now using the + // common Length and Segment count past the length. + // + // Some examples: + // + // C:\Foo C:\Bar L3, S1 -> ..\Bar + // C:\Foo C:\Foo\Bar L6, S0 -> Bar + // C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar + // C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar + var sb = new StringBuilder(); + sb.EnsureCapacity(Math.Max(relativeTo.Length, path.Length)); + + // Add parent segments for segments past the common on the "from" path + if (commonLength < relativeToLength) + { + sb.Append(".."); + + for (var i = commonLength + 1; i < relativeToLength; i++) + { + if (IsDirectorySeparator(relativeTo[i])) + { + sb.Append(Path.DirectorySeparatorChar); + sb.Append(".."); + } + } + } + else if (IsDirectorySeparator(path[commonLength])) + { + // No parent segments and we need to eat the initial separator + // (C:\Foo C:\Foo\Bar case) + commonLength++; + } + + // Now add the rest of the "to" path, adding back the trailing separator + var differenceLength = pathLength - commonLength; + if (pathEndsInSeparator) + { + differenceLength++; + } + + if (differenceLength > 0) + { + if (sb.Length > 0) + { + sb.Append(Path.DirectorySeparatorChar); + } + + sb.Append(path.Substring(commonLength, differenceLength)); + } + + return sb.ToString(); + } + + /// + /// Get the common path length from the start of the string. + /// + private static int GetCommonPathLength(string first, string second, bool ignoreCase) + { + var commonChars = EqualStartingCharacterCount(first, second, ignoreCase); + + // If nothing matches + if (commonChars == 0) + { + return commonChars; + } + + // Or we're a full string and equal length or match to a separator + if (commonChars == first.Length && (commonChars == second.Length || IsDirectorySeparator(second[commonChars]))) + { + return commonChars; + } + + if (commonChars == second.Length && IsDirectorySeparator(first[commonChars])) + { + return commonChars; + } + + // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar. + while (commonChars > 0 && !IsDirectorySeparator(first[commonChars - 1])) + { + commonChars--; + } + + return commonChars; + } + + /// + /// Gets the count of common characters from the left optionally ignoring case + /// + private static int EqualStartingCharacterCount(string first, string second, bool ignoreCase) + { + if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) + { + return 0; + } + + var commonChars = 0; + for (; commonChars < first.Length && commonChars < second.Length; ++commonChars) + { + if (first[commonChars] == second[commonChars] || + (ignoreCase && char.ToUpperInvariant(first[commonChars]) == char.ToUpperInvariant(second[commonChars]))) + { + continue; + } + + break; + } + + return commonChars; + } + + /// + /// Returns true if the two paths have the same root. + /// + private static bool AreRootsEqual(string first, string second) + { + var firstRoot = Path.GetPathRoot(first); + var secondRoot = Path.GetPathRoot(second); + + return string.Equals(firstRoot, secondRoot, PathComparisonType); + } + + /// + /// True if the given character is a directory separator. + /// + /// The char to compare. + /// True if is a separator. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsDirectorySeparator(char c) + { + return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar; + } + + /// + /// Returns true if the path ends in a directory separator. + /// + private static bool EndsInDirectorySeparator(string path) + { + return !string.IsNullOrEmpty(path) && IsDirectorySeparator(path[path.Length - 1]); + } +} diff --git a/src/NuGetForUnity/Editor/PathHelper.cs.meta b/src/NuGetForUnity/Editor/PathHelper.cs.meta new file mode 100644 index 00000000..6aeb9458 --- /dev/null +++ b/src/NuGetForUnity/Editor/PathHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d0f357dc817cff428024bc195921e57 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/NuGetForUnity/Editor/UnityPreImportedLibraryResolver.cs b/src/NuGetForUnity/Editor/UnityPreImportedLibraryResolver.cs index c63213af..3db9d322 100644 --- a/src/NuGetForUnity/Editor/UnityPreImportedLibraryResolver.cs +++ b/src/NuGetForUnity/Editor/UnityPreImportedLibraryResolver.cs @@ -28,11 +28,6 @@ internal static HashSet GetAlreadyImportedLibs() // Find all the assemblies already installed by NuGetForUnity var alreadyInstalledDllFileNames = new HashSet(); - if (NugetHelper.NugetConfigFile == null) - { - NugetHelper.LoadNugetConfigFile(); - } - if (Directory.Exists(NugetHelper.NugetConfigFile.RepositoryPath)) { alreadyInstalledDllFileNames = new HashSet( From 50b340b3953d9a67b60b8e09331fbca55f9f72f0 Mon Sep 17 00:00:00 2001 From: Ilija Stankovic Date: Mon, 7 Aug 2023 16:22:12 +0200 Subject: [PATCH 05/10] Fixed comments and set package.config path to be relative to NuGet.config file's directory by default --- src/NuGetForUnity.Tests/Assets/NuGet.config | 2 +- .../Assets/Tests/Editor/NuGetTests.cs | 27 +- .../Assets/Tests/Resources/NuGet.config | 2 +- src/NuGetForUnity/Editor/NugetConfigFile.cs | 27 +- src/NuGetForUnity/Editor/NugetHelper.cs | 10 +- src/NuGetForUnity/Editor/NugetPreferences.cs | 28 +- .../Editor/PackagesConfigFile.cs | 63 ++-- src/NuGetForUnity/Editor/PathHelper.cs | 343 +++++++++--------- 8 files changed, 238 insertions(+), 264 deletions(-) diff --git a/src/NuGetForUnity.Tests/Assets/NuGet.config b/src/NuGetForUnity.Tests/Assets/NuGet.config index a948d6d1..7242880e 100644 --- a/src/NuGetForUnity.Tests/Assets/NuGet.config +++ b/src/NuGetForUnity.Tests/Assets/NuGet.config @@ -12,7 +12,7 @@ - + \ No newline at end of file diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs index 043ffe9d..b6a7c239 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs +++ b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs @@ -704,31 +704,16 @@ public void TestPostprocessDifferentVersion(string packageId, string packageVers [Test] [TestCase("Assets", "Assets")] [TestCase(".", ".")] - [TestCase("", ".")] + [TestCase("", "")] [TestCase("Assets/../../", "..")] - [TestCase("Assets/", "Assets/")] + [TestCase("Assets/../../../", "../..")] + [TestCase("M:/Test", "M:/Test")] + [TestCase("./Assets", "Assets")] [TestCase("a/b/c", "a/b/c")] [TestCase("../../", "../..")] [TestCase("../..", "../..")] - [TestCase("Assets/../../../", "../..")] - public void GetProjectRelativePathTest(string projectRelativePath, string expected) - { - // allow running tests on windows and linux - expected = expected.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); - - var path = Path.GetFullPath(Path.Combine(NugetHelper.AbsoluteProjectPath, projectRelativePath)); - var relativePath = NugetHelper.GetProjectRelativePath(path); - - Assert.That(relativePath, Is.EqualTo(expected)); - } - - [Test] - [TestCase("Assets", "Assets")] - [TestCase("Assets/../../", "..")] - [TestCase("C:/Test", "C:/Test")] - [TestCase("./Assets", "Assets")] - [TestCase("C:/Test/", "C:/Test/")] - [TestCase("C:/Test/test.txt", "C:/Test/test.txt")] + [TestCase("M:/Test/", "M:/Test/")] + [TestCase("M:/Test/test.txt", "M:/Test/test.txt")] public void GetRelativePathTest(string path, string expected) { // allow running tests on windows and linux diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config b/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config index 889800d9..c5d0f689 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config +++ b/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config @@ -16,7 +16,7 @@ - + \ No newline at end of file diff --git a/src/NuGetForUnity/Editor/NugetConfigFile.cs b/src/NuGetForUnity/Editor/NugetConfigFile.cs index 56c0ebfd..fda6f0eb 100644 --- a/src/NuGetForUnity/Editor/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/NugetConfigFile.cs @@ -74,30 +74,13 @@ public class NugetConfigFile /// public bool ReadOnlyPackageFiles { get; set; } - public string PackagesConfigPath; + public string PackagesConfigPath { get; set; } public string RelativePackagesConfigPath { - get => NugetHelper.GetProjectRelativePath(PackagesConfigPath); + get => NugetHelper.GetAssetsRelativePath(PackagesConfigPath); - private set - { - var tempValue = Environment.ExpandEnvironmentVariables(value); - if (tempValue == "Assets" || tempValue == "Assets/" || tempValue == "Assets\\") - { - PackagesConfigPath = Application.dataPath; - } - else if (tempValue.StartsWith("Assets/") || tempValue.StartsWith("Assets\\")) - { - PackagesConfigPath = Path.Combine(Application.dataPath, tempValue.Substring("Assets/".Length)); - } - else - { - Debug.LogWarning($"Path to packages.config in NuGet.config must be relative to the project root (attempted path: {tempValue}). Setting to default."); - PackagesConfigPath = Application.dataPath; - Save(NugetHelper.NugetConfigFilePath); - } - } + private set => PackagesConfigPath = Path.GetFullPath(Path.Combine(Application.dataPath, value)); } /// @@ -269,7 +252,7 @@ public static NugetConfigFile Load(string filePath) configFile.PackageSources = new List(); configFile.InstallFromCache = true; configFile.ReadOnlyPackageFiles = false; - configFile.RelativePackagesConfigPath = "Assets"; + configFile.RelativePackagesConfigPath = "."; var file = XDocument.Load(filePath); @@ -421,7 +404,7 @@ public static NugetConfigFile CreateDefaultFile(string filePath) - + "; diff --git a/src/NuGetForUnity/Editor/NugetHelper.cs b/src/NuGetForUnity/Editor/NugetHelper.cs index 8372da52..12cd1f3c 100644 --- a/src/NuGetForUnity/Editor/NugetHelper.cs +++ b/src/NuGetForUnity/Editor/NugetHelper.cs @@ -134,13 +134,13 @@ private static Dictionary InstalledPackagesDictionary } /// - /// Returns the path relative to project directory, or "." if it is the project directory. + /// Returns the path relative to Assets directory, or "." if it is the Assets directory. /// /// The path of witch we calculate the relative path of. - /// The path relative to project directory, or "." if it is the project directory. - public static string GetProjectRelativePath(string path) + /// The path relative to Assets directory, or "." if it is the Assets directory. + public static string GetAssetsRelativePath(string path) { - return PathHelper.GetRelativePath(AbsoluteProjectPath, path); + return PathHelper.GetRelativePath(Application.dataPath, path); } /// @@ -1170,7 +1170,7 @@ internal static bool InstallIdentifier(INugetPackageIdentifier package, bool ref /// The arguments for the formatted message string. public static void LogVerbose(string format, params object[] args) { - if (NugetConfigFile != null && !NugetConfigFile.Verbose) + if (nugetConfigFile != null && !nugetConfigFile.Verbose) { return; } diff --git a/src/NuGetForUnity/Editor/NugetPreferences.cs b/src/NuGetForUnity/Editor/NugetPreferences.cs index 236a42c8..02eda73f 100644 --- a/src/NuGetForUnity/Editor/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/NugetPreferences.cs @@ -48,11 +48,6 @@ public override void OnGUI(string searchContext) EditorGUILayout.LabelField(string.Format("Version: {0}", NuGetForUnityVersion)); - if (NugetHelper.NugetConfigFile == null) - { - NugetHelper.LoadNugetConfigFile(); - } - var installFromCache = EditorGUILayout.Toggle("Install From the Cache", NugetHelper.NugetConfigFile.InstallFromCache); if (installFromCache != NugetHelper.NugetConfigFile.InstallFromCache) { @@ -76,27 +71,26 @@ public override void OnGUI(string searchContext) EditorGUILayout.BeginHorizontal(); { - var packagesConfigPath = NugetHelper.NugetConfigFile.RelativePackagesConfigPath; - EditorGUILayout.LabelField($"Packages Config path: {packagesConfigPath}"); - packagesConfigPath = Path.GetFullPath(packagesConfigPath); + var packagesConfigPath = NugetHelper.NugetConfigFile.PackagesConfigPath; + EditorGUILayout.LabelField( + new GUIContent($"Packages Config path: {NugetHelper.NugetConfigFile.RelativePackagesConfigPath}", $"Absolute path: {packagesConfigPath}")); if (GUILayout.Button("Browse")) { var newPath = EditorUtility.OpenFolderPanel("Select Folder", packagesConfigPath, ""); if (!string.IsNullOrEmpty(newPath) && newPath != packagesConfigPath) { - var pathCheck = NugetHelper.GetProjectRelativePath(newPath); + var pathCheck = NugetHelper.GetAssetsRelativePath(newPath); - // make sure the path is within Assets directory - if (!pathCheck.StartsWith("Assets")) + // if the path root is different or it is not under Assets folder, we issue a warning + if (pathCheck == newPath || pathCheck.StartsWith("..")) { - Debug.LogError("packages.config path has to be within /Assets."); - } - else - { - PackagesConfigFile.Move(newPath); - preferencesChangedThisFrame = true; + Debug.LogWarning( + "Putting packages.config outside of Assets folder disables the functionality of restoring packages if the file is changed on the disk."); } + + PackagesConfigFile.Move(newPath); + preferencesChangedThisFrame = true; } } } diff --git a/src/NuGetForUnity/Editor/PackagesConfigFile.cs b/src/NuGetForUnity/Editor/PackagesConfigFile.cs index 1c11bfd7..c63b463d 100644 --- a/src/NuGetForUnity/Editor/PackagesConfigFile.cs +++ b/src/NuGetForUnity/Editor/PackagesConfigFile.cs @@ -215,48 +215,57 @@ public void Save() contentIsSameAsInFilePath = filePath; } + /// + /// Moves the packages.config file and its corresponding meta file to the given path. If there is no packages.config file on the current + /// path, makes a default version of the file on the new path. + /// + /// Path to move packages.config file to. internal static void Move(string newPath) { var oldFilePath = NugetHelper.NugetConfigFile.PackagesConfigFilePath; var oldPath = NugetHelper.NugetConfigFile.PackagesConfigPath; NugetHelper.NugetConfigFile.PackagesConfigPath = newPath; - if (!File.Exists(oldFilePath)) + try { - Debug.LogFormat("No packages.config file found. Creating default at {0}", newPath); - var configFile = new PackagesConfigFile { Packages = new List() }; - configFile.Save(); - AssetDatabase.Refresh(); - return; - } + if (!File.Exists(oldFilePath)) + { + Debug.LogFormat("No packages.config file found. Creating default at {0}", newPath); + var configFile = new PackagesConfigFile { Packages = new List() }; + configFile.Save(); + AssetDatabase.Refresh(); + return; + } - var newFilePath = Path.GetFullPath(Path.Combine(newPath, FileName)); + var newFilePath = Path.GetFullPath(Path.Combine(newPath, FileName)); - // creating directory if it doesn't exist - if (!Directory.Exists(newPath)) - { Directory.CreateDirectory(newPath); - } - - // moving config to the new path - File.Move(oldFilePath, newFilePath); - // manually moving meta file to suppress Unity warning - if (File.Exists(oldFilePath + ".meta")) - { - File.Move(oldFilePath + ".meta", newFilePath + ".meta"); - } + // moving config to the new path + File.Move(oldFilePath, newFilePath); - // if the old path is now an empty directory, delete it - if (!Directory.EnumerateFileSystemEntries(oldPath).Any()) - { - Directory.Delete(oldPath); + // manually moving meta file to suppress Unity warning + if (File.Exists($"{oldFilePath}.meta")) + { + File.Move($"{oldFilePath}.meta", $"{newFilePath}.meta"); + } - // also delete its meta file if it exists - if (File.Exists(oldPath + ".meta")) + // if the old path is now an empty directory, delete it + if (!Directory.EnumerateFileSystemEntries(oldPath).Any()) { - File.Delete(oldPath + ".meta"); + Directory.Delete(oldPath); + + // also delete its meta file if it exists + if (File.Exists($"{oldPath}.meta")) + { + File.Delete($"{oldPath}.meta"); + } } } + catch (Exception e) + { + Debug.LogException(e); + NugetHelper.NugetConfigFile.PackagesConfigPath = oldPath; + } } private void MarkAsModified() diff --git a/src/NuGetForUnity/Editor/PathHelper.cs b/src/NuGetForUnity/Editor/PathHelper.cs index ed474346..266ed57d 100644 --- a/src/NuGetForUnity/Editor/PathHelper.cs +++ b/src/NuGetForUnity/Editor/PathHelper.cs @@ -4,220 +4,223 @@ using System.Text; using UnityEngine; -/// -/// Helper class to handle path operations. -/// -internal static class PathHelper +namespace NugetForUnity { /// - /// The path comparison type to use for the current platform the editor is running on. + /// Helper class to handle path operations. /// - internal static readonly StringComparison PathComparisonType = - Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.OSXEditor ? - StringComparison.OrdinalIgnoreCase : - StringComparison.Ordinal; - - /// - /// Create a relative path from one path to another. Paths will be resolved before calculating the difference. - /// Default path comparison for the active platform will be used (OrdinalIgnoreCase for Windows or Mac, Ordinal for Unix). - /// - /// - /// Should the project upgrade to Unity version that has .NET Standard 2.1, this can be removed and instead we will use: - /// Path.GetRelativePath(relativeTo, path). - /// - /// . - /// - /// The source path the output should be relative to. This path is always considered to be a directory. - /// The destination path. - /// The relative path or if the paths don't share the same root. - internal static string GetRelativePath(string relativeTo, string path) + internal static class PathHelper { - if (string.IsNullOrEmpty(relativeTo)) - { - throw new ArgumentNullException(nameof(relativeTo)); - } + /// + /// The path comparison type to use for the current platform the editor is running on. + /// + internal static readonly StringComparison PathComparisonType = + Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.OSXEditor ? + StringComparison.OrdinalIgnoreCase : + StringComparison.Ordinal; + + /// + /// Create a relative path from one path to another. Paths will be resolved before calculating the difference. + /// Default path comparison for the active platform will be used (OrdinalIgnoreCase for Windows or Mac, Ordinal for Unix). + /// + /// + /// Should the project upgrade to Unity version that has .NET Standard 2.1, this can be removed and instead we will use: + /// Path.GetRelativePath(relativeTo, path). + /// + /// . + /// + /// The source path the output should be relative to. This path is always considered to be a directory. + /// The destination path. + /// The relative path or if the paths don't share the same root. + internal static string GetRelativePath(string relativeTo, string path) + { + if (string.IsNullOrEmpty(relativeTo)) + { + throw new ArgumentNullException(nameof(relativeTo)); + } - if (string.IsNullOrEmpty(path)) - { - return path; - } + if (string.IsNullOrEmpty(path)) + { + return path; + } - relativeTo = Path.GetFullPath(relativeTo); - path = Path.GetFullPath(path); + relativeTo = Path.GetFullPath(relativeTo); + path = Path.GetFullPath(path); - // Need to check if the roots are different- if they are we need to return the "to" path. - if (!AreRootsEqual(relativeTo, path)) - { - return path; - } + // Need to check if the roots are different - if they are we need to return the "to" path. + if (!AreRootsEqual(relativeTo, path)) + { + return path; + } - var commonLength = GetCommonPathLength(relativeTo, path, PathComparisonType == StringComparison.OrdinalIgnoreCase); + var commonLength = GetCommonPathLength(relativeTo, path, PathComparisonType == StringComparison.OrdinalIgnoreCase); - // If there is nothing in common they can't share the same root, return the "to" path as is. - if (commonLength == 0) - { - return path; - } + // If there is nothing in common they can't share the same root, return the "to" path as is. + if (commonLength == 0) + { + return path; + } - // Trailing separators aren't significant for comparison - var relativeToLength = relativeTo.Length; - if (EndsInDirectorySeparator(relativeTo)) - { - relativeToLength--; - } + // Trailing separators aren't significant for comparison + var relativeToLength = relativeTo.Length; + if (EndsInDirectorySeparator(relativeTo)) + { + relativeToLength--; + } - var pathEndsInSeparator = EndsInDirectorySeparator(path); - var pathLength = path.Length; - if (pathEndsInSeparator) - { - pathLength--; - } + var pathEndsInSeparator = EndsInDirectorySeparator(path); + var pathLength = path.Length; + if (pathEndsInSeparator) + { + pathLength--; + } - // If we have effectively the same path, return "." - if (relativeToLength == pathLength && commonLength >= relativeToLength) - { - return "."; - } + // If we have effectively the same path, return "." + if (relativeToLength == pathLength && commonLength >= relativeToLength) + { + return "."; + } - // We have the same root, we need to calculate the difference now using the - // common Length and Segment count past the length. - // - // Some examples: - // - // C:\Foo C:\Bar L3, S1 -> ..\Bar - // C:\Foo C:\Foo\Bar L6, S0 -> Bar - // C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar - // C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar - var sb = new StringBuilder(); - sb.EnsureCapacity(Math.Max(relativeTo.Length, path.Length)); - - // Add parent segments for segments past the common on the "from" path - if (commonLength < relativeToLength) - { - sb.Append(".."); + // We have the same root, we need to calculate the difference now using the + // common Length and Segment count past the length. + // + // Some examples: + // + // C:\Foo C:\Bar L3, S1 -> ..\Bar + // C:\Foo C:\Foo\Bar L6, S0 -> Bar + // C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar + // C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar + var sb = new StringBuilder(); + sb.EnsureCapacity(Math.Max(relativeTo.Length, path.Length)); + + // Add parent segments for segments past the common on the "from" path + if (commonLength < relativeToLength) + { + sb.Append(".."); + + for (var i = commonLength + 1; i < relativeToLength; i++) + { + if (IsDirectorySeparator(relativeTo[i])) + { + sb.Append(Path.DirectorySeparatorChar); + sb.Append(".."); + } + } + } + else if (IsDirectorySeparator(path[commonLength])) + { + // No parent segments and we need to eat the initial separator + // (C:\Foo C:\Foo\Bar case) + commonLength++; + } + + // Now add the rest of the "to" path, adding back the trailing separator + var differenceLength = pathLength - commonLength; + if (pathEndsInSeparator) + { + differenceLength++; + } - for (var i = commonLength + 1; i < relativeToLength; i++) + if (differenceLength > 0) { - if (IsDirectorySeparator(relativeTo[i])) + if (sb.Length > 0) { sb.Append(Path.DirectorySeparatorChar); - sb.Append(".."); } + + sb.Append(path.Substring(commonLength, differenceLength)); } - } - else if (IsDirectorySeparator(path[commonLength])) - { - // No parent segments and we need to eat the initial separator - // (C:\Foo C:\Foo\Bar case) - commonLength++; - } - // Now add the rest of the "to" path, adding back the trailing separator - var differenceLength = pathLength - commonLength; - if (pathEndsInSeparator) - { - differenceLength++; + return sb.ToString(); } - if (differenceLength > 0) + /// + /// Get the common path length from the start of the string. + /// + private static int GetCommonPathLength(string first, string second, bool ignoreCase) { - if (sb.Length > 0) + var commonChars = EqualStartingCharacterCount(first, second, ignoreCase); + + // If nothing matches + if (commonChars == 0) { - sb.Append(Path.DirectorySeparatorChar); + return commonChars; } - sb.Append(path.Substring(commonLength, differenceLength)); - } + // Or we're a full string and equal length or match to a separator + if (commonChars == first.Length && (commonChars == second.Length || IsDirectorySeparator(second[commonChars]))) + { + return commonChars; + } - return sb.ToString(); - } + if (commonChars == second.Length && IsDirectorySeparator(first[commonChars])) + { + return commonChars; + } - /// - /// Get the common path length from the start of the string. - /// - private static int GetCommonPathLength(string first, string second, bool ignoreCase) - { - var commonChars = EqualStartingCharacterCount(first, second, ignoreCase); + // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar. + while (commonChars > 0 && !IsDirectorySeparator(first[commonChars - 1])) + { + commonChars--; + } - // If nothing matches - if (commonChars == 0) - { return commonChars; } - // Or we're a full string and equal length or match to a separator - if (commonChars == first.Length && (commonChars == second.Length || IsDirectorySeparator(second[commonChars]))) + /// + /// Gets the count of common characters from the left optionally ignoring case + /// + private static int EqualStartingCharacterCount(string first, string second, bool ignoreCase) { - return commonChars; - } + if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) + { + return 0; + } + + var commonChars = 0; + for (; commonChars < first.Length && commonChars < second.Length; ++commonChars) + { + if (first[commonChars] == second[commonChars] || + (ignoreCase && char.ToUpperInvariant(first[commonChars]) == char.ToUpperInvariant(second[commonChars]))) + { + continue; + } + + break; + } - if (commonChars == second.Length && IsDirectorySeparator(first[commonChars])) - { return commonChars; } - // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar. - while (commonChars > 0 && !IsDirectorySeparator(first[commonChars - 1])) + /// + /// Returns true if the two paths have the same root. + /// + private static bool AreRootsEqual(string first, string second) { - commonChars--; - } + var firstRoot = Path.GetPathRoot(first); + var secondRoot = Path.GetPathRoot(second); - return commonChars; - } + return string.Equals(firstRoot, secondRoot, PathComparisonType); + } - /// - /// Gets the count of common characters from the left optionally ignoring case - /// - private static int EqualStartingCharacterCount(string first, string second, bool ignoreCase) - { - if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) + /// + /// True if the given character is a directory separator. + /// + /// The char to compare. + /// True if is a separator. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsDirectorySeparator(char c) { - return 0; + return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar; } - var commonChars = 0; - for (; commonChars < first.Length && commonChars < second.Length; ++commonChars) + /// + /// Returns true if the path ends in a directory separator. + /// + private static bool EndsInDirectorySeparator(string path) { - if (first[commonChars] == second[commonChars] || - (ignoreCase && char.ToUpperInvariant(first[commonChars]) == char.ToUpperInvariant(second[commonChars]))) - { - continue; - } - - break; + return !string.IsNullOrEmpty(path) && IsDirectorySeparator(path[path.Length - 1]); } - - return commonChars; - } - - /// - /// Returns true if the two paths have the same root. - /// - private static bool AreRootsEqual(string first, string second) - { - var firstRoot = Path.GetPathRoot(first); - var secondRoot = Path.GetPathRoot(second); - - return string.Equals(firstRoot, secondRoot, PathComparisonType); - } - - /// - /// True if the given character is a directory separator. - /// - /// The char to compare. - /// True if is a separator. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsDirectorySeparator(char c) - { - return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar; - } - - /// - /// Returns true if the path ends in a directory separator. - /// - private static bool EndsInDirectorySeparator(string path) - { - return !string.IsNullOrEmpty(path) && IsDirectorySeparator(path[path.Length - 1]); } } From 4f1af65e2dd1cd6d6e6ca9261129f54058e4c9d4 Mon Sep 17 00:00:00 2001 From: Ilija Stankovic Date: Mon, 7 Aug 2023 16:26:18 +0200 Subject: [PATCH 06/10] Fixed the path in NuGet.config in Tests project to default --- src/NuGetForUnity.Tests/Assets/NuGet.config | 3 ++- src/NuGetForUnity.Tests/Assets/packages.config | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/NuGetForUnity.Tests/Assets/NuGet.config b/src/NuGetForUnity.Tests/Assets/NuGet.config index 7242880e..3929afc2 100644 --- a/src/NuGetForUnity.Tests/Assets/NuGet.config +++ b/src/NuGetForUnity.Tests/Assets/NuGet.config @@ -4,15 +4,16 @@ + + - \ No newline at end of file diff --git a/src/NuGetForUnity.Tests/Assets/packages.config b/src/NuGetForUnity.Tests/Assets/packages.config index 8f2c3662..3299dfcf 100644 --- a/src/NuGetForUnity.Tests/Assets/packages.config +++ b/src/NuGetForUnity.Tests/Assets/packages.config @@ -1,2 +1,2 @@ - - + + \ No newline at end of file From 2cfe0b8cae5e93e3b0933ef3c323df3fcc1ba610 Mon Sep 17 00:00:00 2001 From: Ilija Stankovic Date: Tue, 8 Aug 2023 10:10:59 +0200 Subject: [PATCH 07/10] Fixed CLI compilation errors --- src/NuGetForUnity.Cli/Fakes/Application.cs | 16 ++++++++++++++++ src/NuGetForUnity.Cli/Fakes/Debug.cs | 6 ++++++ src/NuGetForUnity.Cli/Fakes/RuntimePlatform.cs | 12 ++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 src/NuGetForUnity.Cli/Fakes/RuntimePlatform.cs diff --git a/src/NuGetForUnity.Cli/Fakes/Application.cs b/src/NuGetForUnity.Cli/Fakes/Application.cs index 23d08b19..721f7107 100644 --- a/src/NuGetForUnity.Cli/Fakes/Application.cs +++ b/src/NuGetForUnity.Cli/Fakes/Application.cs @@ -20,6 +20,22 @@ internal static class Application public static ApiCompatibilityLevel ApiCompatibilityLevel { get; private set; } + public static RuntimePlatform platform + { + get + { + switch (Environment.OSVersion.Platform) + { + case PlatformID.MacOSX: + return RuntimePlatform.OSXEditor; + case PlatformID.Unix: + return RuntimePlatform.LinuxEditor; + default: + return RuntimePlatform.WindowsEditor; + } + } + } + internal static StackTraceLogType GetStackTraceLogType(LogType log) { return StackTraceLogType.None; diff --git a/src/NuGetForUnity.Cli/Fakes/Debug.cs b/src/NuGetForUnity.Cli/Fakes/Debug.cs index 0ac9c69e..abef7085 100644 --- a/src/NuGetForUnity.Cli/Fakes/Debug.cs +++ b/src/NuGetForUnity.Cli/Fakes/Debug.cs @@ -43,6 +43,12 @@ internal static void LogError(string format, params object[] args) Console.Error.WriteLine(format, args); } + internal static void LogException(Exception e) + { + HasError = true; + Console.Error.WriteLine(e.ToString()); + } + internal static void Assert(bool condition) { System.Diagnostics.Debug.Assert(condition); diff --git a/src/NuGetForUnity.Cli/Fakes/RuntimePlatform.cs b/src/NuGetForUnity.Cli/Fakes/RuntimePlatform.cs new file mode 100644 index 00000000..78461fe3 --- /dev/null +++ b/src/NuGetForUnity.Cli/Fakes/RuntimePlatform.cs @@ -0,0 +1,12 @@ +#nullable enable +namespace UnityEngine +{ + public enum RuntimePlatform + { + OSXEditor = 0, + + WindowsEditor = 7, + + LinuxEditor = 16 + } +} From 1c694853aeeb62c9e37d92eff47ee23192b3c78d Mon Sep 17 00:00:00 2001 From: Ilija Stankovic Date: Wed, 9 Aug 2023 14:29:26 +0200 Subject: [PATCH 08/10] Fixed naming and added showing warning in preferences if packages.config file path is outside of Assets instead of logging --- src/NuGetForUnity.Tests/Assets/NuGet.config | 2 +- .../Assets/Tests/Resources/NuGet.config | 2 +- src/NuGetForUnity/Editor/NugetConfigFile.cs | 20 +++++++++++-------- src/NuGetForUnity/Editor/NugetPreferences.cs | 20 ++++++++++++------- .../Editor/PackagesConfigFile.cs | 6 +++--- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/NuGetForUnity.Tests/Assets/NuGet.config b/src/NuGetForUnity.Tests/Assets/NuGet.config index 3929afc2..c559e2ea 100644 --- a/src/NuGetForUnity.Tests/Assets/NuGet.config +++ b/src/NuGetForUnity.Tests/Assets/NuGet.config @@ -11,7 +11,7 @@ - + diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config b/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config index c5d0f689..5de0f46c 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config +++ b/src/NuGetForUnity.Tests/Assets/Tests/Resources/NuGet.config @@ -16,7 +16,7 @@ - + \ No newline at end of file diff --git a/src/NuGetForUnity/Editor/NugetConfigFile.cs b/src/NuGetForUnity/Editor/NugetConfigFile.cs index fda6f0eb..17dd67c1 100644 --- a/src/NuGetForUnity/Editor/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/NugetConfigFile.cs @@ -28,6 +28,8 @@ public class NugetConfigFile private const string LockPackagesOnRestoreConfigKey = "LockPackagesOnRestore"; + private const string PackagesConfigDirectoryPathConfigKey = "PackagesConfigDirectoryPath"; + /// /// The incomplete path that is saved. The path is expanded and made public via the property above. /// @@ -74,19 +76,21 @@ public class NugetConfigFile /// public bool ReadOnlyPackageFiles { get; set; } - public string PackagesConfigPath { get; set; } + /// + /// Gets or sets absolute path to directory containing packages.config file. + /// + public string PackagesConfigDirectoryPath { get; set; } public string RelativePackagesConfigPath { - get => NugetHelper.GetAssetsRelativePath(PackagesConfigPath); - - private set => PackagesConfigPath = Path.GetFullPath(Path.Combine(Application.dataPath, value)); + get => PathHelper.GetRelativePath(Application.dataPath, PackagesConfigDirectoryPath); + private set => PackagesConfigDirectoryPath = Path.GetFullPath(Path.Combine(Application.dataPath, value)); } /// /// The path to the packages.config file. /// - public string PackagesConfigFilePath => Path.Combine(PackagesConfigPath, PackagesConfigFile.FileName); + public string PackagesConfigFilePath => Path.Combine(PackagesConfigDirectoryPath, PackagesConfigFile.FileName); /// /// Gets or sets the timeout in seconds used for all web requests to NuGet sources. @@ -162,7 +166,7 @@ public void Save(string filePath) config.Add(addElement); addElement = new XElement("add"); - addElement.Add(new XAttribute("key", "PackagesConfigPath")); + addElement.Add(new XAttribute("key", PackagesConfigDirectoryPathConfigKey)); addElement.Add(new XAttribute("value", RelativePackagesConfigPath)); config.Add(addElement); @@ -375,7 +379,7 @@ public static NugetConfigFile Load(string filePath) { configFile.LockPackagesOnRestore = bool.Parse(value); } - else if (string.Equals(key, "PackagesConfigPath", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(key, PackagesConfigDirectoryPathConfigKey, StringComparison.OrdinalIgnoreCase)) { configFile.RelativePackagesConfigPath = value; } @@ -404,7 +408,7 @@ public static NugetConfigFile CreateDefaultFile(string filePath) - + "; diff --git a/src/NuGetForUnity/Editor/NugetPreferences.cs b/src/NuGetForUnity/Editor/NugetPreferences.cs index 02eda73f..ce8e0b5e 100644 --- a/src/NuGetForUnity/Editor/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/NugetPreferences.cs @@ -20,6 +20,11 @@ public class NugetPreferences : SettingsProvider /// private static Vector2 scrollPosition; + /// + /// Indicates if the warning for packages.config file path should be shown in case it is outside of Assets folder. + /// + private static bool shouldShowPackagesConfigPathWarning; + /// /// Initializes a new instance of the class. /// @@ -71,7 +76,7 @@ public override void OnGUI(string searchContext) EditorGUILayout.BeginHorizontal(); { - var packagesConfigPath = NugetHelper.NugetConfigFile.PackagesConfigPath; + var packagesConfigPath = NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath; EditorGUILayout.LabelField( new GUIContent($"Packages Config path: {NugetHelper.NugetConfigFile.RelativePackagesConfigPath}", $"Absolute path: {packagesConfigPath}")); if (GUILayout.Button("Browse")) @@ -82,12 +87,8 @@ public override void OnGUI(string searchContext) { var pathCheck = NugetHelper.GetAssetsRelativePath(newPath); - // if the path root is different or it is not under Assets folder, we issue a warning - if (pathCheck == newPath || pathCheck.StartsWith("..")) - { - Debug.LogWarning( - "Putting packages.config outside of Assets folder disables the functionality of restoring packages if the file is changed on the disk."); - } + // if the path root is different or it is not under Assets folder, we want to show a warning message + shouldShowPackagesConfigPathWarning = pathCheck == newPath || pathCheck.StartsWith(".."); PackagesConfigFile.Move(newPath); preferencesChangedThisFrame = true; @@ -95,6 +96,11 @@ public override void OnGUI(string searchContext) } } EditorGUILayout.EndHorizontal(); + if (shouldShowPackagesConfigPathWarning) + { + EditorGUILayout.HelpBox( + "Putting packages.config outside of Assets folder disables the functionality of restoring packages if the file is changed on the disk.", MessageType.Warning); + } var requestTimeout = EditorGUILayout.IntField( new GUIContent( diff --git a/src/NuGetForUnity/Editor/PackagesConfigFile.cs b/src/NuGetForUnity/Editor/PackagesConfigFile.cs index c63b463d..01b96ef7 100644 --- a/src/NuGetForUnity/Editor/PackagesConfigFile.cs +++ b/src/NuGetForUnity/Editor/PackagesConfigFile.cs @@ -223,8 +223,8 @@ public void Save() internal static void Move(string newPath) { var oldFilePath = NugetHelper.NugetConfigFile.PackagesConfigFilePath; - var oldPath = NugetHelper.NugetConfigFile.PackagesConfigPath; - NugetHelper.NugetConfigFile.PackagesConfigPath = newPath; + var oldPath = NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath; + NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath = newPath; try { if (!File.Exists(oldFilePath)) @@ -264,7 +264,7 @@ internal static void Move(string newPath) catch (Exception e) { Debug.LogException(e); - NugetHelper.NugetConfigFile.PackagesConfigPath = oldPath; + NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath = oldPath; } } From cfda3d4ddf9e7bcda2be84353400015fa259113f Mon Sep 17 00:00:00 2001 From: Ilija Stankovic Date: Thu, 10 Aug 2023 11:17:40 +0200 Subject: [PATCH 09/10] Fixed PR comments --- .../Assets/Tests/Editor/NuGetTests.cs | 9 ---- src/NuGetForUnity/Editor/NugetConfigFile.cs | 13 +++--- src/NuGetForUnity/Editor/NugetHelper.cs | 2 +- src/NuGetForUnity/Editor/NugetPreferences.cs | 18 +++++--- .../Editor/PackagesConfigFile.cs | 41 ++++++++++--------- 5 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs index b6a7c239..14e9c955 100644 --- a/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs +++ b/src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs @@ -648,9 +648,6 @@ public void TestPostprocessInstall(string packageId, string packageVersion) packagesConfigFile.Save(); Assert.IsFalse(NugetHelper.IsInstalled(package), "The package IS installed: {0} {1}", package.Id, package.Version); - - var assetsIndex = filepath.LastIndexOf("Assets", StringComparison.Ordinal); - filepath = filepath.Substring(assetsIndex); NugetPackageAssetPostprocessor.OnPostprocessAllAssets(new[] { filepath }, null, null, null); Assert.IsTrue(NugetHelper.IsInstalled(package), "The package was NOT installed: {0} {1}", package.Id, package.Version); @@ -668,9 +665,6 @@ public void TestPostprocessUninstall(string packageId, string packageVersion) var packagesConfigFile = new PackagesConfigFile(); packagesConfigFile.Save(); - - var assetsIndex = filepath.LastIndexOf("Assets", StringComparison.Ordinal); - filepath = filepath.Substring(assetsIndex); NugetPackageAssetPostprocessor.OnPostprocessAllAssets(new[] { filepath }, null, null, null); Assert.IsFalse(NugetHelper.IsInstalled(package), "The package is STILL installed: {0} {1}", package.Id, package.Version); @@ -691,9 +685,6 @@ public void TestPostprocessDifferentVersion(string packageId, string packageVers var packagesConfigFile = new PackagesConfigFile(); packagesConfigFile.AddPackage(packageNew); packagesConfigFile.Save(); - - var assetsIndex = filepath.LastIndexOf("Assets", StringComparison.Ordinal); - filepath = filepath.Substring(assetsIndex); NugetPackageAssetPostprocessor.OnPostprocessAllAssets(new[] { filepath }, null, null, null); Assert.IsFalse(NugetHelper.IsInstalled(packageOld), "The old package version IS STILL installed: {0} {1}", packageOld.Id, packageOld.Version); diff --git a/src/NuGetForUnity/Editor/NugetConfigFile.cs b/src/NuGetForUnity/Editor/NugetConfigFile.cs index 17dd67c1..f186ef8c 100644 --- a/src/NuGetForUnity/Editor/NugetConfigFile.cs +++ b/src/NuGetForUnity/Editor/NugetConfigFile.cs @@ -81,14 +81,17 @@ public class NugetConfigFile /// public string PackagesConfigDirectoryPath { get; set; } - public string RelativePackagesConfigPath + /// + /// Gets the relative path to directory containing packages.config file. The path is relative to the folder containing the 'NuGet.config' file. + /// + public string RelativePackagesConfigDirectoryPath { get => PathHelper.GetRelativePath(Application.dataPath, PackagesConfigDirectoryPath); private set => PackagesConfigDirectoryPath = Path.GetFullPath(Path.Combine(Application.dataPath, value)); } /// - /// The path to the packages.config file. + /// Gets the path to the packages.config file. /// public string PackagesConfigFilePath => Path.Combine(PackagesConfigDirectoryPath, PackagesConfigFile.FileName); @@ -167,7 +170,7 @@ public void Save(string filePath) addElement = new XElement("add"); addElement.Add(new XAttribute("key", PackagesConfigDirectoryPathConfigKey)); - addElement.Add(new XAttribute("value", RelativePackagesConfigPath)); + addElement.Add(new XAttribute("value", RelativePackagesConfigDirectoryPath)); config.Add(addElement); // save the default push source @@ -256,7 +259,7 @@ public static NugetConfigFile Load(string filePath) configFile.PackageSources = new List(); configFile.InstallFromCache = true; configFile.ReadOnlyPackageFiles = false; - configFile.RelativePackagesConfigPath = "."; + configFile.RelativePackagesConfigDirectoryPath = "."; var file = XDocument.Load(filePath); @@ -381,7 +384,7 @@ public static NugetConfigFile Load(string filePath) } else if (string.Equals(key, PackagesConfigDirectoryPathConfigKey, StringComparison.OrdinalIgnoreCase)) { - configFile.RelativePackagesConfigPath = value; + configFile.RelativePackagesConfigDirectoryPath = value; } } } diff --git a/src/NuGetForUnity/Editor/NugetHelper.cs b/src/NuGetForUnity/Editor/NugetHelper.cs index 12cd1f3c..ca296a19 100644 --- a/src/NuGetForUnity/Editor/NugetHelper.cs +++ b/src/NuGetForUnity/Editor/NugetHelper.cs @@ -138,7 +138,7 @@ private static Dictionary InstalledPackagesDictionary /// /// The path of witch we calculate the relative path of. /// The path relative to Assets directory, or "." if it is the Assets directory. - public static string GetAssetsRelativePath(string path) + internal static string GetAssetsRelativePath(string path) { return PathHelper.GetRelativePath(Application.dataPath, path); } diff --git a/src/NuGetForUnity/Editor/NugetPreferences.cs b/src/NuGetForUnity/Editor/NugetPreferences.cs index ce8e0b5e..7ab03ffd 100644 --- a/src/NuGetForUnity/Editor/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/NugetPreferences.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Linq; using UnityEditor; using UnityEngine; @@ -18,19 +17,23 @@ public class NugetPreferences : SettingsProvider /// /// The current position of the scroll bar in the GUI. /// - private static Vector2 scrollPosition; + private Vector2 scrollPosition; /// /// Indicates if the warning for packages.config file path should be shown in case it is outside of Assets folder. /// - private static bool shouldShowPackagesConfigPathWarning; + private bool shouldShowPackagesConfigPathWarning; /// /// Initializes a new instance of the class. + /// Path of packages.config file is checked here as well in case it was manually changed. /// public NugetPreferences() : base("Preferences/NuGet For Unity", SettingsScope.User) { + var packagesConfigPath = NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath; + var pathCheck = NugetHelper.GetAssetsRelativePath(packagesConfigPath); + shouldShowPackagesConfigPathWarning = pathCheck.StartsWith("..") || Path.IsPathRooted(pathCheck); } /// @@ -78,7 +81,9 @@ public override void OnGUI(string searchContext) { var packagesConfigPath = NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath; EditorGUILayout.LabelField( - new GUIContent($"Packages Config path: {NugetHelper.NugetConfigFile.RelativePackagesConfigPath}", $"Absolute path: {packagesConfigPath}")); + new GUIContent( + $"Packages Config path: {NugetHelper.NugetConfigFile.RelativePackagesConfigDirectoryPath}", + $"Absolute path: {packagesConfigPath}")); if (GUILayout.Button("Browse")) { var newPath = EditorUtility.OpenFolderPanel("Select Folder", packagesConfigPath, ""); @@ -88,7 +93,7 @@ public override void OnGUI(string searchContext) var pathCheck = NugetHelper.GetAssetsRelativePath(newPath); // if the path root is different or it is not under Assets folder, we want to show a warning message - shouldShowPackagesConfigPathWarning = pathCheck == newPath || pathCheck.StartsWith(".."); + shouldShowPackagesConfigPathWarning = pathCheck.StartsWith("..") || Path.IsPathRooted(pathCheck); PackagesConfigFile.Move(newPath); preferencesChangedThisFrame = true; @@ -99,7 +104,8 @@ public override void OnGUI(string searchContext) if (shouldShowPackagesConfigPathWarning) { EditorGUILayout.HelpBox( - "Putting packages.config outside of Assets folder disables the functionality of restoring packages if the file is changed on the disk.", MessageType.Warning); + "The packages.config is placed outside of Assets folder, this disables the functionality of automatically restoring packages if the file is changed on the disk.", + MessageType.Warning); } var requestTimeout = EditorGUILayout.IntField( diff --git a/src/NuGetForUnity/Editor/PackagesConfigFile.cs b/src/NuGetForUnity/Editor/PackagesConfigFile.cs index 01b96ef7..5e396398 100644 --- a/src/NuGetForUnity/Editor/PackagesConfigFile.cs +++ b/src/NuGetForUnity/Editor/PackagesConfigFile.cs @@ -225,6 +225,7 @@ internal static void Move(string newPath) var oldFilePath = NugetHelper.NugetConfigFile.PackagesConfigFilePath; var oldPath = NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath; NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath = newPath; + var newFilePath = Path.GetFullPath(Path.Combine(newPath, FileName)); try { if (!File.Exists(oldFilePath)) @@ -236,35 +237,35 @@ internal static void Move(string newPath) return; } - var newFilePath = Path.GetFullPath(Path.Combine(newPath, FileName)); - Directory.CreateDirectory(newPath); // moving config to the new path File.Move(oldFilePath, newFilePath); - - // manually moving meta file to suppress Unity warning - if (File.Exists($"{oldFilePath}.meta")) - { - File.Move($"{oldFilePath}.meta", $"{newFilePath}.meta"); - } - - // if the old path is now an empty directory, delete it - if (!Directory.EnumerateFileSystemEntries(oldPath).Any()) - { - Directory.Delete(oldPath); - - // also delete its meta file if it exists - if (File.Exists($"{oldPath}.meta")) - { - File.Delete($"{oldPath}.meta"); - } - } } catch (Exception e) { + // usually unauthorized access or IO exception (trying to move to a folder where the same file exists) Debug.LogException(e); NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath = oldPath; + return; + } + + // manually moving meta file to suppress Unity warning + if (File.Exists($"{oldFilePath}.meta")) + { + File.Move($"{oldFilePath}.meta", $"{newFilePath}.meta"); + } + + // if the old path is now an empty directory, delete it + if (!Directory.EnumerateFileSystemEntries(oldPath).Any()) + { + Directory.Delete(oldPath); + + // also delete its meta file if it exists + if (File.Exists($"{oldPath}.meta")) + { + File.Delete($"{oldPath}.meta"); + } } } From 1e6860abb1627da50913c207ce7b07183d43c2ef Mon Sep 17 00:00:00 2001 From: Ilija Stankovic Date: Fri, 11 Aug 2023 10:17:24 +0200 Subject: [PATCH 10/10] Moved path check to a method --- src/NuGetForUnity/Editor/NugetPreferences.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/NuGetForUnity/Editor/NugetPreferences.cs b/src/NuGetForUnity/Editor/NugetPreferences.cs index 7ab03ffd..6bbad2e6 100644 --- a/src/NuGetForUnity/Editor/NugetPreferences.cs +++ b/src/NuGetForUnity/Editor/NugetPreferences.cs @@ -31,9 +31,7 @@ public class NugetPreferences : SettingsProvider public NugetPreferences() : base("Preferences/NuGet For Unity", SettingsScope.User) { - var packagesConfigPath = NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath; - var pathCheck = NugetHelper.GetAssetsRelativePath(packagesConfigPath); - shouldShowPackagesConfigPathWarning = pathCheck.StartsWith("..") || Path.IsPathRooted(pathCheck); + shouldShowPackagesConfigPathWarning = IsPathInAssets(NugetHelper.NugetConfigFile.PackagesConfigDirectoryPath); } /// @@ -46,6 +44,17 @@ public static SettingsProvider Create() return new NugetPreferences(); } + /// + /// Checks if given path is within Assets folder. + /// + /// Path to check. + /// True if path is within Assets folder, false otherwise. + private static bool IsPathInAssets(string path) + { + var pathCheck = NugetHelper.GetAssetsRelativePath(path); + return pathCheck.StartsWith("..") || Path.IsPathRooted(pathCheck); + } + /// /// Draws the preferences GUI inside the Unity preferences window in the Editor. /// @@ -90,10 +99,8 @@ public override void OnGUI(string searchContext) if (!string.IsNullOrEmpty(newPath) && newPath != packagesConfigPath) { - var pathCheck = NugetHelper.GetAssetsRelativePath(newPath); - // if the path root is different or it is not under Assets folder, we want to show a warning message - shouldShowPackagesConfigPathWarning = pathCheck.StartsWith("..") || Path.IsPathRooted(pathCheck); + shouldShowPackagesConfigPathWarning = IsPathInAssets(newPath); PackagesConfigFile.Move(newPath); preferencesChangedThisFrame = true;