-
-
Notifications
You must be signed in to change notification settings - Fork 344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve Plugin Metadata & Path Management #3272
base: dev
Are you sure you want to change the base?
Changes from all commits
89bca3b
5919418
f1b5e68
1aaba46
9cd3011
8f7ad27
d07b304
b50db58
6012111
3efe550
126153b
3106b02
012ef49
58de625
47adfd1
a0c2a42
a29ed64
65ae342
6622815
fe86e23
f8d0981
7975ab5
ce3a3e9
486cc6a
af3b391
c690e59
b0b1a26
e3af882
c87b731
ee0b039
5f976b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,7 +35,7 @@ public static class PluginManager | |
|
||
private static PluginsSettings Settings; | ||
private static List<PluginMetadata> _metadatas; | ||
private static List<string> _modifiedPlugins = new List<string>(); | ||
private static List<string> _modifiedPlugins = new(); | ||
|
||
/// <summary> | ||
/// Directories that will hold Flow Launcher plugin directory | ||
|
@@ -72,15 +72,20 @@ public static async ValueTask DisposePluginsAsync() | |
{ | ||
foreach (var pluginPair in AllPlugins) | ||
{ | ||
switch (pluginPair.Plugin) | ||
{ | ||
case IDisposable disposable: | ||
disposable.Dispose(); | ||
break; | ||
case IAsyncDisposable asyncDisposable: | ||
await asyncDisposable.DisposeAsync(); | ||
break; | ||
} | ||
await DisposePluginAsync(pluginPair); | ||
} | ||
} | ||
|
||
private static async Task DisposePluginAsync(PluginPair pluginPair) | ||
{ | ||
switch (pluginPair.Plugin) | ||
{ | ||
case IDisposable disposable: | ||
disposable.Dispose(); | ||
break; | ||
case IAsyncDisposable asyncDisposable: | ||
await asyncDisposable.DisposeAsync(); | ||
break; | ||
} | ||
} | ||
|
||
|
@@ -155,6 +160,25 @@ public static void LoadPlugins(PluginsSettings settings) | |
Settings = settings; | ||
Settings.UpdatePluginSettings(_metadatas); | ||
AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); | ||
// Since dotnet plugins need to get assembly name first, we should update plugin directory after loading plugins | ||
UpdatePluginDirectory(_metadatas); | ||
} | ||
|
||
private static void UpdatePluginDirectory(List<PluginMetadata> metadatas) | ||
{ | ||
foreach (var metadata in metadatas) | ||
{ | ||
if (AllowedLanguage.IsDotNet(metadata.Language)) | ||
{ | ||
metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.AssemblyName); | ||
metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.AssemblyName); | ||
} | ||
else | ||
{ | ||
metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); | ||
metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
|
@@ -228,10 +252,9 @@ public static ICollection<PluginPair> ValidPluginsForQuery(Query query) | |
if (query is null) | ||
return Array.Empty<PluginPair>(); | ||
|
||
if (!NonGlobalPlugins.ContainsKey(query.ActionKeyword)) | ||
if (!NonGlobalPlugins.TryGetValue(query.ActionKeyword, out var plugin)) | ||
return GlobalPlugins; | ||
|
||
var plugin = NonGlobalPlugins[query.ActionKeyword]; | ||
return new List<PluginPair> | ||
{ | ||
plugin | ||
|
@@ -445,10 +468,10 @@ public static bool PluginModified(string uuid) | |
/// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url, | ||
/// unless it's a local path installation | ||
/// </summary> | ||
public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath) | ||
public static async Task UpdatePluginAsync(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath) | ||
{ | ||
InstallPlugin(newVersion, zipFilePath, checkModified:false); | ||
UninstallPlugin(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false); | ||
await UninstallPluginAsync(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false); | ||
_modifiedPlugins.Add(existingVersion.ID); | ||
} | ||
|
||
|
@@ -463,9 +486,9 @@ public static void InstallPlugin(UserPlugin plugin, string zipFilePath) | |
/// <summary> | ||
/// Uninstall a plugin. | ||
/// </summary> | ||
public static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false) | ||
public static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false) | ||
{ | ||
UninstallPlugin(plugin, removePluginFromSettings, removePluginSettings, true); | ||
await UninstallPluginAsync(plugin, removePluginFromSettings, removePluginSettings, true); | ||
} | ||
|
||
#endregion | ||
|
@@ -546,63 +569,62 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c | |
} | ||
} | ||
|
||
internal static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) | ||
internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) | ||
{ | ||
if (checkModified && PluginModified(plugin.ID)) | ||
{ | ||
throw new ArgumentException($"Plugin {plugin.Name} has been modified"); | ||
} | ||
|
||
if (removePluginSettings) | ||
if (removePluginSettings || removePluginFromSettings) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's the difference between these two? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Because these two will all delete folders of plugins (one for cache folder, another for settings folder), so we need to dispose plugin if either of them is true. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. emmm I think the first one is pretty weird...but sure |
||
{ | ||
if (AllowedLanguage.IsDotNet(plugin.Language)) // for the plugin in .NET, we can use assembly loader | ||
// If we want to remove plugin from AllPlugins, | ||
// we need to dispose them so that they can release file handles | ||
// which can help FL to delete the plugin settings & cache folders successfully | ||
var pluginPairs = AllPlugins.FindAll(p => p.Metadata.ID == plugin.ID); | ||
foreach (var pluginPair in pluginPairs) | ||
{ | ||
var assemblyLoader = new PluginAssemblyLoader(plugin.ExecuteFilePath); | ||
var assembly = assemblyLoader.LoadAssemblyAndDependencies(); | ||
jjw24 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var assemblyName = assembly.GetName().Name; | ||
await DisposePluginAsync(pluginPair); | ||
} | ||
} | ||
|
||
// if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin | ||
// so we need to remove it from the api instance | ||
if (removePluginSettings) | ||
{ | ||
// For dotnet plugins, we need to remove their PluginJsonStorage instance | ||
if (AllowedLanguage.IsDotNet(plugin.Language)) | ||
{ | ||
var method = API.GetType().GetMethod("RemovePluginSettings"); | ||
var pluginJsonStorage = method?.Invoke(API, new object[] { assemblyName }); | ||
method?.Invoke(API, new object[] { plugin.AssemblyName }); | ||
} | ||
|
||
// if there exists a json storage for current plugin, we need to delete the directory path | ||
if (pluginJsonStorage != null) | ||
{ | ||
var deleteMethod = pluginJsonStorage.GetType().GetMethod("DeleteDirectory"); | ||
try | ||
{ | ||
deleteMethod?.Invoke(pluginJsonStorage, null); | ||
} | ||
catch (Exception e) | ||
{ | ||
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e); | ||
API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), | ||
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); | ||
} | ||
} | ||
try | ||
{ | ||
var pluginSettingsDirectory = plugin.PluginSettingsDirectoryPath; | ||
if (Directory.Exists(pluginSettingsDirectory)) | ||
Directory.Delete(pluginSettingsDirectory, true); | ||
} | ||
else // the plugin with json prc interface | ||
catch (Exception e) | ||
{ | ||
var pluginPair = AllPlugins.FirstOrDefault(p => p.Metadata.ID == plugin.ID); | ||
if (pluginPair != null && pluginPair.Plugin is JsonRPCPlugin jsonRpcPlugin) | ||
{ | ||
try | ||
{ | ||
jsonRpcPlugin.DeletePluginSettingsDirectory(); | ||
} | ||
catch (Exception e) | ||
{ | ||
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e); | ||
API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), | ||
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); | ||
} | ||
} | ||
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin settings folder for {plugin.Name}", e); | ||
API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), | ||
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); | ||
} | ||
} | ||
|
||
if (removePluginFromSettings) | ||
{ | ||
try | ||
{ | ||
var pluginCacheDirectory = plugin.PluginCacheDirectoryPath; | ||
if (Directory.Exists(pluginCacheDirectory)) | ||
Directory.Delete(pluginCacheDirectory, true); | ||
} | ||
catch (Exception e) | ||
{ | ||
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin cache folder for {plugin.Name}", e); | ||
API.ShowMsg(API.GetTranslation("failedToRemovePluginCacheTitle"), | ||
string.Format(API.GetTranslation("failedToRemovePluginCacheMessage"), plugin.Name)); | ||
} | ||
Settings.Plugins.Remove(plugin.ID); | ||
AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code quality improvement