Files
Installer/Godot~/addons/shrinksdk/ShrinkProjectPackageEditor.cs
T

149 lines
6.8 KiB
C#

#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace ShrinkSDK.Godot.Editor;
public static class ShrinkProjectPackageEditor
{
public static readonly IReadOnlyDictionary<string, string> RecommendedPackages =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["ShrinkSDK.Godot"] = "0.1.0",
["ShrinkSDK.EventBus"] = "2.1.0",
["ShrinkSDK.Context.Core"] = "0.2.0",
["ShrinkSDK.Command"] = "0.3.0",
["ShrinkSDK.Command.Integration.EventBus"] = "0.2.0",
["ShrinkSDK.Command.Integration.Network"] = "0.2.0",
["ShrinkSDK.Command.Integration.App"] = "0.2.0",
["ShrinkSDK.Network"] = "0.3.0",
["ShrinkSDK.Network.Integration.EventBus"] = "0.2.0",
["ShrinkSDK.Network.Integration.App"] = "0.2.0",
["ShrinkSDK.DataSaver"] = "2.3.0",
["ShrinkSDK.DataSaver.Integration.EventBus"] = "2.2.0",
["ShrinkSDK.DataSaver.Integration.App"] = "0.2.0",
["ShrinkSDK.App.Core"] = "0.2.0",
["ShrinkSDK.Context.AppAdapter"] = "0.2.0",
["ShrinkSDK.App.Starter.Basic"] = "0.3.0",
["ShrinkSDK.ModFramework"] = "0.3.0",
["ShrinkSDK.ModFramework.Godot"] = "0.1.0",
["ShrinkSDK.Tutorial"] = "0.2.0",
["ShrinkSDK.Tutorial.Godot"] = "0.1.0"
};
public static IReadOnlyDictionary<string, string> ReadPackageReferences(string projectPath)
{
var document = XDocument.Load(projectPath, LoadOptions.PreserveWhitespace);
return document.Descendants("PackageReference")
.Select(element => new
{
Id = ((string?)element.Attribute("Include") ?? (string?)element.Attribute("Update"))?.Trim(),
Version = ((string?)element.Attribute("Version") ??
(string?)element.Attribute("VersionOverride") ??
element.Element("Version")?.Value)?.Trim() ?? string.Empty
})
.Where(item => !string.IsNullOrWhiteSpace(item.Id))
.GroupBy(item => item.Id!, StringComparer.OrdinalIgnoreCase)
.ToDictionary(group => group.Key, group => group.First().Version, StringComparer.OrdinalIgnoreCase);
}
public static void SetPackageReference(string projectPath, string packageId, string? version)
{
var document = XDocument.Load(projectPath, LoadOptions.PreserveWhitespace);
var root = document.Root ?? throw new InvalidDataException("The C# project has no root element.");
var references = root.Descendants("PackageReference").Where(element =>
string.Equals((string?)element.Attribute("Include"), packageId, StringComparison.OrdinalIgnoreCase)).ToArray();
foreach (var duplicate in references.Skip(1)) duplicate.Remove();
if (string.IsNullOrWhiteSpace(version))
{
foreach (var reference in references) reference.Remove();
}
else if (references.Length > 0)
{
var reference = references[0];
if (reference.Attribute("VersionOverride") != null)
reference.SetAttributeValue("VersionOverride", version);
else if (reference.Attribute("Version") != null)
reference.SetAttributeValue("Version", version);
else if (reference.Element("Version") != null)
reference.Element("Version")!.Value = version;
else
reference.SetAttributeValue("Version", version);
}
else
{
var group = root.Elements("ItemGroup").FirstOrDefault(element => element.Elements("PackageReference").Any());
if (group == null)
{
group = new XElement("ItemGroup");
root.Add(group);
}
group.Add(new XElement("PackageReference", new XAttribute("Include", packageId), new XAttribute("Version", version)));
}
WriteAtomically(projectPath, document);
}
public static void EnsureShrinkFeed(string nugetConfigPath)
{
XDocument document;
if (File.Exists(nugetConfigPath)) document = XDocument.Load(nugetConfigPath, LoadOptions.PreserveWhitespace);
else document = new XDocument(new XDeclaration("1.0", "utf-8", null), new XElement("configuration"));
var root = document.Root ?? throw new InvalidDataException("NuGet.Config has no root element.");
var sources = root.Element("packageSources");
if (sources == null)
{
sources = new XElement("packageSources");
root.Add(sources);
}
var existing = sources.Elements("add").FirstOrDefault(element =>
string.Equals((string?)element.Attribute("key"), "ShrinkSDK", StringComparison.OrdinalIgnoreCase));
if (existing == null)
sources.Add(new XElement("add", new XAttribute("key", "ShrinkSDK"),
new XAttribute("value", "https://git.crash.work/api/packages/ShrinkSDK/nuget/index.json")));
else
existing.SetAttributeValue("value", "https://git.crash.work/api/packages/ShrinkSDK/nuget/index.json");
WriteAtomically(nugetConfigPath, document);
}
public static async Task<(int ExitCode, string Output)> RunDotnetAsync(string projectDirectory,
string arguments, CancellationToken cancellationToken = default)
{
var output = new StringBuilder();
using var process = new Process
{
StartInfo = new ProcessStartInfo("dotnet", arguments)
{
WorkingDirectory = projectDirectory,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
process.OutputDataReceived += (_, args) => { if (args.Data != null) output.AppendLine(args.Data); };
process.ErrorDataReceived += (_, args) => { if (args.Data != null) output.AppendLine(args.Data); };
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
await process.WaitForExitAsync(cancellationToken);
return (process.ExitCode, output.ToString());
}
private static void WriteAtomically(string path, XDocument document)
{
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))!);
var temporaryPath = path + ".shrinksdk.tmp";
using (var writer = new StreamWriter(temporaryPath, false, new UTF8Encoding(false)))
document.Save(writer, SaveOptions.DisableFormatting);
File.Move(temporaryPath, path, true);
}
}