refactor UpdatePluginsAsync() to take a list of plugins to update instead

This commit is contained in:
goat 2024-06-15 18:35:47 +02:00
parent 8d18940108
commit bc2edf765f
3 changed files with 51 additions and 38 deletions

View file

@ -712,7 +712,11 @@ internal class PluginInstallerWindow : Window, IDisposable
this.updateStatus = OperationStatus.InProgress; this.updateStatus = OperationStatus.InProgress;
this.loadingIndicatorKind = LoadingIndicatorKind.UpdatingAll; this.loadingIndicatorKind = LoadingIndicatorKind.UpdatingAll;
Task.Run(() => pluginManager.UpdatePluginsAsync(true, false)) var toUpdate = this.pluginListUpdatable
.Where(x => x.InstalledPlugin.IsLoaded)
.ToList();
Task.Run(() => pluginManager.UpdatePluginsAsync(toUpdate, false))
.ContinueWith(task => .ContinueWith(task =>
{ {
this.updateStatus = OperationStatus.Complete; this.updateStatus = OperationStatus.Complete;

View file

@ -255,8 +255,6 @@ internal class AutoUpdateManager : IServiceType
private async Task RunAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins) private async Task RunAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins)
{ {
var pluginStates = new List<PluginUpdateStatus>();
Log.Information("Found {UpdatablePluginsCount} plugins to update", updatablePlugins.Count); Log.Information("Found {UpdatablePluginsCount} plugins to update", updatablePlugins.Count);
if (updatablePlugins.Count == 0) if (updatablePlugins.Count == 0)
@ -275,33 +273,14 @@ internal class AutoUpdateManager : IServiceType
Minimized = false, Minimized = false,
}); });
var numDone = 0; var progress = new Progress<PluginManager.PluginUpdateProgress>();
// TODO: This is NOT correct, we need to do this inside PM to be able to avoid notifying for each of these and avoid progress.ProgressChanged += (_, progress) =>
// refreshing the plugin list until we're done. See PluginManager::UpdatePluginsAsync().
// Maybe have a function in PM that can take a list of AvailablePluginUpdate instead and update them all,
// and get rid of UpdatePluginsAsync()? Will have to change the installer a bit but that might be for the better API-wise.
foreach (var plugin in updatablePlugins)
{ {
try notification.Content = $"Updating {progress.CurrentPluginManifest.Name}...";
{ notification.Progress = (float)progress.PluginsProcessed / progress.TotalPlugins;
notification.Content = $"Updating {plugin.InstalledPlugin.Manifest.Name}..."; };
notification.Progress = (float)numDone / updatablePlugins.Count;
if (this.isDryRun.Value) var pluginStates = await this.pluginManager.UpdatePluginsAsync(updatablePlugins, this.isDryRun.Value, true, progress);
{
await Task.Delay(5000);
}
var status = await this.pluginManager.UpdateSinglePluginAsync(plugin, true, this.isDryRun.Value);
pluginStates.Add(status);
}
catch (Exception ex)
{
Log.Error(ex, "Failed to auto-update plugin {PluginName}", plugin.InstalledPlugin.Manifest.Name);
}
numDone++;
}
notification.Progress = 1; notification.Progress = 1;
notification.UserDismissable = true; notification.UserDismissable = true;
@ -318,7 +297,8 @@ internal class AutoUpdateManager : IServiceType
}; };
// Update the notification to show the final state // Update the notification to show the final state
if (pluginStates.All(x => x.Status == PluginUpdateStatus.StatusKind.Success)) var pluginUpdateStatusEnumerable = pluginStates as PluginUpdateStatus[] ?? pluginStates.ToArray();
if (pluginUpdateStatusEnumerable.All(x => x.Status == PluginUpdateStatus.StatusKind.Success))
{ {
notification.Minimized = true; notification.Minimized = true;
@ -337,7 +317,7 @@ internal class AutoUpdateManager : IServiceType
notification.Type = NotificationType.Error; notification.Type = NotificationType.Error;
notification.Content = "Some plugins failed to update. Please check the plugin installer for more information."; notification.Content = "Some plugins failed to update. Please check the plugin installer for more information.";
var failedPlugins = pluginStates var failedPlugins = pluginUpdateStatusEnumerable
.Where(x => x.Status != PluginUpdateStatus.StatusKind.Success) .Where(x => x.Status != PluginUpdateStatus.StatusKind.Success)
.Select(x => x.Name).ToList(); .Select(x => x.Name).ToList();

View file

@ -977,32 +977,39 @@ internal class PluginManager : IInternalDisposableService
/// <summary> /// <summary>
/// Update all non-dev plugins. /// Update all non-dev plugins.
/// </summary> /// </summary>
/// <param name="ignoreDisabled">Ignore disabled plugins.</param> /// <param name="toUpdate">List of plugins to update.</param>
/// <param name="dryRun">Perform a dry run, don't install anything.</param> /// <param name="dryRun">Perform a dry run, don't install anything.</param>
/// <param name="autoUpdate">If this action was performed as part of an auto-update.</param> /// <param name="autoUpdate">If this action was performed as part of an auto-update.</param>
/// <param name="progress">An <see cref="IProgress{T}"/> implementation to receive progress updates about the installation status.</param>
/// <returns>Success or failure and a list of updated plugin metadata.</returns> /// <returns>Success or failure and a list of updated plugin metadata.</returns>
public async Task<IEnumerable<PluginUpdateStatus>> UpdatePluginsAsync(bool ignoreDisabled, bool dryRun, bool autoUpdate = false) public async Task<IEnumerable<PluginUpdateStatus>> UpdatePluginsAsync(
ICollection<AvailablePluginUpdate> toUpdate,
bool dryRun,
bool autoUpdate = false,
IProgress<PluginUpdateProgress>? progress = null)
{ {
Log.Information("Starting plugin update"); Log.Information("Starting plugin update");
var updateTasks = new List<Task<PluginUpdateStatus>>(); var updateTasks = new List<Task<PluginUpdateStatus>>();
var totalPlugins = toUpdate.Count;
var processedPlugins = 0;
// Prevent collection was modified errors // Prevent collection was modified errors
lock (this.pluginListLock) lock (this.pluginListLock)
{ {
foreach (var plugin in this.updatablePluginsList) foreach (var plugin in toUpdate)
{ {
// Can't update that! // Can't update that!
if (plugin.InstalledPlugin.IsDev) if (plugin.InstalledPlugin.IsDev)
continue; continue;
if (!plugin.InstalledPlugin.IsWantedByAnyProfile && ignoreDisabled) if (!plugin.InstalledPlugin.IsWantedByAnyProfile)
continue; continue;
if (plugin.InstalledPlugin.Manifest.ScheduledForDeletion) if (plugin.InstalledPlugin.Manifest.ScheduledForDeletion)
continue; continue;
updateTasks.Add(this.UpdateSinglePluginAsync(plugin, false, dryRun)); updateTasks.Add(UpdateSinglePluginWithProgressAsync(plugin));
} }
} }
@ -1013,9 +1020,26 @@ internal class PluginManager : IInternalDisposableService
autoUpdate ? PluginListInvalidationKind.AutoUpdate : PluginListInvalidationKind.Update, autoUpdate ? PluginListInvalidationKind.AutoUpdate : PluginListInvalidationKind.Update,
updatedList.Select(x => x.InternalName)); updatedList.Select(x => x.InternalName));
Log.Information("Plugin update OK. {updateCount} plugins updated.", updatedList.Length); Log.Information("Plugin update OK. {UpdateCount} plugins updated", updatedList.Length);
return updatedList; return updatedList;
async Task<PluginUpdateStatus> UpdateSinglePluginWithProgressAsync(AvailablePluginUpdate plugin)
{
var result = await this.UpdateSinglePluginAsync(plugin, false, dryRun);
// Update the progress
if (progress != null)
{
var newProcessedAmount = Interlocked.Increment(ref processedPlugins);
progress.Report(new PluginUpdateProgress(
newProcessedAmount,
totalPlugins,
plugin.InstalledPlugin.Manifest));
}
return result;
}
} }
/// <summary> /// <summary>
@ -1832,6 +1856,11 @@ internal class PluginManager : IInternalDisposableService
} }
} }
/// <summary>
/// Class representing progress of an update operation.
/// </summary>
public record PluginUpdateProgress(int PluginsProcessed, int TotalPlugins, IPluginManifest CurrentPluginManifest);
/// <summary> /// <summary>
/// Simple class that tracks the internal names and public names of plugins that we are planning to load at startup, /// Simple class that tracks the internal names and public names of plugins that we are planning to load at startup,
/// and are still actively loading. /// and are still actively loading.