From fda6dbf256e585039ba141606be34c1443dce794 Mon Sep 17 00:00:00 2001 From: mob-sakai Date: Mon, 27 Jan 2020 10:22:53 +0900 Subject: [PATCH] feat: change caching scheme for available package versions --- .../Editor/Scripts/AvailableVersions.cs | 160 +++++++++++++ .../Editor/Scripts/AvailableVersions.cs.meta | 11 + .../Editor/Scripts/Utils.cs | 224 ++++-------------- 3 files changed, 211 insertions(+), 184 deletions(-) create mode 100644 Packages/com.coffee.upm-git-extension/Editor/Scripts/AvailableVersions.cs create mode 100644 Packages/com.coffee.upm-git-extension/Editor/Scripts/AvailableVersions.cs.meta diff --git a/Packages/com.coffee.upm-git-extension/Editor/Scripts/AvailableVersions.cs b/Packages/com.coffee.upm-git-extension/Editor/Scripts/AvailableVersions.cs new file mode 100644 index 0000000..108ac9a --- /dev/null +++ b/Packages/com.coffee.upm-git-extension/Editor/Scripts/AvailableVersions.cs @@ -0,0 +1,160 @@ +#if OPEN_SESAME // This line is added by Open Sesame Portable. DO NOT remov manually. +using UnityEngine; +using UnityEditor; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Diagnostics; +// using UnityEditor.PackageManager.UI.InternalBridge; +// using Debug = UnityEditor.PackageManager.UI.InternalBridge.Debug; + + +namespace Coffee.PackageManager.UI +{ + [Serializable] + public class AvailableVersion : IEquatable, ISerializationCallbackReceiver + { + public string packageName = ""; + public string version = ""; + public string refName = ""; + public string repoUrl = ""; + + public string refNameText { get { return version == refName ? version : version + " - " + refName; } } + + bool IEquatable.Equals(AvailableVersion other) + { + return other != null + && packageName == other.packageName + && version == other.version + && repoUrl == other.repoUrl + && refName == other.refName; + } + + public override int GetHashCode() + { + return packageName.GetHashCode() + + version.GetHashCode() + + repoUrl.GetHashCode() + + refName.GetHashCode(); + } + + public void OnBeforeSerialize() + { + } + + public void OnAfterDeserialize() + { + repoUrl = PackageUtils.GetRepoUrl(repoUrl); + } + } + + public class AvailableVersions : ScriptableSingleton + { + const string kResultDir = "Library/UpmGitExtension/results"; + const string kHeader = "[AvailableVersions] "; + const string kGetVersionsJs = "Packages/com.coffee.upm-git-extension/Editor/Commands/get-available-versions.js"; + + public AvailableVersion[] versions = new AvailableVersion[0]; + + public static event Action OnChanged = () => { }; + + [Serializable] + public class ResultInfo + { + public AvailableVersion[] versions; + } + + public static void Clear() + { + instance.versions = new AvailableVersion[0]; + } + + public static IEnumerable GetVersions(string packageName) + { + return instance.versions.Where(x => x.packageName == packageName); + } + + public static IEnumerable GetVersionsFromRepo(string url) + { + url = PackageUtils.GetRepoUrl(url); + return instance.versions.Where(x => x.repoUrl == url); + } + + public static void AddVersions(IEnumerable add) + { + var length = instance.versions.Length; + var versions = instance.versions + .Union(add) + .ToArray(); + + if (versions.Length != length) + { + instance.versions = versions; + OnChanged(); + } + } + + public static void UpdateAvailableVersions(string packageName, string repoUrl) + { + var unity = Application.unityVersion; +#if UNITY_EDITOR_WIN + var node = Path.Combine(EditorApplication.applicationContentsPath, "Tools/nodejs/node.exe").Replace('/', '\\'); +#else + var node = Path.Combine(EditorApplication.applicationContentsPath, "Tools/nodejs/bin/node"); +#endif + + new UnityEditor.Utils.Program(new ProcessStartInfo() + { + FileName = node, + Arguments = string.Format("\"{0}\" {1} {2} {3}", kGetVersionsJs, packageName, repoUrl, unity) + }) + .Start(); + Debug.Log(kHeader, "{0} {1}", node, string.Format("\"{0}\" {1} {2} {3}", kGetVersionsJs, packageName, repoUrl, unity)); + } + + static void OnResultCreated(object o, FileSystemEventArgs e) + { + if (Path.GetExtension(e.Name) == ".json") + OnResultCreated(e.Name); + } + + static void OnResultCreated(string file) + { + try + { + var info = JsonUtility.FromJson(File.ReadAllText(file)); + EditorApplication.delayCall += () => AvailableVersions.AddVersions(info.versions); + } + finally + { + File.Delete(file); + } + } + + + [InitializeOnLoadMethod] + static void WatchResultJson() + { +#if !UNITY_EDITOR_WIN + Environment.SetEnvironmentVariable("MONO_MANAGED_WATCHER", "enabled"); +#endif + if (!Directory.Exists(kResultDir)) + Directory.CreateDirectory(kResultDir); + + foreach (var file in Directory.GetFiles(kResultDir, "*.json")) + OnResultCreated(file); + + var watcher = new FileSystemWatcher() + { + Path = kResultDir, + NotifyFilter = NotifyFilters.CreationTime, + IncludeSubdirectories = false, + EnableRaisingEvents = true, + }; + + watcher.Created += OnResultCreated; + } + } +} +#endif // This line is added by Open Sesame Portable. DO NOT remov manually. \ No newline at end of file diff --git a/Packages/com.coffee.upm-git-extension/Editor/Scripts/AvailableVersions.cs.meta b/Packages/com.coffee.upm-git-extension/Editor/Scripts/AvailableVersions.cs.meta new file mode 100644 index 0000000..b67cd4f --- /dev/null +++ b/Packages/com.coffee.upm-git-extension/Editor/Scripts/AvailableVersions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 36a22e774e9ce45a69191dd1d81ab343 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.coffee.upm-git-extension/Editor/Scripts/Utils.cs b/Packages/com.coffee.upm-git-extension/Editor/Scripts/Utils.cs index 67a3cdc..4c49e0e 100644 --- a/Packages/com.coffee.upm-git-extension/Editor/Scripts/Utils.cs +++ b/Packages/com.coffee.upm-git-extension/Editor/Scripts/Utils.cs @@ -9,6 +9,7 @@ #endif using System.Text.RegularExpressions; using UnityEngine; +using UnityEditor; using System; using System.Collections.Generic; using System.IO; @@ -17,213 +18,59 @@ namespace Coffee.PackageManager.UI { - public class Debug + public static class Debug { - [Conditional("DEBUG")] - public static void Log(object message) + static bool logEnabled = false; + static Debug() { - UnityEngine.Debug.Log(message); + logEnabled = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup) + .Split(';', ',') + .Any(x => x == "UGE_LOG"); } - [Conditional("DEBUG")] - public static void LogFormat(string format, params object[] args) + static void Log(string header, string format, params object[] args) { - UnityEngine.Debug.LogFormat(format, args); + if (logEnabled) + UnityEngine.Debug.LogFormat(header + format, args); } - public static void LogError(object message) + public static void Log(string header, object message) { - UnityEngine.Debug.LogError(message); + if (logEnabled) + UnityEngine.Debug.Log(header + message); } - public static void LogErrorFormat(string format, params object[] args) + static void Warning(string header, string format, params object[] args) { - UnityEngine.Debug.LogErrorFormat(format, args); + if (logEnabled) + UnityEngine.Debug.LogWarningFormat(header + format, args); } - public static void LogException(Exception e) + public static void Warning(string header, object message) { - UnityEngine.Debug.LogException(e); + if (logEnabled) + UnityEngine.Debug.LogWarning(header + message); } - } - - - public static class UIUtils - { - const string kDisplayNone = "display-none"; - public static void SetElementDisplay(VisualElement element, bool value) - { - if (element == null) - return; - - SetElementClass(element, kDisplayNone, !value); - element.visible = value; - } - - public static bool IsElementDisplay(VisualElement element) - { - return !HasElementClass(element, kDisplayNone); - } - - public static void SetElementClass(VisualElement element, string className, bool value) - { - if (element == null) - return; - - if (value) - element.AddToClassList(className); - else - element.RemoveFromClassList(className); - } - - public static bool HasElementClass(VisualElement element, string className) - { - if (element == null) - return false; - - return element.ClassListContains(className); - } - - public static VisualElement GetRoot(VisualElement element) - { - while (element != null && element.parent != null) - { - element = element.parent; - } - return element; - } - } - - public static class GitUtils - { - static readonly Regex REG_REFS = new Regex("refs/(tags|remotes/origin)/([^/]+),(.+),([^\\s\\r\\n]+)", RegexOptions.Compiled | RegexOptions.Multiline); - static readonly string GET_REFS_SCRIPT = "Packages/com.coffee.upm-git-extension/Editor/Commands/get-available-refs"; - - /// - /// Fetch the all branch/tag names where the package can be installed from the repository. - /// - package.json and package.json.meta exist in root - /// - Support current unity verison - /// - Branch name is not nested - /// - /// Package name - /// Repo url - /// Callback. The argument is "[tagOrBranchName],[version],[packageName]" - public static void GetRefs(string packageName, string repoUrl, Action> callback) - { - Debug.LogFormat("[GitUtils.GetRefs] Start get refs: {0}, {1}", packageName, repoUrl); - if (string.IsNullOrEmpty(repoUrl)) - { - callback(Enumerable.Empty()); - return; - } - - var appDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - var cacheRoot = new DirectoryInfo(Path.Combine(appDir, "UpmGitExtension")); - var cacheDir = new DirectoryInfo(Path.Combine(cacheRoot.FullName, Uri.EscapeDataString(packageName + "@" + repoUrl))); - var cacheFile = new FileInfo(Path.Combine(cacheDir.FullName, "versions")); - // Results are cached for 5 minutes. - if (cacheFile.Exists && (DateTime.Now - cacheFile.LastWriteTime).TotalMinutes < 5) - { - Debug.LogFormat("[GitUtils.GetRefs] Refs has been cached: {0}", cacheFile); - var versions = REG_REFS.Matches(File.ReadAllText(cacheFile.FullName)) - .Cast() - .Where(m => packageName.Length == 0 || packageName == m.Groups[4].Value) - .Select(m => string.Format("{0},{1},{2}", m.Groups[2], m.Groups[3], m.Groups[4])); - callback(versions); - } - // Run script and cache result. - else - { - string execute, args; - var unity = Application.unityVersion; - var dir = cacheDir.FullName; - - if (Application.platform == RuntimePlatform.WindowsEditor) - { - execute = "cmd.exe"; - args = string.Format("/C \"\"{0}.bat\" \"{1}\" \"{2}\" \"{3}\"\"", Path.GetFullPath(GET_REFS_SCRIPT), repoUrl, dir, unity); - } - else - { - execute = "/bin/sh"; - args = string.Format("\"{0}.sh\" \"{1}\" \"{2}\" \"{3}\"", Path.GetFullPath(GET_REFS_SCRIPT), repoUrl, dir, unity); - } - - ExecuteShell(execute, args, (success) => - { - if (success) - GetRefs(packageName, repoUrl, callback); - else - callback(Enumerable.Empty()); - }); - } - } - - static void ExecuteShell(string script, string args, Action callback) + public static void Error(string header, string format, params object[] args) { - Debug.LogFormat("[GitUtils.ExecuteShell] script = {0}, args = {1}", script, args); - var startInfo = new System.Diagnostics.ProcessStartInfo - { - Arguments = args, - CreateNoWindow = true, - FileName = script, - UseShellExecute = false, - }; - - var launchProcess = System.Diagnostics.Process.Start(startInfo); - if (launchProcess == null || launchProcess.HasExited == true || launchProcess.Id == 0) - { - Debug.LogErrorFormat("[GitUtils.ExecuteShell] failed: script = {0}, args = {1}", script, args); - callback(false); - } - else - { - //Add process callback. - launchProcess.Exited += (sender, e) => - { - Debug.LogFormat("[GitUtils.ExecuteShell] exit {0}", launchProcess.ExitCode); - bool success = 0 == launchProcess.ExitCode; - if (!success) - { - Debug.LogErrorFormat("[Utils.ExecuteShell] failed: script = {0}, args = {1}", script, args); - } - callback(success); - }; - - launchProcess.EnableRaisingEvents = true; - } + UnityEngine.Debug.LogErrorFormat(header + format, args); } - } - - public static class JsonUtils - { - public static Dictionary DeserializeFile(string file) + public static void Error(string header, object message) { - var text = File.ReadAllText(file); -#if UNITY_2019_1_9_OR_NEWER - return Json.Deserialize(text) as Dictionary; -#else - return Expose.FromType(Type.GetType("UnityEditor.Json, UnityEditor")).Call("Deserialize", text).As>(); -#endif + UnityEngine.Debug.LogError(header + message); } - public static void SerializeFile(string file, Dictionary json) + public static void Exception(string header, Exception e) { -#if UNITY_2019_1_9_OR_NEWER - var text = Json.Serialize(json); -#elif UNITY_2019_1_OR_NEWER - var text = Expose.FromType(Type.GetType("UnityEditor.Json, UnityEditor")).Call("Serialize", json, false, " ").As(); -#else - var text = Expose.FromType(Type.GetType("UnityEditor.Json, UnityEditor")).Call("Serialize", json).As(); -#endif - File.WriteAllText(file, text); + UnityEngine.Debug.LogException(new Exception(header + e.Message, e.InnerException)); } } public static class PackageUtils { + const string kHeader = "[PackageUtils] "; static readonly Regex REG_PACKAGE_ID = new Regex("^([^@]+)@([^#]+)(#(.+))?$", RegexOptions.Compiled); /// @@ -234,7 +81,7 @@ public static class PackageUtils /// Reference name for install (option) public static void InstallPackage(string packageName, string repoUrl, string refName) { - Debug.LogFormat("[PackageUtils.InstallPackage] packageName = {0}, repoUrl = {1}, refName = {2}", packageName, repoUrl, refName); + Debug.Log(kHeader, "[PackageUtils.InstallPackage] packageName = {0}, repoUrl = {1}, refName = {2}", packageName, repoUrl, refName); UpdateManifestJson(dependencies => { // Remove from dependencies. @@ -256,7 +103,7 @@ public static void InstallPackage(string packageName, string repoUrl, string ref /// Package name public static void UninstallPackage(string packageName) { - Debug.LogFormat("[PackageUtils.UninstallPackage] packageName = {0}", packageName); + Debug.Log(kHeader, "[PackageUtils.UninstallPackage] packageName = {0}", packageName); UpdateManifestJson(dependencies => dependencies.Remove(packageName)); } @@ -266,13 +113,13 @@ public static void UninstallPackage(string packageName) /// Action for dependencies static void UpdateManifestJson(Action> actionForDependencies) { - Debug.LogFormat("[PackageUtils.UpdateManifestJson]"); + Debug.Log(kHeader, "[PackageUtils.UpdateManifestJson]"); const string manifestPath = "Packages/manifest.json"; - var manifest = JsonUtils.DeserializeFile(manifestPath); + var manifest = Json.Deserialize(File.ReadAllText(manifestPath)) as Dictionary; actionForDependencies(manifest["dependencies"] as Dictionary); // Save manifest.json. - JsonUtils.SerializeFile(manifestPath, manifest); + File.WriteAllText(manifestPath, Json.Serialize(manifest)); EditorApplication.delayCall += () => AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); } @@ -334,6 +181,15 @@ public static void OverwriteCallback(this Button button, Action action) button.clickable = new Clickable(action); button.AddManipulator(button.clickable); } + + public static VisualElement GetRoot(this VisualElement element) + { + while (element != null && element.parent != null) + { + element = element.parent; + } + return element; + } } }