mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
commit
b7e848b7a2
@ -1,28 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.CommandLine.NamingConventionBinder;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Wabbajack.CLI.Verbs;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Services.OSIntegrated;
|
||||
|
||||
namespace Wabbajack.CLI;
|
||||
|
||||
public class CommandLineBuilder
|
||||
public partial class CommandLineBuilder
|
||||
{
|
||||
private readonly IConsole _console;
|
||||
private readonly IEnumerable<IVerb> _verbs;
|
||||
private static IServiceProvider _provider;
|
||||
|
||||
public CommandLineBuilder(IEnumerable<IVerb> verbs, IConsole console, LoggingRateLimiterReporter _)
|
||||
public CommandLineBuilder(IServiceProvider provider, IConsole console, LoggingRateLimiterReporter _)
|
||||
{
|
||||
_console = console;
|
||||
_verbs = verbs;
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
static CommandLineBuilder()
|
||||
{
|
||||
RegisterAll();
|
||||
}
|
||||
|
||||
public async Task<int> Run(string[] args)
|
||||
{
|
||||
var root = new RootCommand();
|
||||
foreach (var verb in _verbs)
|
||||
root.Add(verb.MakeCommand());
|
||||
foreach (var verb in _commands)
|
||||
{
|
||||
root.Add(MakeCommend(verb.Type, verb.Handler, verb.Definition));
|
||||
}
|
||||
|
||||
return await root.InvokeAsync(args);
|
||||
}
|
||||
|
||||
private static Dictionary<Type, Func<OptionDefinition, Option>> _optionCtors = new()
|
||||
{
|
||||
{
|
||||
typeof(string),
|
||||
d => new Option<string>(d.Aliases, description: d.Description)
|
||||
},
|
||||
{
|
||||
typeof(AbsolutePath),
|
||||
d => new Option<AbsolutePath>(d.Aliases, description: d.Description, parseArgument: d => d.Tokens.Single().Value.ToAbsolutePath())
|
||||
},
|
||||
{
|
||||
typeof(Uri),
|
||||
d => new Option<Uri>(d.Aliases, description: d.Description)
|
||||
},
|
||||
{
|
||||
typeof(bool),
|
||||
d => new Option<bool>(d.Aliases, description: d.Description)
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
private Command MakeCommend(Type verbType, Func<IVerb, Delegate> verbHandler, VerbDefinition definition)
|
||||
{
|
||||
var command = new Command(definition.Name, definition.Description);
|
||||
foreach (var option in definition.Options)
|
||||
{
|
||||
command.Add(_optionCtors[option.Type](option));
|
||||
}
|
||||
command.Handler = new HandlerDelegate(_provider, verbType, verbHandler);
|
||||
return command;
|
||||
}
|
||||
|
||||
private class HandlerDelegate : ICommandHandler
|
||||
{
|
||||
private IServiceProvider _provider;
|
||||
private Type _type;
|
||||
private readonly Func<IVerb, Delegate> _delgate;
|
||||
|
||||
public HandlerDelegate(IServiceProvider provider, Type type, Func<IVerb, Delegate> inner)
|
||||
{
|
||||
_provider = provider;
|
||||
_type = type;
|
||||
_delgate = inner;
|
||||
}
|
||||
public int Invoke(InvocationContext context)
|
||||
{
|
||||
var service = (IVerb)_provider.GetRequiredService(_type);
|
||||
var handler = CommandHandler.Create(_delgate(service));
|
||||
return handler.Invoke(context);
|
||||
}
|
||||
|
||||
public Task<int> InvokeAsync(InvocationContext context)
|
||||
{
|
||||
var service = (IVerb)_provider.GetRequiredService(_type);
|
||||
var handler = CommandHandler.Create(_delgate(service));
|
||||
return handler.InvokeAsync(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<(Type Type, VerbDefinition Definition, Func<IVerb, Delegate> Handler)> _commands { get; set; } = new();
|
||||
public static IEnumerable<Type> Verbs => _commands.Select(c => c.Type);
|
||||
public static void RegisterCommand<T>(VerbDefinition definition, Func<IVerb, Delegate> handler)
|
||||
{
|
||||
_commands.Add((typeof(T), definition, handler));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public record OptionDefinition(Type Type, string ShortOption, string LongOption, string Description)
|
||||
{
|
||||
public string[] Aliases
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[] { "-" + ShortOption, "--" + LongOption };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record VerbDefinition(string Name, string Description, OptionDefinition[] Options)
|
||||
{
|
||||
}
|
||||
|
||||
public static class CommandLineBuilderExtensions
|
||||
{
|
||||
public static IServiceCollection AddCommands(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<CommandLineBuilder>();
|
||||
|
||||
foreach (var verb in CommandLineBuilder.Verbs)
|
||||
{
|
||||
services.AddSingleton(verb);
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
@ -57,32 +57,7 @@ internal class Program
|
||||
|
||||
|
||||
services.AddTransient<Context>();
|
||||
services.AddSingleton<IVerb, HashFile>();
|
||||
services.AddSingleton<IVerb, VFSIndexFolder>();
|
||||
services.AddSingleton<IVerb, Encrypt>();
|
||||
services.AddSingleton<IVerb, Decrypt>();
|
||||
services.AddSingleton<IVerb, ValidateLists>();
|
||||
services.AddSingleton<IVerb, DownloadCef>();
|
||||
services.AddSingleton<IVerb, DownloadUrl>();
|
||||
services.AddSingleton<IVerb, GenerateMetricsReports>();
|
||||
services.AddSingleton<IVerb, ForceHeal>();
|
||||
services.AddSingleton<IVerb, MirrorFile>();
|
||||
services.AddSingleton<IVerb, SteamLogin>();
|
||||
services.AddSingleton<IVerb, SteamAppDumpInfo>();
|
||||
services.AddSingleton<IVerb, SteamDownloadFile>();
|
||||
services.AddSingleton<IVerb, UploadToNexus>();
|
||||
services.AddSingleton<IVerb, ListCreationClubContent>();
|
||||
services.AddSingleton<IVerb, ListModlists>();
|
||||
services.AddSingleton<IVerb, Extract>();
|
||||
services.AddSingleton<IVerb, DumpZipInfo>();
|
||||
services.AddSingleton<IVerb, Install>();
|
||||
services.AddSingleton<IVerb, Compile>();
|
||||
services.AddSingleton<IVerb, InstallCompileInstallVerify>();
|
||||
services.AddSingleton<IVerb, HashUrlString>();
|
||||
services.AddSingleton<IVerb, DownloadAll>();
|
||||
services.AddSingleton<IVerb, ModlistReport>();
|
||||
services.AddSingleton<IVerb, ListGames>();
|
||||
|
||||
services.AddCommands();
|
||||
services.AddSingleton<IUserInterventionHandler, UserInterventionHandler>();
|
||||
}).Build();
|
||||
|
||||
|
32
Wabbajack.CLI/VerbRegistration.cs
Normal file
32
Wabbajack.CLI/VerbRegistration.cs
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
namespace Wabbajack.CLI;
|
||||
using Wabbajack.CLI.Verbs;
|
||||
|
||||
public partial class CommandLineBuilder {
|
||||
|
||||
private static void RegisterAll() {
|
||||
RegisterCommand<Compile>(Compile.Definition, c => ((Compile)c).Run);
|
||||
RegisterCommand<Decrypt>(Decrypt.Definition, c => ((Decrypt)c).Run);
|
||||
RegisterCommand<DownloadAll>(DownloadAll.Definition, c => ((DownloadAll)c).Run);
|
||||
RegisterCommand<DownloadUrl>(DownloadUrl.Definition, c => ((DownloadUrl)c).Run);
|
||||
RegisterCommand<DumpZipInfo>(DumpZipInfo.Definition, c => ((DumpZipInfo)c).Run);
|
||||
RegisterCommand<Encrypt>(Encrypt.Definition, c => ((Encrypt)c).Run);
|
||||
RegisterCommand<Extract>(Extract.Definition, c => ((Extract)c).Run);
|
||||
RegisterCommand<ForceHeal>(ForceHeal.Definition, c => ((ForceHeal)c).Run);
|
||||
RegisterCommand<HashFile>(HashFile.Definition, c => ((HashFile)c).Run);
|
||||
RegisterCommand<HashUrlString>(HashUrlString.Definition, c => ((HashUrlString)c).Run);
|
||||
RegisterCommand<Install>(Install.Definition, c => ((Install)c).Run);
|
||||
RegisterCommand<InstallCompileInstallVerify>(InstallCompileInstallVerify.Definition, c => ((InstallCompileInstallVerify)c).Run);
|
||||
RegisterCommand<ListCreationClubContent>(ListCreationClubContent.Definition, c => ((ListCreationClubContent)c).Run);
|
||||
RegisterCommand<ListGames>(ListGames.Definition, c => ((ListGames)c).Run);
|
||||
RegisterCommand<ListModlists>(ListModlists.Definition, c => ((ListModlists)c).Run);
|
||||
RegisterCommand<MirrorFile>(MirrorFile.Definition, c => ((MirrorFile)c).Run);
|
||||
RegisterCommand<ModlistReport>(ModlistReport.Definition, c => ((ModlistReport)c).Run);
|
||||
RegisterCommand<SteamDownloadFile>(SteamDownloadFile.Definition, c => ((SteamDownloadFile)c).Run);
|
||||
RegisterCommand<SteamDumpAppInfo>(SteamDumpAppInfo.Definition, c => ((SteamDumpAppInfo)c).Run);
|
||||
RegisterCommand<SteamLogin>(SteamLogin.Definition, c => ((SteamLogin)c).Run);
|
||||
RegisterCommand<UploadToNexus>(UploadToNexus.Definition, c => ((UploadToNexus)c).Run);
|
||||
RegisterCommand<ValidateLists>(ValidateLists.Definition, c => ((ValidateLists)c).Run);
|
||||
RegisterCommand<VFSIndex>(VFSIndex.Definition, c => ((VFSIndex)c).Run);
|
||||
}
|
||||
}
|
26
Wabbajack.CLI/VerbRegistration.tt
Normal file
26
Wabbajack.CLI/VerbRegistration.tt
Normal file
@ -0,0 +1,26 @@
|
||||
<#@ template language="C#v3.5" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.IO"#>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
|
||||
namespace Wabbajack.CLI;
|
||||
using Wabbajack.CLI.Verbs;
|
||||
|
||||
public partial class CommandLineBuilder {
|
||||
|
||||
private static void RegisterAll() {
|
||||
<#
|
||||
foreach (var verb in Directory.EnumerateFiles("Verbs"))
|
||||
{
|
||||
var klass = verb.Split('\\').Last().Split('.').First();
|
||||
if (klass == "IVerb") continue;
|
||||
#>
|
||||
RegisterCommand<<#=klass#>>(<#=klass#>.Definition, c => ((<#=klass#>)c).Run);
|
||||
<#
|
||||
}
|
||||
|
||||
#>
|
||||
}
|
||||
}
|
@ -38,16 +38,12 @@ public class Compile : IVerb
|
||||
_inferencer = inferencer;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new("compile", "Compiles a modlist",
|
||||
new[]
|
||||
{
|
||||
var command = new Command("compile");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-i", "-installPath"}, "Install Path"));
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-o", "-output"}, "Output"));
|
||||
command.Description = "Installs a modlist, compiles it, installs it again, verifies it";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
|
||||
new OptionDefinition(typeof(AbsolutePath), "i", "installPath", "Install Path"),
|
||||
new OptionDefinition(typeof(AbsolutePath), "o", "outputPath", "OutputPath")
|
||||
});
|
||||
public async Task<int> Run(AbsolutePath installPath, AbsolutePath outputPath,
|
||||
CancellationToken token)
|
||||
{
|
||||
|
@ -19,15 +19,13 @@ public class Decrypt : IVerb
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new VerbDefinition("decrypt",
|
||||
"Decrypts a file from the wabbajack encrypted storage",
|
||||
new[]
|
||||
{
|
||||
var command = new Command("decrypt");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-o", "-output"}, "Output file path"));
|
||||
command.Add(new Option<string>(new[] {"-n", "-name"}, "Name of the key to load data from"));
|
||||
command.Description = "Decrypts a file from the Wabbajack encrypted storage";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "o", "output", "Output file path"),
|
||||
new OptionDefinition(typeof(string), "n", "name", "Name of the key to load data from")
|
||||
});
|
||||
|
||||
public async Task<int> Run(AbsolutePath output, string name)
|
||||
{
|
||||
|
@ -41,16 +41,14 @@ public class DownloadAll : IVerb
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new VerbDefinition("download-all",
|
||||
"Downloads all files for all modlists in the gallery",
|
||||
new[]
|
||||
{
|
||||
var command = new Command("download-all");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-o", "-output"}, "Output folder"));
|
||||
command.Description = "Downloads all files for all modlists in the gallery";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "o", "output", "Output folder")
|
||||
});
|
||||
|
||||
private async Task<int> Run(AbsolutePath output, CancellationToken token)
|
||||
internal async Task<int> Run(AbsolutePath output, CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("Downloading modlists");
|
||||
|
||||
|
@ -1,115 +0,0 @@
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.CommandLine.NamingConventionBinder;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ICSharpCode.SharpZipLib.BZip2;
|
||||
using ICSharpCode.SharpZipLib.Tar;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.CLI.DTOs;
|
||||
using Wabbajack.Downloaders;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
using Version = System.Version;
|
||||
|
||||
namespace Wabbajack.CLI.Verbs;
|
||||
|
||||
public class DownloadCef : IVerb
|
||||
{
|
||||
private readonly DownloadDispatcher _dispatcher;
|
||||
private readonly FileExtractor.FileExtractor _fileExtractor;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<DownloadCef> _logger;
|
||||
|
||||
public DownloadCef(ILogger<DownloadCef> logger, DownloadDispatcher dispatcher,
|
||||
FileExtractor.FileExtractor fileExtractor, HttpClient httpClient)
|
||||
{
|
||||
_logger = logger;
|
||||
_dispatcher = dispatcher;
|
||||
_fileExtractor = fileExtractor;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
{
|
||||
var command = new Command("download-cef");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-f", "-folder"}, "Path to Wabbajack"));
|
||||
command.Add(new Option<bool>(new[] {"--force"}, "Force the download even if the output already exists"));
|
||||
command.Description = "Downloads CEF into this folder";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
|
||||
|
||||
public async Task<int> Run(AbsolutePath folder, bool force = false)
|
||||
{
|
||||
if (folder == default) folder = KnownFolders.EntryPoint;
|
||||
|
||||
var cefNet = folder.Combine("CefNet.dll");
|
||||
if (!cefNet.FileExists())
|
||||
{
|
||||
_logger.LogError("Cannot find CefNet.dll in {folder}", folder);
|
||||
return 1;
|
||||
}
|
||||
|
||||
var version = Version.Parse(FileVersionInfo.GetVersionInfo(cefNet.ToString()).FileVersion!);
|
||||
var downloadVersion = $"{version.Major}.{version.Minor}";
|
||||
var runtime = RuntimeInformation.RuntimeIdentifier;
|
||||
if (folder.Combine("libcef.dll").FileExists() && !force)
|
||||
{
|
||||
_logger.LogInformation("Not downloading, cef already exists");
|
||||
return 0;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Downloading Cef version {version} for {runtime}", downloadVersion, runtime);
|
||||
|
||||
var versions = await CefCDNResponse.Load(_httpClient);
|
||||
|
||||
var findSource = versions.FindSource(downloadVersion);
|
||||
|
||||
var fileUri = new Uri($"https://cef-builds.spotifycdn.com/{findSource.Name}");
|
||||
|
||||
var parsed = _dispatcher.Parse(fileUri);
|
||||
var tempFile = folder.Combine(findSource.Name);
|
||||
await _dispatcher.Download(new Archive {State = parsed!}, tempFile, CancellationToken.None);
|
||||
|
||||
{
|
||||
_logger.LogInformation("Extracting {file}", tempFile);
|
||||
|
||||
await using var istream = tempFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
await using var bzip2 = new BZip2InputStream(istream);
|
||||
await using var tar = new TarInputStream(bzip2, Encoding.UTF8);
|
||||
var prefix = tempFile.FileName.WithoutExtension().WithoutExtension().Combine("Release");
|
||||
var fullPrefix = prefix.RelativeTo(folder);
|
||||
while (true)
|
||||
{
|
||||
var entry = tar.GetNextEntry();
|
||||
if (entry == null) break;
|
||||
|
||||
var path = entry.Name.ToRelativePath();
|
||||
|
||||
if (path.InFolder(prefix) && entry.Size > 0)
|
||||
{
|
||||
var outputPath = path.RelativeTo(folder).RelativeTo(fullPrefix).RelativeTo(folder);
|
||||
outputPath.Parent.CreateDirectory();
|
||||
|
||||
_logger.LogInformation("Extracting {FileName} to {Folder}", outputPath.FileName,
|
||||
outputPath.RelativeTo(folder));
|
||||
await using var os = outputPath.Open(FileMode.Create, FileAccess.Write);
|
||||
tar.CopyEntryContents(os);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tempFile.Delete();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -24,18 +24,15 @@ public class DownloadUrl : IVerb
|
||||
_dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new VerbDefinition("download-url", "Downloads a file to a given output",
|
||||
new[]
|
||||
{
|
||||
var command = new Command("download-url");
|
||||
command.Add(new Option<Uri>(new[] {"-u", "-url"}, "Url to parse"));
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-o", "-output"}, "Output file"));
|
||||
command.Add(new Option<bool>(new [] {"-p", "--proxy"}, "Use the Wabbajack Proxy (default: true)"));
|
||||
command.Description = "Downloads a file to a given output";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(Uri), "u", "url", "Url to parse"),
|
||||
new OptionDefinition(typeof(AbsolutePath), "o", "output", "Output File"),
|
||||
new OptionDefinition(typeof(bool), "p", "proxy", "Use the Wabbajack Proxy (default true)")
|
||||
});
|
||||
|
||||
private async Task<int> Run(Uri url, AbsolutePath output, bool proxy = true)
|
||||
internal async Task<int> Run(Uri url, AbsolutePath output, bool proxy = true)
|
||||
{
|
||||
var parsed = _dispatcher.Parse(url);
|
||||
if (parsed == null)
|
||||
|
@ -25,17 +25,15 @@ public class DumpZipInfo : IVerb
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new("dump-zip-info",
|
||||
"Dumps the contents of a zip file to the console, for use in debugging wabbajack files",
|
||||
new[]
|
||||
{
|
||||
var command = new Command("dump-zip-info");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-i", "-input"}, "Zip file ot parse"));
|
||||
command.Add(new Option<bool>(new[] {"-t", "-test"}, "Test extracting each file"));
|
||||
command.Description = "Dumps the contents of a zip file to the console, for use in debugging wabbajack files";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "i", "input", "Zip file to parse"),
|
||||
new OptionDefinition(typeof(bool), "t", "test", "Test extracting each file")
|
||||
});
|
||||
|
||||
private async Task<int> Run(AbsolutePath input, bool test)
|
||||
internal async Task<int> Run(AbsolutePath input, bool test)
|
||||
{
|
||||
await using var ar = new ZipReader(input.Open(FileMode.Open), false);
|
||||
foreach (var value in (await ar.GetFiles()))
|
||||
|
@ -18,17 +18,15 @@ public class Encrypt : IVerb
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new("encrypt",
|
||||
"Encrypts a file and stores it in the Wabbajack encrypted storage",
|
||||
new[]
|
||||
{
|
||||
var command = new Command("encrypt");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-i", "-input"}, "Path to the file to enrypt"));
|
||||
command.Add(new Option<string>(new[] {"-n", "-name"}, "Name of the key to store the data into"));
|
||||
command.Description = "Encrypts a file and stores it in the Wabbajack encrypted storage";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "i", "input", "Path to the file to encrypt"),
|
||||
new OptionDefinition(typeof(string), "n", "name", "Name of the key to store the data into")
|
||||
});
|
||||
|
||||
public async Task<int> Run(AbsolutePath input, string name)
|
||||
internal async Task<int> Run(AbsolutePath input, string name)
|
||||
{
|
||||
var data = await input.ReadAllBytesAsync();
|
||||
_logger.LogInformation("Encrypting {bytes} bytes into `{key}`", data.Length, name);
|
||||
|
@ -25,17 +25,14 @@ public class Extract : IVerb
|
||||
_extractor = extractor;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new("extract",
|
||||
"Extracts the contents of an archive into a folder", new[]
|
||||
{
|
||||
var command = new Command("extract");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-i", "-input"}, "Input Archive"));
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-o", "-output"}, "Output folder"));
|
||||
command.Description = "Extracts the contents of an archive into a folder";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "i", "input", "Input Archive"),
|
||||
new OptionDefinition(typeof(AbsolutePath), "o", "output", "Output Folder")
|
||||
});
|
||||
|
||||
private async Task<int> Run(AbsolutePath input, AbsolutePath output, CancellationToken token)
|
||||
internal async Task<int> Run(AbsolutePath input, AbsolutePath output, CancellationToken token)
|
||||
{
|
||||
if (!output.DirectoryExists())
|
||||
output.Parent.CreateDirectory();
|
||||
|
@ -43,15 +43,13 @@ public class ForceHeal : IVerb
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new("force-heal",
|
||||
"Creates a patch from New file to Old file and uploads it",
|
||||
new[]
|
||||
{
|
||||
var command = new Command("force-heal");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-n", "-new-file"}, "New File"));
|
||||
command.Add(new Option<string>(new[] {"-o", "-old-file"}, "Old File"));
|
||||
command.Description = "Creates a patch from New file to Old File and uploads it";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "n", "new-file", "New file"),
|
||||
new OptionDefinition(typeof(AbsolutePath), "o", "old-file", "Old File")
|
||||
});
|
||||
|
||||
public async Task<int> Run(AbsolutePath oldFile, AbsolutePath newFile)
|
||||
{
|
||||
|
@ -1,69 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.CommandLine.NamingConventionBinder;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
using Wabbajack.DTOs.ServerResponses;
|
||||
using Wabbajack.Paths;
|
||||
|
||||
namespace Wabbajack.CLI.Verbs;
|
||||
|
||||
public class GenerateMetricsReports : IVerb
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
private readonly DTOSerializer _dtos;
|
||||
|
||||
public GenerateMetricsReports(HttpClient client, DTOSerializer dtos)
|
||||
{
|
||||
_client = client;
|
||||
_dtos = dtos;
|
||||
}
|
||||
public Command MakeCommand()
|
||||
{
|
||||
var command = new Command("generate-metrics-report");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-o", "-output"}, "Output folder"));
|
||||
command.Description = "Generates usage metrics and outputs a html report about them";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
|
||||
|
||||
}
|
||||
|
||||
private async Task<int> Run(AbsolutePath output)
|
||||
{
|
||||
var subjects = await GetMetrics("one day ago", "now", "finish_install")
|
||||
.Where(d => d.Action != d.Subject)
|
||||
.Select(async d => d.GroupingSubject)
|
||||
.ToHashSet();
|
||||
|
||||
var allTime = await GetMetrics("10 days ago", "now", "finish_install")
|
||||
.Where(d => subjects.Contains(d.GroupingSubject))
|
||||
.ToList();
|
||||
|
||||
var grouped = allTime.GroupBy(g => (g.Timestamp.ToString("yyyy-MM-dd"), g.GroupingSubject)).ToArray();
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<MetricResult> GetMetrics(string start, string end, string action)
|
||||
{
|
||||
await using var response = await _client.GetStreamAsync(new Uri($"https://build.wabbajack.org/metrics/report?action={action}&from={start}&end={end}"));
|
||||
|
||||
var sr = new StreamReader(response, leaveOpen: false);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var line = await sr.ReadLineAsync();
|
||||
if (line == null) break;
|
||||
yield return _dtos.Deserialize<MetricResult>(line)!;
|
||||
}
|
||||
}
|
||||
}
|
@ -20,15 +20,11 @@ public class HashFile : IVerb
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new VerbDefinition("hash-file",
|
||||
"Hashes a file with Wabbajack's hashing routines", new[]
|
||||
{
|
||||
var command = new Command("hash-file");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-i", "-input"}, "Path to the file to hash"));
|
||||
command.Description = "Hashes a file with Wabbajack's xxHash64 implementation";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
|
||||
new OptionDefinition(typeof(AbsolutePath), "i", "input", "Path to the file to hash")
|
||||
});
|
||||
|
||||
public async Task<int> Run(AbsolutePath input)
|
||||
{
|
||||
|
@ -17,15 +17,11 @@ public class HashUrlString : IVerb
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new VerbDefinition("hash-url-string",
|
||||
"Hashes a URL string and returns the hashcode as hex", new[]
|
||||
{
|
||||
var command = new Command("hash-url-string");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-u", "-url"}, "Url string to hash"));
|
||||
command.Description = "Hashes a URL string and returns the hashcode as hex";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
|
||||
new OptionDefinition(typeof(AbsolutePath), "u", "url", "Url string to hash")
|
||||
});
|
||||
|
||||
public async Task<int> Run(string u)
|
||||
{
|
||||
|
@ -4,5 +4,4 @@ namespace Wabbajack.CLI.Verbs;
|
||||
|
||||
public interface IVerb
|
||||
{
|
||||
public Command MakeCommand();
|
||||
}
|
@ -39,18 +39,16 @@ public class Install : IVerb
|
||||
_cache = cache;
|
||||
_gameLocator = gameLocator;
|
||||
}
|
||||
public Command MakeCommand()
|
||||
|
||||
public static VerbDefinition Definition = new VerbDefinition("install", "Installs a wabbajack file", new[]
|
||||
{
|
||||
var command = new Command("install");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-w", "-wabbajack"}, "Wabbajack file"));
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-m", "-machineUrl"}, "Machine url to download"));
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-o", "-output"}, "Output path"));
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-d", "-downloads"}, "Downloads path"));
|
||||
command.Description = "Installs a wabbajack file";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
public async Task<int> Run(AbsolutePath wabbajack, AbsolutePath output, AbsolutePath downloads, string machineUrl, CancellationToken token)
|
||||
new OptionDefinition(typeof(AbsolutePath), "w", "wabbajack", "Wabbajack file"),
|
||||
new OptionDefinition(typeof(string), "m", "machineUrl", "Machine url to download"),
|
||||
new OptionDefinition(typeof(AbsolutePath), "o", "output", "Output path"),
|
||||
new OptionDefinition(typeof(AbsolutePath), "d", "downloads", "Downloads path")
|
||||
});
|
||||
|
||||
internal async Task<int> Run(AbsolutePath wabbajack, AbsolutePath output, AbsolutePath downloads, string machineUrl, CancellationToken token)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(machineUrl))
|
||||
{
|
||||
|
@ -47,16 +47,13 @@ public class InstallCompileInstallVerify : IVerb
|
||||
_inferencer = inferencer;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new VerbDefinition("install-compile-install-verify",
|
||||
"Installs a modlist, compiles it, installs it again, verifies it", new[]
|
||||
{
|
||||
var command = new Command("install-compile-install-verify");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-m", "-machineUrls"}, "Machine url(s) to download"));
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-d", "-downloads"}, "Downloads path"));
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-o", "-outputs"}, "Outputs path"));
|
||||
command.Description = "Installs a modlist, compiles it, installs it again, verifies it";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "m", "machineUrl", "Machine url(s) to download"),
|
||||
new OptionDefinition(typeof(AbsolutePath), "d", "downloads", "Downloads path"),
|
||||
new OptionDefinition(typeof(AbsolutePath), "o", "outputs", "Output paths")
|
||||
});
|
||||
|
||||
public async Task<int> Run(AbsolutePath outputs, AbsolutePath downloads, IEnumerable<string> machineUrls, CancellationToken token)
|
||||
{
|
||||
|
@ -29,13 +29,9 @@ public class ListCreationClubContent : IVerb
|
||||
_client = wjClient;
|
||||
_downloader = downloader;
|
||||
}
|
||||
public Command MakeCommand()
|
||||
{
|
||||
var command = new Command("list-creation-club-content");
|
||||
command.Description = "Lists all known creation club content";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
|
||||
public static VerbDefinition Definition =
|
||||
new("list-creation-club-content", "Lists all known creation club content", Array.Empty<OptionDefinition>());
|
||||
|
||||
public async Task<int> Run(CancellationToken token)
|
||||
{
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.CommandLine.NamingConventionBinder;
|
||||
@ -22,15 +23,11 @@ public class ListGames : IVerb
|
||||
_logger = logger;
|
||||
_locator = locator;
|
||||
}
|
||||
public Command MakeCommand()
|
||||
{
|
||||
var command = new Command("list-games");
|
||||
command.Description = "Lists all games Wabbajack recognizes, and their installed versions/locations (if any)";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
|
||||
public async Task<int> Run(CancellationToken token)
|
||||
public static VerbDefinition Definition = new VerbDefinition("list-games",
|
||||
"Lists all games Wabbajack recognizes, and their installed versions/locations (if any)", Array.Empty<OptionDefinition>());
|
||||
|
||||
internal async Task<int> Run(CancellationToken token)
|
||||
{
|
||||
foreach (var game in GameRegistry.Games.OrderBy(g => g.Value.HumanFriendlyGameName))
|
||||
{
|
||||
|
@ -24,13 +24,9 @@ public class ListModlists : IVerb
|
||||
_logger = logger;
|
||||
_client = wjClient;
|
||||
}
|
||||
public Command MakeCommand()
|
||||
{
|
||||
var command = new Command("list-modlists");
|
||||
command.Description = "Lists all known modlists";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
|
||||
public static VerbDefinition Definition =
|
||||
new("list-modlists", "Lists all known modlists", Array.Empty<OptionDefinition>());
|
||||
|
||||
public async Task<int> Run(CancellationToken token)
|
||||
{
|
||||
|
@ -18,15 +18,12 @@ public class MirrorFile : IVerb
|
||||
_logger = logger;
|
||||
_client = wjClient;
|
||||
}
|
||||
public Command MakeCommand()
|
||||
{
|
||||
var command = new Command("mirror-file");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-i", "-input"}, "File to Mirror"));
|
||||
command.Description = "Mirrors a file to the Wabbajack CDN";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
|
||||
public static VerbDefinition Definition = new("mirror-file", "Mirrors a file to the Wabbajack CDN",
|
||||
new[]
|
||||
{
|
||||
new OptionDefinition(typeof(AbsolutePath), "i", "input", "File to Mirror")
|
||||
});
|
||||
public async Task<int> Run(AbsolutePath input)
|
||||
{
|
||||
_logger.LogInformation("Generating File Definition for {Name}", input.FileName);
|
||||
|
@ -28,14 +28,12 @@ public class ModlistReport : IVerb
|
||||
_logger = logger;
|
||||
_dtos = dtos;
|
||||
}
|
||||
public Command MakeCommand()
|
||||
|
||||
public static VerbDefinition Definition = new("modlist-report",
|
||||
"Generates a usage report for a Modlist file", new[]
|
||||
{
|
||||
var command = new Command("modlist-report");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-i", "-input"}, "Wabbajack file from which to generate a report"));
|
||||
command.Description = "Generates a usage report for a Modlist file";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "i", "input", "Wabbajack file from which to generate a report")
|
||||
});
|
||||
|
||||
private static async Task<string> ReportTemplate(object o)
|
||||
{
|
||||
|
@ -34,21 +34,18 @@ public class SteamDownloadFile : IVerb
|
||||
_dtos = dtos;
|
||||
_wjClient = wjClient;
|
||||
}
|
||||
public Command MakeCommand()
|
||||
|
||||
public static VerbDefinition Definition = new VerbDefinition("steam-download-file",
|
||||
"Dumps information to the console about the given app",
|
||||
new[]
|
||||
{
|
||||
var command = new Command("steam-download-file");
|
||||
command.Description = "Dumps information to the console about the given app";
|
||||
new OptionDefinition(typeof(string), "g", "game", "Wabbajack game name"),
|
||||
new OptionDefinition(typeof(string), "v", "version", "Version of the game to download for"),
|
||||
new OptionDefinition(typeof(string), "f", "file", "File to download (relative path)"),
|
||||
new OptionDefinition(typeof(string), "o", "output", "Output location")
|
||||
});
|
||||
|
||||
command.Add(new Option<string>(new[] {"-g", "-game", "-gameName"}, "Wabbajack game name"));
|
||||
|
||||
command.Add(new Option<string>(new[] {"-v", "-version"}, "Version of the game to download for"));
|
||||
command.Add(new Option<string>(new[] {"-f", "-file"}, "File to download (relative path)"));
|
||||
command.Add(new Option<string>(new[] {"-o", "-output"}, "Output location"));
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
|
||||
private async Task<int> Run(string gameName, string version, string file, AbsolutePath output)
|
||||
internal async Task<int> Run(string gameName, string version, string file, AbsolutePath output)
|
||||
{
|
||||
if (!GameRegistry.TryGetByFuzzyName(gameName, out var game))
|
||||
_logger.LogError("Can't find definition for {Game}", gameName);
|
||||
|
@ -15,15 +15,15 @@ using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace Wabbajack.CLI.Verbs;
|
||||
|
||||
public class SteamAppDumpInfo : IVerb
|
||||
public class SteamDumpAppInfo : IVerb
|
||||
{
|
||||
private readonly ILogger<SteamAppDumpInfo> _logger;
|
||||
private readonly ILogger<SteamDumpAppInfo> _logger;
|
||||
private readonly Client _client;
|
||||
private readonly ITokenProvider<SteamLoginState> _token;
|
||||
private readonly DepotDownloader _downloader;
|
||||
private readonly DTOSerializer _dtos;
|
||||
|
||||
public SteamAppDumpInfo(ILogger<SteamAppDumpInfo> logger, Client steamClient, ITokenProvider<SteamLoginState> token,
|
||||
public SteamDumpAppInfo(ILogger<SteamDumpAppInfo> logger, Client steamClient, ITokenProvider<SteamLoginState> token,
|
||||
DepotDownloader downloader, DTOSerializer dtos)
|
||||
{
|
||||
_logger = logger;
|
||||
@ -32,6 +32,13 @@ public class SteamAppDumpInfo : IVerb
|
||||
_downloader = downloader;
|
||||
_dtos = dtos;
|
||||
}
|
||||
|
||||
public static VerbDefinition Definition = new VerbDefinition("steam-app-dump-info",
|
||||
"Dumps information to the console about the given app", new[]
|
||||
{
|
||||
new OptionDefinition(typeof(string), "g", "game", "Wabbajack game name")
|
||||
});
|
||||
|
||||
public Command MakeCommand()
|
||||
{
|
||||
var command = new Command("steam-app-dump-info");
|
||||
|
@ -22,15 +22,12 @@ public class SteamLogin : IVerb
|
||||
_client = steamClient;
|
||||
_token = token;
|
||||
}
|
||||
public Command MakeCommand()
|
||||
{
|
||||
var command = new Command("steam-login");
|
||||
command.Description = "Logs into Steam via interactive prompts";
|
||||
|
||||
command.Add(new Option<string>(new[] {"-u", "-user"}, "Username for login"));
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
public static VerbDefinition Definition = new("steam-login",
|
||||
"Logs into Steam via interactive prompts", new[]
|
||||
{
|
||||
new OptionDefinition(typeof(string), "u", "user", "Username for login")
|
||||
});
|
||||
|
||||
public async Task<int> Run(string user)
|
||||
{
|
||||
|
@ -26,14 +26,13 @@ public class UploadToNexus : IVerb
|
||||
_client = wjClient;
|
||||
_dtos = dtos;
|
||||
}
|
||||
public Command MakeCommand()
|
||||
|
||||
public static VerbDefinition Definition = new("upload-to-nexus",
|
||||
"Uploads a file to the Nexus defined by the given .json definition file", new[]
|
||||
{
|
||||
var command = new Command("upload-to-nexus");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-d", "-definition"}, "Definition JSON file"));
|
||||
command.Description = "Uploads a file to the Nexus defined by the given .json definition file";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "d", "definition", "Definition JSON file")
|
||||
});
|
||||
|
||||
|
||||
public async Task<int> Run(AbsolutePath definition)
|
||||
{
|
||||
|
@ -8,24 +8,20 @@ using Wabbajack.VFS;
|
||||
|
||||
namespace Wabbajack.CLI.Verbs;
|
||||
|
||||
public class VFSIndexFolder : IVerb
|
||||
public class VFSIndex : IVerb
|
||||
{
|
||||
private readonly Context _context;
|
||||
|
||||
public VFSIndexFolder(Context context)
|
||||
public VFSIndex(Context context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new VerbDefinition("vfs-index",
|
||||
"Index and cache the contents of a folder", new[]
|
||||
{
|
||||
var command = new Command("vfs-index");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-f", "--folder"}, "Folder to index"));
|
||||
command.Description = "Index and cache the contents of a folder";
|
||||
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "f", "folder", "Folder to index")
|
||||
});
|
||||
|
||||
public async Task<int> Run(AbsolutePath folder)
|
||||
{
|
||||
|
@ -74,19 +74,11 @@ public class ValidateLists : IVerb
|
||||
_httpLimiter = httpLimiter;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
public static VerbDefinition Definition = new VerbDefinition("validate-lists",
|
||||
"Gets a list of modlists, validates them and exports a result list", new[]
|
||||
{
|
||||
var command = new Command("validate-lists");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-r", "--reports"}, "Location to store validation report outputs"));
|
||||
|
||||
command.Add(new Option<AbsolutePath>(new[] {"--other-archives"},
|
||||
"Look for files here before downloading (stored by hex hash name)")
|
||||
{IsRequired = false});
|
||||
|
||||
command.Description = "Gets a list of modlists, validates them and exports a result list";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
new OptionDefinition(typeof(AbsolutePath), "r", "reports", "Location to store validation report outputs")
|
||||
});
|
||||
|
||||
public async Task<int> Run(AbsolutePath reports, AbsolutePath otherArchives)
|
||||
{
|
||||
|
@ -40,6 +40,18 @@
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\ModlistReport.html" />
|
||||
<EmbeddedResource Include="Resources\ModlistReport.html" />
|
||||
<None Update="VerbRegistration.tt">
|
||||
<Generator>TextTemplatingFileGenerator</Generator>
|
||||
<LastGenOutput>VerbRegistration.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="VerbRegistration.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>VerbRegistration.tt</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
Loading…
Reference in New Issue
Block a user