mirror of
https://github.com/FalloutCollaborationProject/FCP-Mod-Updater.git
synced 2026-07-27 17:04:05 -07:00
Version 1.0
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
using FCPModUpdater.Commands.Settings;
|
||||
using FCPModUpdater.Services;
|
||||
using FCPModUpdater.UI;
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace FCPModUpdater.Commands;
|
||||
|
||||
public class ScanCommand : AsyncCommand<ModPathSettings>
|
||||
{
|
||||
public override async Task<int> ExecuteAsync(CommandContext context, ModPathSettings settings,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Resolve mods directory
|
||||
var modsDirectory = ResolveModsDirectory(settings);
|
||||
if (modsDirectory == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
AnsiConsole.MarkupLine($"[grey]Using mods directory: {modsDirectory}[/]");
|
||||
AnsiConsole.WriteLine();
|
||||
|
||||
// Check for git
|
||||
var gitService = new GitService();
|
||||
if (!await gitService.IsGitInstalledAsync(cancellationToken))
|
||||
{
|
||||
AnsiConsole.MarkupLine("[yellow]Warning: Git is not installed or not in PATH.[/]");
|
||||
AnsiConsole.MarkupLine("[yellow]Git-based features will be limited.[/]");
|
||||
AnsiConsole.MarkupLine("[grey]Install git from: https://git-scm.com/downloads[/]");
|
||||
AnsiConsole.WriteLine();
|
||||
}
|
||||
|
||||
// Initialize services
|
||||
var gitHubApiService = new GitHubApiService();
|
||||
var modDiscoveryService = new ModDiscoveryService(gitService, gitHubApiService);
|
||||
|
||||
// Run interactive menu
|
||||
var menu = new InteractiveMenu(
|
||||
gitService,
|
||||
gitHubApiService,
|
||||
modDiscoveryService,
|
||||
modsDirectory);
|
||||
|
||||
await menu.RunAsync(cancellationToken);
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[grey]Operation cancelled.[/]");
|
||||
return 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AnsiConsole.WriteException(ex);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static string? ResolveModsDirectory(ModPathSettings settings)
|
||||
{
|
||||
if (settings.ModDirectory != null)
|
||||
{
|
||||
return settings.ModDirectory.FullName;
|
||||
}
|
||||
|
||||
// Auto-discover
|
||||
var pathDiscovery = new PathDiscoveryService();
|
||||
var paths = pathDiscovery.DiscoverModPaths();
|
||||
|
||||
if (paths.Count == 0)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Error: Could not find RimWorld Mods folder.[/]");
|
||||
AnsiConsole.MarkupLine("[grey]Please specify the path using --directory[/]");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (paths.Count == 1)
|
||||
{
|
||||
return paths[0];
|
||||
}
|
||||
|
||||
// Multiple paths found - let user choose
|
||||
return AnsiConsole.Prompt(
|
||||
new SelectionPrompt<string>()
|
||||
.Title("[bold]Multiple RimWorld installations found. Select one:[/]")
|
||||
.PageSize(10)
|
||||
.AddChoices(paths));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.ComponentModel;
|
||||
using JetBrains.Annotations;
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace FCPModUpdater.Commands.Settings;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class ModPathSettings : CommandSettings
|
||||
{
|
||||
[CommandOption("-d|--directory")]
|
||||
[Description("Path to the RimWorld/Mods folder. If not specified, will attempt to auto-discover.")]
|
||||
public DirectoryInfo? ModDirectory { get; init; }
|
||||
|
||||
public override ValidationResult Validate()
|
||||
{
|
||||
if (ModDirectory != null && !ModDirectory.Exists)
|
||||
{
|
||||
return ValidationResult.Error($"Directory does not exist: {ModDirectory.FullName}");
|
||||
}
|
||||
|
||||
return ValidationResult.Success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
using FCPModUpdater.Commands.Settings;
|
||||
using FCPModUpdater.Models;
|
||||
using FCPModUpdater.Services;
|
||||
using FCPModUpdater.UI;
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace FCPModUpdater.Commands;
|
||||
|
||||
public class UpdateCommand : AsyncCommand<ModPathSettings>
|
||||
{
|
||||
public override async Task<int> ExecuteAsync(CommandContext context, ModPathSettings settings,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Resolve mods directory
|
||||
var modsDirectory = ResolveModsDirectory(settings);
|
||||
if (modsDirectory == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
AnsiConsole.MarkupLine($"[grey]Using mods directory: {modsDirectory}[/]");
|
||||
AnsiConsole.WriteLine();
|
||||
|
||||
// Check for git
|
||||
var gitService = new GitService();
|
||||
if (!await gitService.IsGitInstalledAsync(cancellationToken))
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Error: Git is not installed or not in PATH.[/]");
|
||||
AnsiConsole.MarkupLine("[grey]Install git from: https://git-scm.com/downloads[/]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize services
|
||||
var gitHubApiService = new GitHubApiService();
|
||||
var modDiscoveryService = new ModDiscoveryService(gitService, gitHubApiService);
|
||||
|
||||
// Discover mods
|
||||
var mods = await ProgressReporter.WithStatusAsync(
|
||||
"Scanning mods directory...",
|
||||
async () => await modDiscoveryService.DiscoverModsAsync(modsDirectory, ct: cancellationToken));
|
||||
|
||||
// Find updateable mods
|
||||
var updateableMods = mods
|
||||
.Where(m => m.Source == ModSource.Git && m.Status == ModStatus.Behind)
|
||||
.ToList();
|
||||
|
||||
if (updateableMods.Count == 0)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[green]All FCP mods are up to date![/]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
AnsiConsole.MarkupLine($"[yellow]Found {updateableMods.Count} mod(s) with updates available:[/]");
|
||||
foreach (var mod in updateableMods)
|
||||
{
|
||||
AnsiConsole.MarkupLine($" • {mod.Name} [grey]({mod.CommitsBehind} commits behind)[/]");
|
||||
}
|
||||
|
||||
AnsiConsole.WriteLine();
|
||||
|
||||
// Update all
|
||||
var results = await ProgressReporter.WithBatchProgressAsync(
|
||||
"Updating mods",
|
||||
updateableMods,
|
||||
m => m.Name,
|
||||
async (mod, progress) =>
|
||||
{
|
||||
progress.Report(25);
|
||||
var fetchOk = await gitService.FetchAsync(mod.Path, ct: cancellationToken);
|
||||
if (!fetchOk)
|
||||
return (false, "Fetch failed");
|
||||
|
||||
progress.Report(50);
|
||||
var pullOk = await gitService.PullAsync(mod.Path, ct: cancellationToken);
|
||||
progress.Report(100);
|
||||
|
||||
return (pullOk, pullOk ? null : "Pull failed");
|
||||
});
|
||||
|
||||
ModTableRenderer.RenderUpdateSummary(results);
|
||||
|
||||
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 string? ResolveModsDirectory(ModPathSettings settings)
|
||||
{
|
||||
if (settings.ModDirectory != null)
|
||||
{
|
||||
return settings.ModDirectory.FullName;
|
||||
}
|
||||
|
||||
// Auto-discover
|
||||
var pathDiscovery = new PathDiscoveryService();
|
||||
var paths = pathDiscovery.DiscoverModPaths();
|
||||
|
||||
if (paths.Count == 0)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Error: Could not find RimWorld Mods folder.[/]");
|
||||
AnsiConsole.MarkupLine("[grey]Please specify the path using --directory[/]");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (paths.Count == 1)
|
||||
{
|
||||
return paths[0];
|
||||
}
|
||||
|
||||
// Multiple paths found - use first one for non-interactive mode
|
||||
AnsiConsole.MarkupLine($"[yellow]Multiple installations found, using: {paths[0]}[/]");
|
||||
AnsiConsole.MarkupLine("[grey]Use --directory to specify a different path[/]");
|
||||
return paths[0];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user