Cleaned up scripts project
This commit is contained in:
@@ -1,31 +1,41 @@
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Cocona;
|
using Cocona;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Scripts.Helpers;
|
using Scripts.Helpers;
|
||||||
|
|
||||||
namespace Scripts.Commands;
|
namespace Scripts.Commands;
|
||||||
|
|
||||||
public class PackCommand
|
public class PackCommand
|
||||||
{
|
{
|
||||||
private readonly CommandHelper CommandHelper;
|
|
||||||
private readonly string TmpDir = "/tmp/mlbuild";
|
private readonly string TmpDir = "/tmp/mlbuild";
|
||||||
|
private readonly ILogger<PackCommand> Logger;
|
||||||
|
private readonly CsprojHelper CsprojHelper;
|
||||||
|
private readonly NupkgHelper NupkgHelper;
|
||||||
|
|
||||||
public PackCommand(CommandHelper commandHelper)
|
private readonly string[] ValidTags = ["apiserver", "frontend", "shared"];
|
||||||
|
|
||||||
|
public PackCommand(
|
||||||
|
ILogger<PackCommand> logger,
|
||||||
|
CsprojHelper csprojHelper,
|
||||||
|
NupkgHelper nupkgHelper
|
||||||
|
)
|
||||||
{
|
{
|
||||||
CommandHelper = commandHelper;
|
CsprojHelper = csprojHelper;
|
||||||
|
NupkgHelper = nupkgHelper;
|
||||||
|
Logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command("pack", Description = "Packs the specified folder/solution into nuget packages")]
|
[Command("pack", Description = "Packs the specified folder/solution into nuget packages")]
|
||||||
public async Task Pack(
|
public async Task Pack(
|
||||||
[Argument] string solutionDirectory,
|
[Argument] string solutionDirectory,
|
||||||
[Argument] string outputLocation,
|
[Argument] string outputLocation,
|
||||||
[Option] string buildConfiguration = "Debug",
|
[Option] string buildConfiguration = "Debug"
|
||||||
[Option] string? nugetDir = null
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(solutionDirectory))
|
if (!Directory.Exists(solutionDirectory))
|
||||||
{
|
{
|
||||||
Console.WriteLine("The specified solution directory does not exist");
|
Logger.LogError("The specified solution directory does not exist");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,74 +48,55 @@ public class PackCommand
|
|||||||
Directory.CreateDirectory(TmpDir);
|
Directory.CreateDirectory(TmpDir);
|
||||||
|
|
||||||
// Find the project files
|
// Find the project files
|
||||||
Console.WriteLine("Searching for projects inside the specified folder");
|
Logger.LogInformation("Searching for projects inside the specified folder");
|
||||||
var csProjFiles = Directory.GetFiles(solutionDirectory, "*csproj", SearchOption.AllDirectories);
|
|
||||||
|
var projects = await CsprojHelper.FindProjectsInPath(solutionDirectory, ValidTags);
|
||||||
|
|
||||||
// Show the user
|
// Show the user
|
||||||
Console.WriteLine($"Found {csProjFiles.Length} project(s) to check:");
|
Logger.LogInformation("Found {count} project(s) to check:", projects.Count);
|
||||||
|
|
||||||
foreach (var csProjFile in csProjFiles)
|
foreach (var path in projects.Keys)
|
||||||
Console.WriteLine($"- {Path.GetFullPath(csProjFile)}");
|
Logger.LogInformation("- {path}", Path.GetFullPath(path));
|
||||||
|
|
||||||
// Filter out project files which have specific tags specified
|
// Filter out project files which have specific tags specified
|
||||||
Console.WriteLine("Filtering projects by tags");
|
Logger.LogInformation("Filtering projects by tags");
|
||||||
|
|
||||||
List<string> apiServerProjects = [];
|
var apiServerProjects = projects
|
||||||
List<string> frontendProjects = [];
|
.Where(x => x.Value.PackageTags.Contains("apiserver", StringComparer.InvariantCultureIgnoreCase))
|
||||||
List<string> sharedProjects = [];
|
.ToArray();
|
||||||
|
|
||||||
foreach (var csProjFile in csProjFiles)
|
var frontendProjects = projects
|
||||||
{
|
.Where(x => x.Value.PackageTags.Contains("frontend", StringComparer.InvariantCultureIgnoreCase))
|
||||||
await using var fs = File.Open(
|
.ToArray();
|
||||||
csProjFile,
|
|
||||||
FileMode.Open,
|
|
||||||
FileAccess.ReadWrite,
|
|
||||||
FileShare.ReadWrite
|
|
||||||
);
|
|
||||||
|
|
||||||
var document = await XDocument.LoadAsync(fs, LoadOptions.None, CancellationToken.None);
|
var sharedProjects = projects
|
||||||
fs.Close();
|
.Where(x => x.Value.PackageTags.Contains("shared", StringComparer.InvariantCultureIgnoreCase))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
// Search for tag definitions
|
Logger.LogInformation(
|
||||||
var packageTagsElements = document.Descendants("PackageTags").ToArray();
|
"Found {apiServerCount} api server project(s), {frontendCount} frontend project(s) and {sharedCount} shared project(s)",
|
||||||
|
apiServerProjects.Length,
|
||||||
if (packageTagsElements.Length == 0)
|
frontendProjects.Length,
|
||||||
{
|
sharedProjects.Length
|
||||||
Console.WriteLine($"No package tags found in {Path.GetFullPath(csProjFile)}. Skipping it when packing");
|
);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var packageTags = packageTagsElements.First().Value;
|
|
||||||
|
|
||||||
if (packageTags.Contains("apiserver", StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
apiServerProjects.Add(csProjFile);
|
|
||||||
|
|
||||||
if (packageTags.Contains("frontend", StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
frontendProjects.Add(csProjFile);
|
|
||||||
|
|
||||||
if (packageTags.Contains("shared", StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
sharedProjects.Add(csProjFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine(
|
|
||||||
$"Found {apiServerProjects.Count} api server project(s), {frontendProjects.Count} frontend project(s) and {sharedProjects.Count} shared project(s)");
|
|
||||||
|
|
||||||
// Now build all these projects so we can pack them
|
// Now build all these projects so we can pack them
|
||||||
Console.WriteLine("Building and packing api server project(s)");
|
Logger.LogInformation("Building and packing api server project(s)");
|
||||||
|
|
||||||
foreach (var apiServerProject in apiServerProjects)
|
foreach (var apiServerProject in apiServerProjects)
|
||||||
{
|
{
|
||||||
await BuildProject(
|
var csProjectFile = apiServerProject.Key;
|
||||||
apiServerProject,
|
var manifest = apiServerProject.Value;
|
||||||
buildConfiguration,
|
|
||||||
nugetDir
|
await CsprojHelper.Build(
|
||||||
|
csProjectFile,
|
||||||
|
buildConfiguration
|
||||||
);
|
);
|
||||||
|
|
||||||
var nugetFilePath = await PackProject(
|
var nugetFilePath = await CsprojHelper.Pack(
|
||||||
apiServerProject,
|
csProjectFile,
|
||||||
TmpDir,
|
TmpDir,
|
||||||
buildConfiguration,
|
buildConfiguration
|
||||||
nugetDir
|
|
||||||
);
|
);
|
||||||
|
|
||||||
var nugetPackage = ZipFile.Open(
|
var nugetPackage = ZipFile.Open(
|
||||||
@@ -113,31 +104,41 @@ public class PackCommand
|
|||||||
ZipArchiveMode.Update
|
ZipArchiveMode.Update
|
||||||
);
|
);
|
||||||
|
|
||||||
await RemoveContentFiles(nugetPackage);
|
await NupkgHelper.RemoveContentFiles(nugetPackage);
|
||||||
|
|
||||||
await CleanDependencies(nugetPackage);
|
// We don't want to clean moonlight references when we are packing moonlight,
|
||||||
|
// as it would remove references to its own shared project
|
||||||
|
|
||||||
Console.WriteLine("Finishing package and copying to output directory");
|
if (!manifest.PackageId.StartsWith("Moonlight."))
|
||||||
|
await NupkgHelper.CleanDependencies(nugetPackage, "Moonlight.");
|
||||||
|
|
||||||
|
Logger.LogInformation("Finishing package and copying to output directory");
|
||||||
|
|
||||||
nugetPackage.Dispose();
|
nugetPackage.Dispose();
|
||||||
File.Move(nugetFilePath, Path.Combine(outputLocation, Path.GetFileName(nugetFilePath)), true);
|
|
||||||
|
File.Move(
|
||||||
|
nugetFilePath,
|
||||||
|
Path.Combine(outputLocation, Path.GetFileName(nugetFilePath)),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Building and packing frontend projects");
|
Logger.LogInformation("Building and packing frontend projects");
|
||||||
|
|
||||||
foreach (var frontendProject in frontendProjects)
|
foreach (var frontendProject in frontendProjects)
|
||||||
{
|
{
|
||||||
await BuildProject(
|
var csProjectFile = frontendProject.Key;
|
||||||
frontendProject,
|
var manifest = frontendProject.Value;
|
||||||
buildConfiguration,
|
|
||||||
nugetDir
|
await CsprojHelper.Build(
|
||||||
|
csProjectFile,
|
||||||
|
buildConfiguration
|
||||||
);
|
);
|
||||||
|
|
||||||
var nugetFilePath = await PackProject(
|
var nugetFilePath = await CsprojHelper.Pack(
|
||||||
frontendProject,
|
csProjectFile,
|
||||||
TmpDir,
|
TmpDir,
|
||||||
buildConfiguration,
|
buildConfiguration
|
||||||
nugetDir
|
|
||||||
);
|
);
|
||||||
|
|
||||||
var nugetPackage = ZipFile.Open(
|
var nugetPackage = ZipFile.Open(
|
||||||
@@ -145,48 +146,20 @@ public class PackCommand
|
|||||||
ZipArchiveMode.Update
|
ZipArchiveMode.Update
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach (var entry in nugetPackage.Entries.ToArray())
|
await NupkgHelper.RemoveStaticWebAssets(nugetPackage, "_framework");
|
||||||
{
|
await NupkgHelper.RemoveStaticWebAssets(nugetPackage, "css/style.min.css");
|
||||||
if (!entry.FullName.StartsWith("staticwebassets/_framework"))
|
await NupkgHelper.RemoveContentFiles(nugetPackage);
|
||||||
continue;
|
|
||||||
|
|
||||||
Console.WriteLine($"Removing framework file: {entry.FullName}");
|
// We don't want to clean moonlight references when we are packing moonlight,
|
||||||
entry.Delete();
|
// as it would remove references to its own shared project
|
||||||
}
|
|
||||||
|
|
||||||
var buildTargetEntry = nugetPackage.Entries.FirstOrDefault(x =>
|
if (!manifest.PackageId.StartsWith("Moonlight."))
|
||||||
x.FullName == "build/Microsoft.AspNetCore.StaticWebAssets.props"
|
await NupkgHelper.CleanDependencies(nugetPackage, "Moonlight.");
|
||||||
);
|
|
||||||
|
|
||||||
if (buildTargetEntry != null)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Removing framework file references");
|
|
||||||
|
|
||||||
await ModifyXmlInPackage(nugetPackage, buildTargetEntry,
|
|
||||||
document => document
|
|
||||||
.Descendants("StaticWebAsset")
|
|
||||||
.Where(x =>
|
|
||||||
{
|
|
||||||
var relativePath = x.Element("RelativePath")!.Value;
|
|
||||||
|
|
||||||
if (relativePath.StartsWith("_framework"))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (relativePath.StartsWith("css/style.min.css"))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await CleanDependencies(nugetPackage);
|
|
||||||
|
|
||||||
await RemoveContentFiles(nugetPackage);
|
|
||||||
|
|
||||||
// Pack razor and html files into src folder
|
// Pack razor and html files into src folder
|
||||||
var additionalSrcFiles = new List<string>();
|
var additionalSrcFiles = new List<string>();
|
||||||
var basePath = Path.GetDirectoryName(frontendProject)!;
|
var basePath = Path.GetDirectoryName(csProjectFile)!;
|
||||||
|
|
||||||
additionalSrcFiles.AddRange(
|
additionalSrcFiles.AddRange(
|
||||||
Directory.GetFiles(basePath, "*.razor", SearchOption.AllDirectories)
|
Directory.GetFiles(basePath, "*.razor", SearchOption.AllDirectories)
|
||||||
@@ -196,181 +169,58 @@ public class PackCommand
|
|||||||
Directory.GetFiles(basePath, "index.html", SearchOption.AllDirectories)
|
Directory.GetFiles(basePath, "index.html", SearchOption.AllDirectories)
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach (var additionalSrcFile in additionalSrcFiles)
|
await NupkgHelper.AddSourceFiles(
|
||||||
{
|
nugetPackage,
|
||||||
var relativePath = "src/" + additionalSrcFile.Replace(basePath, "").Trim('/');
|
additionalSrcFiles.ToArray(),
|
||||||
|
file => "src/" + file.Replace(basePath, "").Trim('/')
|
||||||
|
);
|
||||||
|
|
||||||
Console.WriteLine($"Adding additional files as src: {relativePath}");
|
Logger.LogInformation("Finishing package and copying to output directory");
|
||||||
|
|
||||||
await using var fs = File.Open(
|
|
||||||
additionalSrcFile,
|
|
||||||
FileMode.Open,
|
|
||||||
FileAccess.ReadWrite,
|
|
||||||
FileShare.ReadWrite
|
|
||||||
);
|
|
||||||
|
|
||||||
var entry = nugetPackage.CreateEntry(relativePath);
|
|
||||||
await using var entryFs = entry.Open();
|
|
||||||
|
|
||||||
await fs.CopyToAsync(entryFs);
|
|
||||||
await entryFs.FlushAsync();
|
|
||||||
|
|
||||||
fs.Close();
|
|
||||||
entryFs.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("Finishing package and copying to output directory");
|
|
||||||
|
|
||||||
nugetPackage.Dispose();
|
nugetPackage.Dispose();
|
||||||
File.Move(nugetFilePath, Path.Combine(outputLocation, Path.GetFileName(nugetFilePath)), true);
|
|
||||||
|
File.Move(
|
||||||
|
nugetFilePath,
|
||||||
|
Path.Combine(outputLocation, Path.GetFileName(nugetFilePath)),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Building and packing shared projects");
|
Logger.LogInformation("Building and packing shared projects");
|
||||||
|
|
||||||
foreach (var sharedProject in sharedProjects)
|
foreach (var sharedProject in sharedProjects)
|
||||||
{
|
{
|
||||||
await BuildProject(
|
var csProjectFile = sharedProject.Key;
|
||||||
sharedProject,
|
var manifest = sharedProject.Value;
|
||||||
buildConfiguration,
|
|
||||||
nugetDir
|
await CsprojHelper.Build(
|
||||||
|
csProjectFile,
|
||||||
|
buildConfiguration
|
||||||
);
|
);
|
||||||
|
|
||||||
var nugetFilePath = await PackProject(
|
var nugetFilePath = await CsprojHelper.Pack(
|
||||||
sharedProject,
|
csProjectFile,
|
||||||
TmpDir,
|
TmpDir,
|
||||||
buildConfiguration,
|
buildConfiguration
|
||||||
nugetDir
|
|
||||||
);
|
);
|
||||||
|
|
||||||
File.Move(nugetFilePath, Path.Combine(outputLocation, Path.GetFileName(nugetFilePath)), true);
|
var nugetPackage = ZipFile.Open(
|
||||||
}
|
nugetFilePath,
|
||||||
}
|
ZipArchiveMode.Update
|
||||||
|
);
|
||||||
|
|
||||||
private async Task BuildProject(string file, string configuration, string? nugetDir)
|
// We don't want to clean moonlight references when we are packing moonlight,
|
||||||
{
|
// as it would remove references to its own shared project
|
||||||
var basePath = Path.GetFullPath(Path.GetDirectoryName(file)!);
|
|
||||||
var fileName = Path.GetFileName(file);
|
|
||||||
|
|
||||||
await CommandHelper.Run(
|
if (!manifest.PackageId.StartsWith("Moonlight."))
|
||||||
"/usr/bin/dotnet",
|
await NupkgHelper.CleanDependencies(nugetPackage, "Moonlight.");
|
||||||
$"build {fileName} --configuration {configuration}" + (string.IsNullOrEmpty(nugetDir) ? "" : $" --source {nugetDir}"),
|
|
||||||
basePath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> PackProject(string file, string output, string configuration, string? nugetDir)
|
nugetPackage.Dispose();
|
||||||
{
|
|
||||||
var basePath = Path.GetFullPath(Path.GetDirectoryName(file)!);
|
|
||||||
var fileName = Path.GetFileName(file);
|
|
||||||
|
|
||||||
await CommandHelper.Run(
|
File.Move(
|
||||||
"/usr/bin/dotnet",
|
nugetFilePath,
|
||||||
$"pack {fileName} --output {output} --configuration {configuration}" + (string.IsNullOrEmpty(nugetDir) ? "" : $" --source {nugetDir}"),
|
Path.Combine(outputLocation, Path.GetFileName(nugetFilePath)),
|
||||||
basePath
|
true
|
||||||
);
|
|
||||||
|
|
||||||
var nugetFilesPaths = Directory.GetFiles(TmpDir, "*.nupkg", SearchOption.TopDirectoryOnly);
|
|
||||||
|
|
||||||
if (nugetFilesPaths.Length == 0)
|
|
||||||
throw new Exception("No nuget packages were built");
|
|
||||||
|
|
||||||
if (nugetFilesPaths.Length > 1)
|
|
||||||
throw new Exception("More than one nuget package has been built");
|
|
||||||
|
|
||||||
return nugetFilesPaths.First();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task CleanDependencies(ZipArchive nugetPackage)
|
|
||||||
{
|
|
||||||
var nuspecEntry = nugetPackage.Entries.FirstOrDefault(x => x.Name.EndsWith(".nuspec"));
|
|
||||||
|
|
||||||
if (nuspecEntry == null)
|
|
||||||
{
|
|
||||||
Console.WriteLine("No nuspec file to modify found in nuget package");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await ModifyXmlInPackage(nugetPackage, nuspecEntry, document =>
|
|
||||||
{
|
|
||||||
var ns = document.Root!.GetDefaultNamespace();
|
|
||||||
|
|
||||||
var metadata = document.Root!.Element(ns + "metadata")!;
|
|
||||||
|
|
||||||
var id = metadata.Element(ns + "id")!.Value;
|
|
||||||
|
|
||||||
// Skip the removal of moonlight references when
|
|
||||||
// we are packing moonlight itself
|
|
||||||
if (id.StartsWith("Moonlight."))
|
|
||||||
return [];
|
|
||||||
|
|
||||||
return document
|
|
||||||
.Descendants(ns + "dependency")
|
|
||||||
.Where(x => x.Attribute("id")?.Value.StartsWith("Moonlight.") ?? false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<ZipArchiveEntry> ModifyXmlInPackage(
|
|
||||||
ZipArchive archive,
|
|
||||||
ZipArchiveEntry entry,
|
|
||||||
Func<XDocument, IEnumerable<XElement>> filter
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var oldPath = entry.FullName;
|
|
||||||
await using var oldFs = entry.Open();
|
|
||||||
|
|
||||||
var document = await XDocument.LoadAsync(
|
|
||||||
oldFs,
|
|
||||||
LoadOptions.None,
|
|
||||||
CancellationToken.None
|
|
||||||
);
|
|
||||||
|
|
||||||
var itemsToRemove = filter.Invoke(document);
|
|
||||||
var items = itemsToRemove.ToArray();
|
|
||||||
|
|
||||||
foreach (var item in items)
|
|
||||||
item.Remove();
|
|
||||||
|
|
||||||
oldFs.Close();
|
|
||||||
entry.Delete();
|
|
||||||
|
|
||||||
var newEntry = archive.CreateEntry(oldPath);
|
|
||||||
var newFs = newEntry.Open();
|
|
||||||
|
|
||||||
await document.SaveAsync(newFs, SaveOptions.None, CancellationToken.None);
|
|
||||||
|
|
||||||
await newFs.FlushAsync();
|
|
||||||
newFs.Close();
|
|
||||||
|
|
||||||
return newEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RemoveContentFiles(ZipArchive nugetPackage)
|
|
||||||
{
|
|
||||||
// Remove all content files
|
|
||||||
foreach (var entry in nugetPackage.Entries.ToArray())
|
|
||||||
{
|
|
||||||
if (!entry.FullName.StartsWith("contentFiles") && !entry.FullName.StartsWith("content"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Console.WriteLine($"Removing content file: {entry.FullName}");
|
|
||||||
entry.Delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove references to those files in the nuspec file
|
|
||||||
var nuspecFile = nugetPackage.Entries.FirstOrDefault(x => x.Name.EndsWith(".nuspec"));
|
|
||||||
|
|
||||||
if (nuspecFile != null)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Removing references to content files in the nuspec files");
|
|
||||||
|
|
||||||
await ModifyXmlInPackage(
|
|
||||||
nugetPackage,
|
|
||||||
nuspecFile,
|
|
||||||
document =>
|
|
||||||
{
|
|
||||||
var ns = document.Root!.GetDefaultNamespace();
|
|
||||||
return document.Descendants(ns + "contentFiles");
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,108 +2,119 @@ using System.IO.Compression;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Cocona;
|
using Cocona;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
using Scripts.Helpers;
|
using Scripts.Helpers;
|
||||||
|
using Scripts.Models;
|
||||||
|
|
||||||
namespace Scripts.Commands;
|
namespace Scripts.Commands;
|
||||||
|
|
||||||
public class PreBuildCommand
|
public class PreBuildCommand
|
||||||
{
|
{
|
||||||
private readonly CommandHelper CommandHelper;
|
private readonly NupkgHelper NupkgHelper;
|
||||||
|
private readonly CsprojHelper CsprojHelper;
|
||||||
|
private readonly CodeHelper CodeHelper;
|
||||||
|
private readonly ILogger<PreBuildCommand> Logger;
|
||||||
|
|
||||||
private const string GeneratedStart = "// MLBUILD Generated Start";
|
private const string GeneratedStart = "// MLBUILD Generated Start";
|
||||||
private const string GeneratedEnd = "// MLBUILD Generated End";
|
private const string GeneratedEnd = "// MLBUILD Generated End";
|
||||||
private const string GeneratedHook = "// MLBUILD_PLUGIN_STARTUP_HERE";
|
private const string GeneratedHook = "// MLBUILD_PLUGIN_STARTUP_HERE";
|
||||||
|
|
||||||
public PreBuildCommand(CommandHelper commandHelper)
|
private readonly string[] ValidTags = ["frontend", "apiserver", "shared"];
|
||||||
|
|
||||||
|
public PreBuildCommand(
|
||||||
|
CsprojHelper csprojHelper,
|
||||||
|
NupkgHelper nupkgHelper,
|
||||||
|
CodeHelper codeHelper,
|
||||||
|
ILogger<PreBuildCommand> logger
|
||||||
|
)
|
||||||
{
|
{
|
||||||
CommandHelper = commandHelper;
|
CsprojHelper = csprojHelper;
|
||||||
|
NupkgHelper = nupkgHelper;
|
||||||
|
CodeHelper = codeHelper;
|
||||||
|
Logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command("prebuild")]
|
[Command("prebuild")]
|
||||||
public async Task Prebuild(
|
public async Task Prebuild(
|
||||||
[Argument] string moonlightDir,
|
[Argument] string moonlightDirectory,
|
||||||
[Argument] string nugetDir
|
[Argument] string pluginsDirectory
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var dependencies = await GetDependenciesFromNuget(nugetDir);
|
var projects = await CsprojHelper.FindProjectsInPath(moonlightDirectory, ValidTags);
|
||||||
|
|
||||||
Console.WriteLine("Following plugins found:");
|
var nugetManifests = await GetNugetManifests(pluginsDirectory);
|
||||||
|
|
||||||
foreach (var dependency in dependencies)
|
Logger.LogInformation("Following plugins found:");
|
||||||
|
|
||||||
|
foreach (var manifest in nugetManifests)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{dependency.Id} ({dependency.Version}) [{dependency.Tags}]");
|
Logger.LogInformation(
|
||||||
|
"- {id} ({version}) [{tags}]",
|
||||||
|
manifest.Id,
|
||||||
|
manifest.Version,
|
||||||
|
string.Join(", ", manifest.Tags)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var csProjFiles = Directory.GetFiles(moonlightDir, "*.csproj", SearchOption.AllDirectories);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Console.WriteLine("Adjusting csproj files");
|
Logger.LogInformation("Adjusting csproj files");
|
||||||
foreach (var csProjFile in csProjFiles)
|
|
||||||
|
foreach (var project in projects)
|
||||||
{
|
{
|
||||||
|
var csProjectPath = project.Key;
|
||||||
|
|
||||||
await using var fs = File.Open(
|
await using var fs = File.Open(
|
||||||
csProjFile,
|
csProjectPath,
|
||||||
FileMode.Open,
|
FileMode.Open,
|
||||||
FileAccess.ReadWrite,
|
FileAccess.ReadWrite,
|
||||||
FileShare.ReadWrite
|
FileShare.ReadWrite
|
||||||
);
|
);
|
||||||
|
|
||||||
var document = await XDocument.LoadAsync(fs, LoadOptions.None, CancellationToken.None);
|
var document = await XDocument.LoadAsync(fs, LoadOptions.None, CancellationToken.None);
|
||||||
fs.Close();
|
fs.Position = 0;
|
||||||
|
|
||||||
// Search for tag definitions
|
var dependenciesToAdd = nugetManifests
|
||||||
var packageTagsElements = document.Descendants("PackageTags").ToArray();
|
.Where(x => x.Tags.Any(tag =>
|
||||||
|
project.Value.PackageTags.Contains(tag, StringComparer.InvariantCultureIgnoreCase)))
|
||||||
if (packageTagsElements.Length == 0)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"No package tags found in {Path.GetFullPath(csProjFile)}. Skipping it");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var packageTags = packageTagsElements.First().Value;
|
|
||||||
|
|
||||||
var dependenciesToAdd = dependencies
|
|
||||||
.Where(x => x.Tags.Contains(packageTags, StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
await RemoveDependencies(csProjFile);
|
await CsprojHelper.CleanDependencies(document, "MoonlightBuildDeps");
|
||||||
await AddDependencies(csProjFile, dependenciesToAdd);
|
await CsprojHelper.AddDependencies(document, dependenciesToAdd, "MoonlightBuildDeps");
|
||||||
|
|
||||||
|
fs.Position = 0;
|
||||||
|
await document.SaveAsync(fs, SaveOptions.None, CancellationToken.None);
|
||||||
|
|
||||||
|
await fs.FlushAsync();
|
||||||
|
fs.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Restoring projects");
|
Logger.LogInformation("Restoring projects");
|
||||||
foreach (var csProjFile in csProjFiles)
|
|
||||||
|
foreach (var csProjectPath in projects.Keys)
|
||||||
|
await CsprojHelper.Restore(csProjectPath);
|
||||||
|
|
||||||
|
Logger.LogInformation("Generating plugin startup");
|
||||||
|
|
||||||
|
foreach (var currentTag in ValidTags)
|
||||||
{
|
{
|
||||||
await RestoreProject(csProjFile, nugetDir);
|
Logger.LogInformation("Checking for '{currentTag}' projects", currentTag);
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("Generating plugin startup");
|
var projectsWithTag = projects
|
||||||
|
.Where(x =>
|
||||||
|
x.Value.PackageTags.Contains(currentTag, StringComparer.InvariantCultureIgnoreCase)
|
||||||
|
)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
string[] validTags = ["apiserver", "frontend"];
|
foreach (var project in projectsWithTag)
|
||||||
|
|
||||||
foreach (var currentTag in validTags)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Checking for '{currentTag}' projects");
|
|
||||||
|
|
||||||
foreach (var csProjFile in csProjFiles)
|
|
||||||
{
|
{
|
||||||
var tags = await GetTagsFromCsproj(csProjFile);
|
var csProjectPath = project.Key;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(tags))
|
var currentDependencies = nugetManifests
|
||||||
{
|
|
||||||
Console.WriteLine($"No package tags found in {Path.GetFullPath(csProjFile)}. Skipping it");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tags.Contains(currentTag))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var currentDeps = dependencies
|
|
||||||
.Where(x => x.Tags.Contains(currentTag))
|
.Where(x => x.Tags.Contains(currentTag))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var classPaths = await FindStartupClasses(currentDeps);
|
var classPaths = await FindStartupClasses(currentDependencies);
|
||||||
|
|
||||||
var code = new StringBuilder();
|
var code = new StringBuilder();
|
||||||
|
|
||||||
@@ -112,10 +123,10 @@ public class PreBuildCommand
|
|||||||
foreach (var path in classPaths)
|
foreach (var path in classPaths)
|
||||||
code.AppendLine($"pluginStartups.Add(new global::{path}());");
|
code.AppendLine($"pluginStartups.Add(new global::{path}());");
|
||||||
|
|
||||||
code.AppendLine(GeneratedEnd);
|
code.Append(GeneratedEnd);
|
||||||
|
|
||||||
var filesToSearch = Directory.GetFiles(
|
var filesToSearch = Directory.GetFiles(
|
||||||
Path.GetDirectoryName(csProjFile)!,
|
Path.GetDirectoryName(csProjectPath)!,
|
||||||
"*.cs",
|
"*.cs",
|
||||||
SearchOption.AllDirectories
|
SearchOption.AllDirectories
|
||||||
);
|
);
|
||||||
@@ -127,7 +138,7 @@ public class PreBuildCommand
|
|||||||
if (!content.Contains(GeneratedHook, StringComparison.InvariantCultureIgnoreCase))
|
if (!content.Contains(GeneratedHook, StringComparison.InvariantCultureIgnoreCase))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Console.WriteLine($"Injecting generated code to: {Path.GetFullPath(file)}");
|
Logger.LogInformation("Injecting generated code to: {path}", Path.GetFullPath(file));
|
||||||
|
|
||||||
content = content.Replace(
|
content = content.Replace(
|
||||||
GeneratedHook,
|
GeneratedHook,
|
||||||
@@ -142,12 +153,15 @@ public class PreBuildCommand
|
|||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
Console.WriteLine("An error occured while prebuilding moonlight. Removing csproj modifications");
|
Logger.LogInformation("An error occured while prebuilding moonlight. Removing csproj modifications");
|
||||||
|
|
||||||
foreach (var csProjFile in csProjFiles)
|
foreach (var project in projects)
|
||||||
await RemoveDependencies(csProjFile);
|
{
|
||||||
|
await CsprojHelper.CleanDependencies(project.Key, "MoonlightBuildDeps");
|
||||||
|
|
||||||
await RemoveGeneratedCode(moonlightDir);
|
var path = Path.GetDirectoryName(project.Key)!;
|
||||||
|
await RemoveGeneratedCode(path);
|
||||||
|
}
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
@@ -158,220 +172,68 @@ public class PreBuildCommand
|
|||||||
[Argument] string moonlightDir
|
[Argument] string moonlightDir
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var csProjFiles = Directory.GetFiles(moonlightDir, "*.csproj", SearchOption.AllDirectories);
|
var projects = await CsprojHelper.FindProjectsInPath(moonlightDir, ValidTags);
|
||||||
|
|
||||||
Console.WriteLine("Reverting csproj changes");
|
Logger.LogInformation("Reverting csproj changes");
|
||||||
|
|
||||||
foreach (var csProjFile in csProjFiles)
|
foreach (var project in projects)
|
||||||
await RemoveDependencies(csProjFile);
|
|
||||||
|
|
||||||
Console.WriteLine("Removing generated code");
|
|
||||||
await RemoveGeneratedCode(moonlightDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("test")]
|
|
||||||
public async Task Test(
|
|
||||||
[Argument] string nugetDir
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var dependencies = await GetDependenciesFromNuget(nugetDir);
|
|
||||||
|
|
||||||
await FindStartupClasses(dependencies);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Dependency[]> GetDependenciesFromNuget(string nugetDir)
|
|
||||||
{
|
|
||||||
var nugetFiles = Directory.GetFiles(nugetDir, "*.nupkg", SearchOption.AllDirectories);
|
|
||||||
var dependencies = new List<Dependency>();
|
|
||||||
|
|
||||||
foreach (var nugetFile in nugetFiles)
|
|
||||||
{
|
{
|
||||||
var dependency = await GetDependencyFromPackage(nugetFile);
|
Logger.LogInformation("Removing dependencies: {project}", project.Key);
|
||||||
dependencies.Add(dependency);
|
await CsprojHelper.CleanDependencies(project.Key, "MoonlightBuildDeps");
|
||||||
|
|
||||||
|
Logger.LogInformation("Removing generated code: {project}", project.Key);
|
||||||
|
var path = Path.GetDirectoryName(project.Key)!;
|
||||||
|
await RemoveGeneratedCode(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dependencies.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dependency> GetDependencyFromPackage(string path)
|
private async Task<NupkgManifest[]> GetNugetManifests(string nugetDir)
|
||||||
{
|
{
|
||||||
using var nugetPackage = ZipFile.Open(path, ZipArchiveMode.Read);
|
var nugetFiles = Directory.GetFiles(
|
||||||
|
nugetDir,
|
||||||
var nuspecEntry = nugetPackage.Entries.First(x => x.Name.EndsWith(".nuspec"));
|
"*.nupkg",
|
||||||
await using var nuspecFs = nuspecEntry.Open();
|
SearchOption.AllDirectories
|
||||||
|
|
||||||
var nuspec = await XDocument.LoadAsync(nuspecFs, LoadOptions.None, CancellationToken.None);
|
|
||||||
|
|
||||||
var ns = nuspec.Root!.GetDefaultNamespace();
|
|
||||||
var metadata = nuspec.Root!.Element(ns + "metadata")!;
|
|
||||||
|
|
||||||
var id = metadata.Element(ns + "id")!.Value;
|
|
||||||
var version = metadata.Element(ns + "version")!.Value;
|
|
||||||
var tags = metadata.Element(ns + "tags")!.Value;
|
|
||||||
|
|
||||||
nuspecFs.Close();
|
|
||||||
|
|
||||||
return new Dependency()
|
|
||||||
{
|
|
||||||
Id = id,
|
|
||||||
Version = version,
|
|
||||||
Tags = tags
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AddDependencies(string path, Dependency[] dependencies)
|
|
||||||
{
|
|
||||||
await using var fs = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
|
|
||||||
fs.Position = 0;
|
|
||||||
|
|
||||||
var csProj = await XDocument.LoadAsync(fs, LoadOptions.None, CancellationToken.None);
|
|
||||||
|
|
||||||
var project = csProj.Element("Project")!;
|
|
||||||
|
|
||||||
var itemGroup = new XElement("ItemGroup");
|
|
||||||
itemGroup.SetAttributeValue("Label", "MoonlightBuildDeps");
|
|
||||||
|
|
||||||
foreach (var dependency in dependencies)
|
|
||||||
{
|
|
||||||
var depElement = new XElement("PackageReference");
|
|
||||||
depElement.SetAttributeValue("Include", dependency.Id);
|
|
||||||
depElement.SetAttributeValue("Version", dependency.Version);
|
|
||||||
|
|
||||||
itemGroup.Add(depElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
project.Add(itemGroup);
|
|
||||||
|
|
||||||
fs.Position = 0;
|
|
||||||
await csProj.SaveAsync(fs, SaveOptions.None, CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task RemoveDependencies(string path)
|
|
||||||
{
|
|
||||||
var csProj = XDocument.Load(path, LoadOptions.None);
|
|
||||||
|
|
||||||
var itemGroupsToRemove = csProj
|
|
||||||
.Descendants("ItemGroup")
|
|
||||||
.Where(x => x.Attribute("Label")?.Value.Contains("MoonlightBuildDeps") ?? false)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
itemGroupsToRemove.Remove();
|
|
||||||
|
|
||||||
csProj.Save(path, SaveOptions.None);
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RestoreProject(string file, string nugetPath)
|
|
||||||
{
|
|
||||||
var basePath = Path.GetFullPath(Path.GetDirectoryName(file)!);
|
|
||||||
var fileName = Path.GetFileName(file);
|
|
||||||
var nugetPathFull = Path.GetFullPath(nugetPath);
|
|
||||||
|
|
||||||
Console.WriteLine($"Restore: {basePath} - {fileName}");
|
|
||||||
|
|
||||||
await CommandHelper.Run(
|
|
||||||
"/usr/bin/dotnet",
|
|
||||||
$"restore {fileName} --source {nugetPathFull}",
|
|
||||||
basePath
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var manifests = new List<NupkgManifest>();
|
||||||
|
|
||||||
|
foreach (var nugetFilePath in nugetFiles)
|
||||||
|
{
|
||||||
|
using var nugetPackage = ZipFile.Open(nugetFilePath, ZipArchiveMode.Read);
|
||||||
|
var manifest = await NupkgHelper.GetManifest(nugetPackage);
|
||||||
|
|
||||||
|
if (manifest == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
manifests.Add(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return manifests.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string[]> FindStartupClasses(Dependency[] dependencies)
|
private async Task<string[]> FindStartupClasses(NupkgManifest[] dependencies)
|
||||||
{
|
{
|
||||||
var result = new List<string>();
|
|
||||||
|
|
||||||
var nugetPath = Path.Combine(
|
var nugetPath = Path.Combine(
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||||
".nuget",
|
".nuget",
|
||||||
"packages"
|
"packages"
|
||||||
);
|
);
|
||||||
|
|
||||||
var filesToScan = dependencies.SelectMany(dependency =>
|
var filesToScan = dependencies
|
||||||
|
.SelectMany(dependency =>
|
||||||
{
|
{
|
||||||
var dependencySrcPath = Path.Combine(nugetPath, dependency.Id.ToLower(), dependency.Version, "src");
|
var dependencySrcPath = Path.Combine(nugetPath, dependency.Id.ToLower(), dependency.Version, "src");
|
||||||
|
|
||||||
Console.WriteLine($"Checking {dependencySrcPath}");
|
Logger.LogDebug("Checking {dependencySrcPath}", dependencySrcPath);
|
||||||
|
|
||||||
if (!Directory.Exists(dependencySrcPath))
|
if (!Directory.Exists(dependencySrcPath))
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
return Directory.GetFiles(dependencySrcPath, "*.cs", SearchOption.AllDirectories);
|
return Directory.GetFiles(dependencySrcPath, "*.cs", SearchOption.AllDirectories);
|
||||||
}
|
})
|
||||||
).ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
|
return await CodeHelper.FindPluginStartups(filesToScan);
|
||||||
|
|
||||||
var trees = new List<SyntaxTree>();
|
|
||||||
|
|
||||||
foreach (var file in filesToScan)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Reading {file}");
|
|
||||||
|
|
||||||
var content = await File.ReadAllTextAsync(file);
|
|
||||||
var tree = CSharpSyntaxTree.ParseText(content);
|
|
||||||
trees.Add(tree);
|
|
||||||
}
|
|
||||||
|
|
||||||
var compilation = CSharpCompilation.Create("Analysis", trees, [mscorlib]);
|
|
||||||
|
|
||||||
foreach (var tree in trees)
|
|
||||||
{
|
|
||||||
var model = compilation.GetSemanticModel(tree);
|
|
||||||
var root = await tree.GetRootAsync();
|
|
||||||
|
|
||||||
var classDeclarations = root
|
|
||||||
.DescendantNodes()
|
|
||||||
.OfType<ClassDeclarationSyntax>();
|
|
||||||
|
|
||||||
foreach (var classDeclaration in classDeclarations)
|
|
||||||
{
|
|
||||||
var symbol = model.GetDeclaredSymbol(classDeclaration);
|
|
||||||
|
|
||||||
if (symbol == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var hasAttribute = symbol.GetAttributes().Any(attr =>
|
|
||||||
{
|
|
||||||
if (attr.AttributeClass == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return attr.AttributeClass.Name == "PluginStartup";
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!hasAttribute)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var classPath =
|
|
||||||
$"{symbol.ContainingNamespace.ToDisplayString()}.{classDeclaration.Identifier.ValueText}";
|
|
||||||
|
|
||||||
Console.WriteLine($"Detected startup in class: {classPath}");
|
|
||||||
result.Add(classPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> GetTagsFromCsproj(string csProjFile)
|
|
||||||
{
|
|
||||||
await using var fs = File.Open(
|
|
||||||
csProjFile,
|
|
||||||
FileMode.Open,
|
|
||||||
FileAccess.ReadWrite,
|
|
||||||
FileShare.ReadWrite
|
|
||||||
);
|
|
||||||
|
|
||||||
var document = await XDocument.LoadAsync(fs, LoadOptions.None, CancellationToken.None);
|
|
||||||
fs.Close();
|
|
||||||
|
|
||||||
// Search for tag definitions
|
|
||||||
var packageTagsElements = document.Descendants("PackageTags").ToArray();
|
|
||||||
|
|
||||||
if (packageTagsElements.Length == 0)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
return packageTagsElements.First().Value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RemoveGeneratedCode(string dir)
|
private async Task RemoveGeneratedCode(string dir)
|
||||||
@@ -384,10 +246,6 @@ public class PreBuildCommand
|
|||||||
|
|
||||||
foreach (var file in filesToSearch)
|
foreach (var file in filesToSearch)
|
||||||
{
|
{
|
||||||
// We dont want to replace ourself
|
|
||||||
if (file.Contains("PreBuildCommand.cs"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var content = await File.ReadAllTextAsync(file);
|
var content = await File.ReadAllTextAsync(file);
|
||||||
|
|
||||||
if (!content.Contains(GeneratedStart) || !content.Contains(GeneratedEnd))
|
if (!content.Contains(GeneratedStart) || !content.Contains(GeneratedEnd))
|
||||||
@@ -404,11 +262,4 @@ public class PreBuildCommand
|
|||||||
await File.WriteAllTextAsync(file, content);
|
await File.WriteAllTextAsync(file, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record Dependency
|
|
||||||
{
|
|
||||||
public string Id { get; set; }
|
|
||||||
public string Version { get; set; }
|
|
||||||
public string Tags { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
73
Resources/Scripts/Helpers/CodeHelper.cs
Normal file
73
Resources/Scripts/Helpers/CodeHelper.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Scripts.Helpers;
|
||||||
|
|
||||||
|
public class CodeHelper
|
||||||
|
{
|
||||||
|
private readonly ILogger<CodeHelper> Logger;
|
||||||
|
|
||||||
|
public CodeHelper(ILogger<CodeHelper> logger)
|
||||||
|
{
|
||||||
|
Logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string[]> FindPluginStartups(string[] filesToSearch)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
|
||||||
|
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
|
||||||
|
|
||||||
|
var trees = new List<SyntaxTree>();
|
||||||
|
|
||||||
|
foreach (var file in filesToSearch)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("Reading {file}", file);
|
||||||
|
|
||||||
|
var content = await File.ReadAllTextAsync(file);
|
||||||
|
var tree = CSharpSyntaxTree.ParseText(content);
|
||||||
|
trees.Add(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
var compilation = CSharpCompilation.Create("Analysis", trees, [mscorlib]);
|
||||||
|
|
||||||
|
foreach (var tree in trees)
|
||||||
|
{
|
||||||
|
var model = compilation.GetSemanticModel(tree);
|
||||||
|
var root = await tree.GetRootAsync();
|
||||||
|
|
||||||
|
var classDeclarations = root
|
||||||
|
.DescendantNodes()
|
||||||
|
.OfType<ClassDeclarationSyntax>();
|
||||||
|
|
||||||
|
foreach (var classDeclaration in classDeclarations)
|
||||||
|
{
|
||||||
|
var symbol = model.GetDeclaredSymbol(classDeclaration);
|
||||||
|
|
||||||
|
if (symbol == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var hasAttribute = symbol.GetAttributes().Any(attr =>
|
||||||
|
{
|
||||||
|
if (attr.AttributeClass == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return attr.AttributeClass.Name == "PluginStartup";
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasAttribute)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var classPath = $"{symbol.ContainingNamespace.ToDisplayString()}.{classDeclaration.Identifier.ValueText}";
|
||||||
|
|
||||||
|
Logger.LogInformation("Detected startup in class: {classPath}", classPath);
|
||||||
|
|
||||||
|
result.Add(classPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
228
Resources/Scripts/Helpers/CsprojHelper.cs
Normal file
228
Resources/Scripts/Helpers/CsprojHelper.cs
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
using System.Collections.Frozen;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Scripts.Models;
|
||||||
|
|
||||||
|
namespace Scripts.Helpers;
|
||||||
|
|
||||||
|
public class CsprojHelper
|
||||||
|
{
|
||||||
|
private readonly ILogger<CsprojHelper> Logger;
|
||||||
|
private readonly CommandHelper CommandHelper;
|
||||||
|
|
||||||
|
public CsprojHelper(ILogger<CsprojHelper> logger, CommandHelper commandHelper)
|
||||||
|
{
|
||||||
|
Logger = logger;
|
||||||
|
CommandHelper = commandHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Add dependencies
|
||||||
|
|
||||||
|
public async Task AddDependencies(string path, NupkgManifest[] dependencies, string label)
|
||||||
|
{
|
||||||
|
await using var fs = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||||
|
fs.Position = 0;
|
||||||
|
|
||||||
|
await AddDependencies(fs, dependencies, label);
|
||||||
|
|
||||||
|
await fs.FlushAsync();
|
||||||
|
fs.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddDependencies(Stream stream, NupkgManifest[] dependencies, string label)
|
||||||
|
{
|
||||||
|
var xmlDocument = await XDocument.LoadAsync(stream, LoadOptions.None, CancellationToken.None);
|
||||||
|
|
||||||
|
await AddDependencies(xmlDocument, dependencies, label);
|
||||||
|
|
||||||
|
stream.Position = 0;
|
||||||
|
await xmlDocument.SaveAsync(stream, SaveOptions.DisableFormatting, CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task AddDependencies(XDocument document, NupkgManifest[] dependencies, string label)
|
||||||
|
{
|
||||||
|
var project = document.Element("Project")!;
|
||||||
|
|
||||||
|
var itemGroup = new XElement("ItemGroup");
|
||||||
|
itemGroup.SetAttributeValue("Label", label);
|
||||||
|
|
||||||
|
foreach (var dependency in dependencies)
|
||||||
|
{
|
||||||
|
var depElement = new XElement("PackageReference");
|
||||||
|
depElement.SetAttributeValue("Include", dependency.Id);
|
||||||
|
depElement.SetAttributeValue("Version", dependency.Version);
|
||||||
|
|
||||||
|
itemGroup.Add(depElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
project.Add(itemGroup);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Clean dependencies
|
||||||
|
|
||||||
|
public async Task CleanDependencies(string path, string label)
|
||||||
|
{
|
||||||
|
var document = XDocument.Load(path, LoadOptions.None);
|
||||||
|
|
||||||
|
await CleanDependencies(document, label);
|
||||||
|
|
||||||
|
document.Save(path, SaveOptions.DisableFormatting);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CleanDependencies(Stream stream, string label)
|
||||||
|
{
|
||||||
|
var xmlDocument = await XDocument.LoadAsync(stream, LoadOptions.None, CancellationToken.None);
|
||||||
|
|
||||||
|
await CleanDependencies(xmlDocument, label);
|
||||||
|
|
||||||
|
stream.Position = 0;
|
||||||
|
await xmlDocument.SaveAsync(stream, SaveOptions.DisableFormatting, CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task CleanDependencies(XDocument document, string label)
|
||||||
|
{
|
||||||
|
var itemGroupsToRemove = document
|
||||||
|
.Descendants("ItemGroup")
|
||||||
|
.Where(x => x.Attribute("Label")?.Value.Contains(label) ?? false)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
itemGroupsToRemove.Remove();
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Read
|
||||||
|
|
||||||
|
public async Task<CsprojManifest> Read(string path)
|
||||||
|
{
|
||||||
|
await using var fileStream = File.Open(
|
||||||
|
path,
|
||||||
|
FileMode.Open,
|
||||||
|
FileAccess.Read,
|
||||||
|
FileShare.ReadWrite
|
||||||
|
);
|
||||||
|
|
||||||
|
var manifest = await Read(fileStream);
|
||||||
|
|
||||||
|
fileStream.Close();
|
||||||
|
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CsprojManifest> Read(Stream stream)
|
||||||
|
{
|
||||||
|
var xmlDocument = await XDocument.LoadAsync(stream, LoadOptions.None, CancellationToken.None);
|
||||||
|
return await Read(xmlDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<CsprojManifest> Read(XDocument document)
|
||||||
|
{
|
||||||
|
var manifest = new CsprojManifest();
|
||||||
|
|
||||||
|
var ns = document.Root!.GetDefaultNamespace();
|
||||||
|
|
||||||
|
manifest.IsPackable = document
|
||||||
|
.Descendants(ns + "IsPackable")
|
||||||
|
.FirstOrDefault()?.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase) ?? false;
|
||||||
|
|
||||||
|
manifest.PackageId = document
|
||||||
|
.Descendants(ns + "PackageId")
|
||||||
|
.FirstOrDefault()?.Value ?? "N/A";
|
||||||
|
|
||||||
|
manifest.Version = document
|
||||||
|
.Descendants(ns + "Version")
|
||||||
|
.FirstOrDefault()?.Value ?? "N/A";
|
||||||
|
|
||||||
|
manifest.PackageTags = document
|
||||||
|
.Descendants(ns + "PackageTags")
|
||||||
|
.FirstOrDefault()?.Value
|
||||||
|
.Split(";", StringSplitOptions.RemoveEmptyEntries) ?? [];
|
||||||
|
|
||||||
|
return Task.FromResult(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public async Task Restore(string path)
|
||||||
|
{
|
||||||
|
var basePath = Path.GetFullPath(Path.GetDirectoryName(path)!);
|
||||||
|
var fileName = Path.GetFileName(path);
|
||||||
|
|
||||||
|
Logger.LogInformation("Restore: {basePath} - {fileName}", basePath, fileName);
|
||||||
|
|
||||||
|
await CommandHelper.Run(
|
||||||
|
"/usr/bin/dotnet",
|
||||||
|
$"restore {fileName}",
|
||||||
|
basePath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Build(string file, string configuration)
|
||||||
|
{
|
||||||
|
var basePath = Path.GetFullPath(Path.GetDirectoryName(file)!);
|
||||||
|
var fileName = Path.GetFileName(file);
|
||||||
|
|
||||||
|
await CommandHelper.Run(
|
||||||
|
"/usr/bin/dotnet",
|
||||||
|
$"build {fileName} --configuration {configuration}",
|
||||||
|
basePath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> Pack(string file, string output, string configuration)
|
||||||
|
{
|
||||||
|
var basePath = Path.GetFullPath(Path.GetDirectoryName(file)!);
|
||||||
|
var fileName = Path.GetFileName(file);
|
||||||
|
|
||||||
|
await CommandHelper.Run(
|
||||||
|
"/usr/bin/dotnet",
|
||||||
|
$"pack {fileName} --output {output} --configuration {configuration}",
|
||||||
|
basePath
|
||||||
|
);
|
||||||
|
|
||||||
|
var nugetFilesPaths = Directory.GetFiles(
|
||||||
|
output,
|
||||||
|
"*.nupkg",
|
||||||
|
SearchOption.TopDirectoryOnly
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nugetFilesPaths.Length == 0)
|
||||||
|
throw new Exception("No nuget packages were built");
|
||||||
|
|
||||||
|
if (nugetFilesPaths.Length > 1)
|
||||||
|
throw new Exception("More than one nuget package has been built");
|
||||||
|
|
||||||
|
return nugetFilesPaths.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FrozenDictionary<string, CsprojManifest>> FindProjectsInPath(string path, string[] validTags)
|
||||||
|
{
|
||||||
|
var projectFiles = Directory.GetFiles(
|
||||||
|
path,
|
||||||
|
"*.csproj",
|
||||||
|
SearchOption.AllDirectories
|
||||||
|
);
|
||||||
|
|
||||||
|
var projects = new Dictionary<string, CsprojManifest>();
|
||||||
|
|
||||||
|
foreach (var projectFile in projectFiles)
|
||||||
|
{
|
||||||
|
var manifest = await Read(projectFile);
|
||||||
|
|
||||||
|
// Ignore all projects which have no matching tags
|
||||||
|
if (!manifest.PackageTags.Any(projectTag =>
|
||||||
|
validTags.Contains(projectTag, StringComparer.InvariantCultureIgnoreCase)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
projects.Add(projectFile, manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return projects.ToFrozenDictionary();
|
||||||
|
}
|
||||||
|
}
|
||||||
195
Resources/Scripts/Helpers/NupkgHelper.cs
Normal file
195
Resources/Scripts/Helpers/NupkgHelper.cs
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
using System.IO.Compression;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Scripts.Models;
|
||||||
|
|
||||||
|
namespace Scripts.Helpers;
|
||||||
|
|
||||||
|
public class NupkgHelper
|
||||||
|
{
|
||||||
|
private readonly ILogger<NupkgHelper> Logger;
|
||||||
|
|
||||||
|
public NupkgHelper(ILogger<NupkgHelper> logger)
|
||||||
|
{
|
||||||
|
Logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<NupkgManifest?> GetManifest(ZipArchive nugetPackage)
|
||||||
|
{
|
||||||
|
var nuspecEntry = nugetPackage.Entries.FirstOrDefault(
|
||||||
|
x => x.Name.EndsWith(".nuspec")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nuspecEntry == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
await using var fs = nuspecEntry.Open();
|
||||||
|
|
||||||
|
var nuspec = await XDocument.LoadAsync(fs, LoadOptions.None, CancellationToken.None);
|
||||||
|
|
||||||
|
var ns = nuspec.Root!.GetDefaultNamespace();
|
||||||
|
var metadata = nuspec.Root!.Element(ns + "metadata")!;
|
||||||
|
|
||||||
|
var id = metadata.Element(ns + "id")!.Value;
|
||||||
|
var version = metadata.Element(ns + "version")!.Value;
|
||||||
|
var tags = metadata.Element(ns + "tags")!.Value;
|
||||||
|
|
||||||
|
return new NupkgManifest()
|
||||||
|
{
|
||||||
|
Id = id,
|
||||||
|
Version = version,
|
||||||
|
Tags = tags.Split(";", StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CleanDependencies(ZipArchive nugetPackage, string filter)
|
||||||
|
{
|
||||||
|
var nuspecEntry = nugetPackage.Entries.FirstOrDefault(
|
||||||
|
x => x.Name.EndsWith(".nuspec")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nuspecEntry == null)
|
||||||
|
{
|
||||||
|
Logger.LogWarning("No nuspec file to modify found in nuget package");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ModifyXmlInPackage(nugetPackage, nuspecEntry, document =>
|
||||||
|
{
|
||||||
|
var ns = document.Root!.GetDefaultNamespace();
|
||||||
|
|
||||||
|
return document
|
||||||
|
.Descendants(ns + "dependency")
|
||||||
|
.Where(x => x.Attribute("id")?.Value.StartsWith(filter) ?? false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveContentFiles(ZipArchive nugetPackage)
|
||||||
|
{
|
||||||
|
foreach (var entry in nugetPackage.Entries.ToArray())
|
||||||
|
{
|
||||||
|
if (!entry.FullName.StartsWith("contentFiles") && !entry.FullName.StartsWith("content"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Logger.LogDebug("Removing content file: {path}", entry.FullName);
|
||||||
|
entry.Delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
var nuspecFile = nugetPackage
|
||||||
|
.Entries
|
||||||
|
.FirstOrDefault(x => x.Name.EndsWith(".nuspec"));
|
||||||
|
|
||||||
|
if (nuspecFile == null)
|
||||||
|
{
|
||||||
|
Logger.LogWarning("Nuspec file missing. Unable to remove content files references from nuspec file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ModifyXmlInPackage(
|
||||||
|
nugetPackage,
|
||||||
|
nuspecFile,
|
||||||
|
document =>
|
||||||
|
{
|
||||||
|
var ns = document.Root!.GetDefaultNamespace();
|
||||||
|
return document.Descendants(ns + "contentFiles");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ZipArchiveEntry> ModifyXmlInPackage(
|
||||||
|
ZipArchive nugetPackage,
|
||||||
|
ZipArchiveEntry entry,
|
||||||
|
Func<XDocument, IEnumerable<XElement>> filter
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var oldPath = entry.FullName;
|
||||||
|
await using var oldFs = entry.Open();
|
||||||
|
|
||||||
|
var document = await XDocument.LoadAsync(
|
||||||
|
oldFs,
|
||||||
|
LoadOptions.None,
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
|
||||||
|
var itemsToRemove = filter.Invoke(document);
|
||||||
|
var items = itemsToRemove.ToArray();
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
item.Remove();
|
||||||
|
|
||||||
|
oldFs.Close();
|
||||||
|
entry.Delete();
|
||||||
|
|
||||||
|
var newEntry = nugetPackage.CreateEntry(oldPath);
|
||||||
|
var newFs = newEntry.Open();
|
||||||
|
|
||||||
|
await document.SaveAsync(newFs, SaveOptions.None, CancellationToken.None);
|
||||||
|
|
||||||
|
await newFs.FlushAsync();
|
||||||
|
newFs.Close();
|
||||||
|
|
||||||
|
return newEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveStaticWebAssets(ZipArchive nugetPackage, string filter)
|
||||||
|
{
|
||||||
|
var filterWithPath = $"staticwebassets/{filter}";
|
||||||
|
|
||||||
|
foreach (var entry in nugetPackage.Entries.ToArray())
|
||||||
|
{
|
||||||
|
if (!entry.FullName.StartsWith(filterWithPath))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Logger.LogDebug("Removing file: {name}", entry.FullName);
|
||||||
|
entry.Delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildTargetEntry = nugetPackage.Entries.FirstOrDefault(x =>
|
||||||
|
x.FullName == "build/Microsoft.AspNetCore.StaticWebAssets.props"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (buildTargetEntry == null)
|
||||||
|
{
|
||||||
|
Logger.LogWarning("Unable to find Microsoft.AspNetCore.StaticWebAssets.props to remove file references");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogDebug("Removing file references");
|
||||||
|
|
||||||
|
await ModifyXmlInPackage(nugetPackage, buildTargetEntry,
|
||||||
|
document => document
|
||||||
|
.Descendants("StaticWebAsset")
|
||||||
|
.Where(x =>
|
||||||
|
{
|
||||||
|
var relativePath = x.Element("RelativePath")!.Value;
|
||||||
|
return relativePath.StartsWith(filter);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddSourceFiles(ZipArchive nugetPackage, string[] files, Func<string, string> buildPath)
|
||||||
|
{
|
||||||
|
foreach (var sourceFile in files)
|
||||||
|
{
|
||||||
|
var path = buildPath.Invoke(sourceFile);
|
||||||
|
|
||||||
|
Logger.LogDebug("Adding additional files as src: {path}", path);
|
||||||
|
|
||||||
|
await using var fs = File.Open(
|
||||||
|
sourceFile,
|
||||||
|
FileMode.Open,
|
||||||
|
FileAccess.Read,
|
||||||
|
FileShare.ReadWrite
|
||||||
|
);
|
||||||
|
|
||||||
|
var entry = nugetPackage.CreateEntry(path);
|
||||||
|
await using var entryFs = entry.Open();
|
||||||
|
|
||||||
|
await fs.CopyToAsync(entryFs);
|
||||||
|
await entryFs.FlushAsync();
|
||||||
|
|
||||||
|
fs.Close();
|
||||||
|
entryFs.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
|
|
||||||
namespace Scripts.Helpers;
|
|
||||||
|
|
||||||
public class StartupClassDetector
|
|
||||||
{
|
|
||||||
public bool Check(string content, out string fullName)
|
|
||||||
{
|
|
||||||
var tree = CSharpSyntaxTree.ParseText(content);
|
|
||||||
var root = tree.GetCompilationUnitRoot();
|
|
||||||
|
|
||||||
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
|
|
||||||
var compilation = CSharpCompilation.Create("MyAnalysis", [tree], [mscorlib]);
|
|
||||||
|
|
||||||
var model = compilation.GetSemanticModel(tree);
|
|
||||||
|
|
||||||
var classDeclarations = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
|
|
||||||
|
|
||||||
foreach (var classDecl in classDeclarations)
|
|
||||||
{
|
|
||||||
var symbol = model.GetDeclaredSymbol(classDecl);
|
|
||||||
|
|
||||||
if(symbol == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var hasAttribute = symbol.GetAttributes().Any(attribute =>
|
|
||||||
{
|
|
||||||
if (attribute.AttributeClass == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return attribute.AttributeClass.Name.Contains("PluginStartup");
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hasAttribute)
|
|
||||||
{
|
|
||||||
fullName = symbol.ContainingNamespace.ToDisplayString() + "." + classDecl.Identifier.ValueText;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fullName = "";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9
Resources/Scripts/Models/CsprojManifest.cs
Normal file
9
Resources/Scripts/Models/CsprojManifest.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Scripts.Models;
|
||||||
|
|
||||||
|
public class CsprojManifest
|
||||||
|
{
|
||||||
|
public bool IsPackable { get; set; }
|
||||||
|
public string Version { get; set; }
|
||||||
|
public string PackageId { get; set; }
|
||||||
|
public string[] PackageTags { get; set; }
|
||||||
|
}
|
||||||
8
Resources/Scripts/Models/NupkgManifest.cs
Normal file
8
Resources/Scripts/Models/NupkgManifest.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Scripts.Models;
|
||||||
|
|
||||||
|
public class NupkgManifest
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Version { get; set; }
|
||||||
|
public string[] Tags { get; set; }
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using Cocona;
|
using Cocona;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MoonCore.Extensions;
|
||||||
using Scripts.Commands;
|
using Scripts.Commands;
|
||||||
using Scripts.Helpers;
|
using Scripts.Helpers;
|
||||||
|
|
||||||
@@ -8,8 +10,13 @@ Console.WriteLine();
|
|||||||
|
|
||||||
var builder = CoconaApp.CreateBuilder(args);
|
var builder = CoconaApp.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.Logging.ClearProviders();
|
||||||
|
builder.Logging.AddMoonCore();
|
||||||
|
|
||||||
builder.Services.AddSingleton<CommandHelper>();
|
builder.Services.AddSingleton<CommandHelper>();
|
||||||
builder.Services.AddSingleton<StartupClassDetector>();
|
builder.Services.AddSingleton<NupkgHelper>();
|
||||||
|
builder.Services.AddSingleton<CsprojHelper>();
|
||||||
|
builder.Services.AddSingleton<CodeHelper>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Cocona" Version="2.2.0" />
|
<PackageReference Include="Cocona" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.13.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.13.0" />
|
||||||
|
<PackageReference Include="MoonCore" Version="1.8.6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
Reference in New Issue
Block a user