Cleaned up scripts project
This commit is contained in:
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user