using FCPModUpdater.Commands.Settings; using FCPModUpdater.Models; using FCPModUpdater.Services; using FCPModUpdater.UI; using JetBrains.Annotations; using Spectre.Console; using Spectre.Console.Cli; namespace FCPModUpdater.Commands; [UsedImplicitly] public class UpdateCommand( IGitService gitService, IModDiscoveryService modDiscoveryService, UpdateCheckService updateCheckService) : AsyncCommand { public static IReadOnlyList GetUpdateableMods(IReadOnlyList mods) => mods.Where(m => m.Source == ModSource.Git && m.Status == ModStatus.Behind).ToList(); protected override async Task ExecuteAsync(CommandContext context, ModPathSettings settings, CancellationToken ct) { try { var modsDirectory = ModsDirectoryResolver.Resolve(settings.ModDirectory?.FullName, interactive: false); AnsiConsole.MarkupLine($"[grey]Using mods directory: {modsDirectory}[/]"); AnsiConsole.WriteLine(); Task updateCheckTask = updateCheckService.CheckForUpdateAsync(ct); var mods = await ProgressReporter.WithStatusAsync( "Scanning mods directory...", async () => await modDiscoveryService.DiscoverModsAsync(modsDirectory, ct: ct)); var updateableMods = GetUpdateableMods(mods); if (updateableMods.Count == 0) { AnsiConsole.MarkupLine("[green]All FCP mods are up to date![/]"); await RenderAppUpdateNoticeAsync(updateCheckTask); return 0; } AnsiConsole.MarkupLine($"[yellow]Found {updateableMods.Count} mod(s) with updates available:[/]"); foreach (InstalledMod mod in updateableMods) { AnsiConsole.MarkupLine($" • {mod.Name} [grey]({mod.CommitsBehind} commits behind)[/]"); } AnsiConsole.WriteLine(); var results = await ProgressReporter.WithBatchProgressAsync( "Updating mods", updateableMods, installedMod => installedMod.Name, async (mod, progress) => await GitModUpdater.UpdateAsync(gitService, mod, progress, ct)); ModTableRenderer.RenderUpdateSummary(results); await RenderAppUpdateNoticeAsync(updateCheckTask); var failCount = results.Count(r => !r.Success); return failCount > 0 ? 1 : 0; } catch (OperationCanceledException) { AnsiConsole.MarkupLine("[grey]Operation cancelled.[/]"); return 1; } catch (Exception ex) { AnsiConsole.WriteException(ex); return 1; } } private static async Task RenderAppUpdateNoticeAsync(Task updateCheckTask) { UpdateCheckResult? updateResult = await updateCheckTask; if (updateResult is null) return; AnsiConsole.WriteLine(); AppUpdateNoticeRenderer.Render(updateResult); } }