mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Added Changelog verb
This commit is contained in:
parent
3ae5ef6d83
commit
400db704ba
@ -16,7 +16,8 @@ namespace Wabbajack.CLI
|
||||
typeof(ChangeDownload),
|
||||
typeof(ServerLog),
|
||||
typeof(MyFiles),
|
||||
typeof(DeleteFile)
|
||||
typeof(DeleteFile),
|
||||
typeof(Changelog)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ namespace Wabbajack.CLI
|
||||
(ServerLog opts) => opts.Execute(),
|
||||
(MyFiles opts) => opts.Execute(),
|
||||
(DeleteFile opts) => opts.Execute(),
|
||||
(Changelog opts) => opts.Execute(),
|
||||
errs => 1);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Wabbajack.CLI": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "change-download --input=\"S:\\Modding FAST\\Wabbajack\\downloads\" --output=\"S:\\Modding FAST\\test\" --modlist=\"S:\\Modding FAST\\Wabbajack\\profiles\\erri120's Dope Test Modlist\\modlist.txt\""
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +1,346 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using CommandLine;
|
||||
using Markdig;
|
||||
using Markdig.Syntax;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
|
||||
namespace Wabbajack.CLI.Verbs
|
||||
{
|
||||
[Verb("changelog", HelpText = "Generate a changelog using two different versions of the same Modlist.")]
|
||||
public class Changelog : AVerb
|
||||
{
|
||||
[IsFile(CustomMessage = "Modlist %1 does not exist!", Extension = Consts.ModListExtension)]
|
||||
[Option("original", Required = true, HelpText = "The original/previous modlist")]
|
||||
public string? Original { get; set; }
|
||||
|
||||
[IsFile(CustomMessage = "Modlist %1 does not exist!", Extension = Consts.ModListExtension)]
|
||||
[Option("update", Required = true, HelpText = "The current/updated modlist")]
|
||||
public string? Update { get; set; }
|
||||
|
||||
[Option('o', "output", Required = false, HelpText = "The output file")]
|
||||
public string? Output { get; set; }
|
||||
|
||||
[Option("changes-downloads", Required = false, Default = true, HelpText = "Include download changes")]
|
||||
public bool IncludeDownloadChanges { get; set; }
|
||||
|
||||
[Option("changes-mods", Required = false, Default = false, HelpText = "Include mods changes")]
|
||||
public bool IncludeModChanges { get; set; }
|
||||
|
||||
[Option("changes-loadorder", Required = false, Default = false, HelpText = "Include load order changes")]
|
||||
public bool IncludeLoadOrderChanges { get; set; }
|
||||
|
||||
protected override Task<int> Run()
|
||||
protected override async Task<ExitCode> Run()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (Original == null)
|
||||
return ExitCode.BadArguments;
|
||||
if (Update == null)
|
||||
return ExitCode.BadArguments;
|
||||
|
||||
ModList original, update;
|
||||
|
||||
try
|
||||
{
|
||||
original = AInstaller.LoadFromFile(Original);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return CLIUtils.Exit($"Error while loading the original Modlist from {Original}!\n{e}", ExitCode.Error);
|
||||
}
|
||||
|
||||
if(original == null)
|
||||
return CLIUtils.Exit($"The Modlist from {Original} could not be loaded!", ExitCode.Error);
|
||||
|
||||
try
|
||||
{
|
||||
update = AInstaller.LoadFromFile(Update);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return CLIUtils.Exit($"Error while loading the updated Modlist from {Update}!\n{e}", ExitCode.Error);
|
||||
}
|
||||
|
||||
if(update == null)
|
||||
return CLIUtils.Exit($"The Modlist from {Update} could not be loaded!", ExitCode.Error);
|
||||
|
||||
var downloadSizeChanges = original.DownloadSize - update.DownloadSize;
|
||||
var installSizeChanges = original.InstallSize - update.InstallSize;
|
||||
|
||||
var versionRegex = new Regex(@"\s([0-9](\.|\s)?){1,4}");
|
||||
var matchOriginal = versionRegex.Match(original.Name);
|
||||
var matchUpdated = versionRegex.Match(update.Name);
|
||||
|
||||
if (!matchOriginal.Success || !matchUpdated.Success)
|
||||
{
|
||||
return CLIUtils.Exit(
|
||||
!matchOriginal.Success
|
||||
? "The name of the original modlist did not match the version check regex!"
|
||||
: "The name of the updated modlist did not match the version check regex!", ExitCode.Error);
|
||||
}
|
||||
|
||||
var version = matchUpdated.Value.Trim();
|
||||
|
||||
var mdText =
|
||||
$"## {version}\n\n" +
|
||||
$"**Build at:** `{File.GetCreationTime(Update)}`\n\n" +
|
||||
"**Info**:\n\n" +
|
||||
$"- Download Size change: {downloadSizeChanges.ToFileSizeString()} (Total: {update.DownloadSize.ToFileSizeString()})\n" +
|
||||
$"- Install Size change: {installSizeChanges.ToFileSizeString()} (Total: {update.InstallSize.ToFileSizeString()})\n\n";
|
||||
|
||||
if (IncludeDownloadChanges)
|
||||
{
|
||||
var updatedArchives = update.Archives
|
||||
.Where(a => original.Archives.All(x => x.Name != a.Name))
|
||||
.Where(a =>
|
||||
{
|
||||
if (!(a.State is NexusDownloader.State nexusState))
|
||||
return false;
|
||||
|
||||
return original.Archives.Any(x =>
|
||||
{
|
||||
if (!(x.State is NexusDownloader.State originalState))
|
||||
return false;
|
||||
|
||||
if (nexusState.Name != originalState.Name)
|
||||
return false;
|
||||
|
||||
if (nexusState.ModID != originalState.ModID)
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(nexusState.FileID, out var currentFileID))
|
||||
return true;
|
||||
|
||||
if (int.TryParse(originalState.FileID, out var originalFileID))
|
||||
{
|
||||
return currentFileID > originalFileID;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}).ToList();
|
||||
|
||||
var newArchives = update.Archives
|
||||
.Where(a => original.Archives.All(x => x.Name != a.Name))
|
||||
.Where(a => updatedArchives.All(x => x != a))
|
||||
.ToList();
|
||||
|
||||
var removedArchives = original.Archives
|
||||
.Where(a => update.Archives.All(x => x.Name != a.Name))
|
||||
.Where(a => updatedArchives.All(x => x != a))
|
||||
.ToList();
|
||||
|
||||
if(newArchives.Any() || removedArchives.Any())
|
||||
mdText += "**Download Changes**:\n\n";
|
||||
|
||||
updatedArchives.Do(a =>
|
||||
{
|
||||
mdText += $"- Updated [{GetModName(a)}]({a.State.GetManifestURL(a)})\n";
|
||||
});
|
||||
|
||||
removedArchives.Do(a =>
|
||||
{
|
||||
mdText += $"- Removed [{GetModName(a)}]({a.State.GetManifestURL(a)})\n";
|
||||
});
|
||||
|
||||
newArchives.Do(a =>
|
||||
{
|
||||
mdText += $"- Added [{GetModName(a)}]({a.State.GetManifestURL(a)})\n";
|
||||
});
|
||||
|
||||
mdText += "\n";
|
||||
}
|
||||
|
||||
if (IncludeLoadOrderChanges)
|
||||
{
|
||||
var originalLoadOrderFile = original.Directives
|
||||
.Where(d => d is InlineFile)
|
||||
.Where(d => d.To.EndsWith("loadorder.txt"))
|
||||
.Cast<InlineFile>()
|
||||
.First();
|
||||
|
||||
var updatedLoadOrderFile = update.Directives
|
||||
.Where(d => d is InlineFile)
|
||||
.Where(d => d.To.EndsWith("loadorder.txt"))
|
||||
.Cast<InlineFile>()
|
||||
.First();
|
||||
|
||||
var originalLoadOrder = GetTextFileFromModlist(Original, original, originalLoadOrderFile.SourceDataID).Split("\n");
|
||||
var updatedLoadOrder = GetTextFileFromModlist(Update, update, updatedLoadOrderFile.SourceDataID).Split("\n");
|
||||
|
||||
var addedPlugins = updatedLoadOrder
|
||||
.Where(p => originalLoadOrder.All(x => p != x))
|
||||
.ToList();
|
||||
|
||||
var removedPlugins = originalLoadOrder
|
||||
.Where(p => updatedLoadOrder.All(x => p != x))
|
||||
.ToList();
|
||||
|
||||
if(addedPlugins.Any() || removedPlugins.Any())
|
||||
mdText += "**Load Order Changes**:\n\n";
|
||||
|
||||
addedPlugins.Do(p =>
|
||||
{
|
||||
mdText += $"- Added {p}\n";
|
||||
});
|
||||
|
||||
removedPlugins.Do(p =>
|
||||
{
|
||||
mdText += $"- Removed {p}\n";
|
||||
});
|
||||
|
||||
mdText += "\n";
|
||||
}
|
||||
|
||||
if (IncludeModChanges)
|
||||
{
|
||||
var originalModlistFile = original.Directives
|
||||
.Where(d => d is InlineFile)
|
||||
.Where(d => d.To.EndsWith("modlist.txt"))
|
||||
.Cast<InlineFile>()
|
||||
.First();
|
||||
|
||||
var updatedModlistFile = update.Directives
|
||||
.Where(d => d is InlineFile)
|
||||
.Where(d => d.To.EndsWith("modlist.txt"))
|
||||
.Cast<InlineFile>()
|
||||
.First();
|
||||
|
||||
var originalModlist = GetTextFileFromModlist(Original, original, originalModlistFile.SourceDataID).Split("\n");
|
||||
var updatedModlist = GetTextFileFromModlist(Update, update, updatedModlistFile.SourceDataID).Split("\n");
|
||||
|
||||
var removedMods = originalModlist
|
||||
.Where(m => m.StartsWith("+"))
|
||||
.Where(m => updatedModlist.All(x => m != x))
|
||||
.Select(m => m.Substring(1))
|
||||
.ToList();
|
||||
|
||||
var addedMods = updatedModlist
|
||||
.Where(m => m.StartsWith("+"))
|
||||
.Where(m => originalModlist.All(x => m != x))
|
||||
.Select(m => m.Substring(1))
|
||||
.ToList();
|
||||
|
||||
if (removedMods.Any() || addedMods.Any())
|
||||
mdText += "**Mod Changes**:\n\n";
|
||||
|
||||
addedMods.Do(m =>
|
||||
{
|
||||
mdText += $"- Added {m}\n";
|
||||
});
|
||||
|
||||
removedMods.Do(m =>
|
||||
{
|
||||
mdText += $"- Removed {m}\n";
|
||||
});
|
||||
}
|
||||
|
||||
var output = string.IsNullOrWhiteSpace(Output)
|
||||
? "changelog.md"
|
||||
: Output;
|
||||
|
||||
if (File.Exists(output) && output.EndsWith("md"))
|
||||
{
|
||||
CLIUtils.Log($"Output file {output} already exists and is a markdown file. It will be updated with the newest version");
|
||||
|
||||
var markdown = File.ReadAllLines(output).ToList();
|
||||
var lines = mdText.Split("\n");
|
||||
|
||||
if (lines.All(l => markdown.Contains(l)))
|
||||
{
|
||||
return CLIUtils.Exit("The output file is already up-to-date", ExitCode.Ok);
|
||||
}
|
||||
|
||||
var doc = Markdown.Parse(File.ReadAllText(output));
|
||||
|
||||
var hasToc = false;
|
||||
var tocLine = 0;
|
||||
|
||||
var headers = doc
|
||||
.Where(b => b is HeadingBlock)
|
||||
.Cast<HeadingBlock>()
|
||||
.ToList();
|
||||
|
||||
if (headers.Count < 2)
|
||||
{
|
||||
return CLIUtils.Exit("The provided output file has less than 2 headers!", ExitCode.Error);
|
||||
}
|
||||
|
||||
if (headers[0].Level == 1 && headers[1].Level == 2)
|
||||
{
|
||||
if (headers[1].Line - headers[0].Line > headers.Count - 1)
|
||||
{
|
||||
var listBlocks = doc
|
||||
.Where(b => b.Line > headers[0].Line && b.Line < headers[1].Line)
|
||||
.OfType<ListBlock>()
|
||||
.ToList();
|
||||
|
||||
if (listBlocks.Count == 1)
|
||||
{
|
||||
hasToc = true;
|
||||
tocLine = listBlocks[0].Line;
|
||||
|
||||
CLIUtils.Log($"Toc found at {tocLine}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var firstHeader = headers
|
||||
.First(h => h.Level >= 2);
|
||||
|
||||
var line = firstHeader.Line-1;
|
||||
|
||||
if (hasToc)
|
||||
{
|
||||
markdown.Insert(tocLine, $"- [{version}](#{ToTocLink(version)})");
|
||||
line++;
|
||||
}
|
||||
|
||||
markdown.InsertRange(line+1, lines);
|
||||
|
||||
File.WriteAllLines(output, markdown);
|
||||
CLIUtils.Log($"Wrote {markdown.Count} lines to {output}");
|
||||
|
||||
return ExitCode.Ok;
|
||||
}
|
||||
|
||||
var text = "# Changelog\n\n" +
|
||||
$"- [{version}](#{ToTocLink(version)})\n\n" +
|
||||
$"{mdText}";
|
||||
|
||||
File.WriteAllText(output, text);
|
||||
CLIUtils.Log($"Wrote changelog to {output}");
|
||||
|
||||
return ExitCode.Ok;
|
||||
}
|
||||
|
||||
private static string GetTextFileFromModlist(string archive, ModList modlist, string sourceID)
|
||||
{
|
||||
var installer = new MO2Installer(archive, modlist, "", "", null);
|
||||
byte[] bytes = installer.LoadBytesFromPath(sourceID);
|
||||
return Encoding.Default.GetString(bytes);
|
||||
}
|
||||
|
||||
private static string ToTocLink(string header)
|
||||
{
|
||||
return header.Trim().Replace(" ", "").Replace(".", "");
|
||||
}
|
||||
|
||||
private static string GetModName(Archive a)
|
||||
{
|
||||
var result = a.Name;
|
||||
|
||||
if (a.State is IMetaState metaState)
|
||||
{
|
||||
result = metaState.Name;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.7.82" />
|
||||
<PackageReference Include="Markdig" Version="0.18.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
Loading…
Reference in New Issue
Block a user