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

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

View file

@ -255,8 +255,6 @@ internal class AutoUpdateManager : IServiceType
private async Task RunAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins)
{
var pluginStates = new List<PluginUpdateStatus>();
Log.Information("Found {UpdatablePluginsCount} plugins to update", updatablePlugins.Count);
if (updatablePlugins.Count == 0)
@ -274,35 +272,16 @@ internal class AutoUpdateManager : IServiceType
Icon = INotificationIcon.From(FontAwesomeIcon.Download),
Minimized = false,
});
var numDone = 0;
// TODO: This is NOT correct, we need to do this inside PM to be able to avoid notifying for each of these and avoid
// 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)
var progress = new Progress<PluginManager.PluginUpdateProgress>();
progress.ProgressChanged += (_, progress) =>
{
try
{
notification.Content = $"Updating {plugin.InstalledPlugin.Manifest.Name}...";
notification.Progress = (float)numDone / updatablePlugins.Count;
if (this.isDryRun.Value)
{
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.Content = $"Updating {progress.CurrentPluginManifest.Name}...";
notification.Progress = (float)progress.PluginsProcessed / progress.TotalPlugins;
};
var pluginStates = await this.pluginManager.UpdatePluginsAsync(updatablePlugins, this.isDryRun.Value, true, progress);
notification.Progress = 1;
notification.UserDismissable = true;
notification.HardExpiry = DateTime.Now.AddSeconds(30);
@ -318,7 +297,8 @@ internal class AutoUpdateManager : IServiceType
};
// 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;
@ -337,7 +317,7 @@ internal class AutoUpdateManager : IServiceType
notification.Type = NotificationType.Error;
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)
.Select(x => x.Name).ToList();

View file

@ -977,32 +977,39 @@ internal class PluginManager : IInternalDisposableService
/// <summary>
/// Update all non-dev plugins.
/// </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="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>
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");
var updateTasks = new List<Task<PluginUpdateStatus>>();
var totalPlugins = toUpdate.Count;
var processedPlugins = 0;
// Prevent collection was modified errors
lock (this.pluginListLock)
{
foreach (var plugin in this.updatablePluginsList)
foreach (var plugin in toUpdate)
{
// Can't update that!
if (plugin.InstalledPlugin.IsDev)
continue;
if (!plugin.InstalledPlugin.IsWantedByAnyProfile && ignoreDisabled)
if (!plugin.InstalledPlugin.IsWantedByAnyProfile)
continue;
if (plugin.InstalledPlugin.Manifest.ScheduledForDeletion)
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,
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;
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>
@ -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>
/// 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.