diff --git a/.nuke/Build.Container.cs b/.nuke/Build.Container.cs index 238b3418beda08bcc89771d7a88abfe864295707..c52ab8448aefde3238cb693c1f0b879a26630712 100644 --- a/.nuke/Build.Container.cs +++ b/.nuke/Build.Container.cs @@ -42,9 +42,13 @@ sealed partial class Build : NukeBuild .SetProcessLogger((outputType, output) => { if (outputType == OutputType.Std) + { Log.Information(output); + } else + { Log.Debug(output); + } }) ); }); diff --git a/.nuke/Build.GitLab.cs b/.nuke/Build.GitLab.cs index a2e562357b0dcd3bff7855435e4c19dedaa4a62d..75519727fe893f635bd1904008c4aba64f830a9c 100644 --- a/.nuke/Build.GitLab.cs +++ b/.nuke/Build.GitLab.cs @@ -10,6 +10,7 @@ using System.IO; using System.IO.Compression; using System.Net.Http; using System.Net.Http.Json; +using System.Threading.Tasks; namespace SuCoS.Nuke; @@ -92,7 +93,7 @@ sealed partial class Build : NukeBuild throw; } - GitLabCreateReleaseLink(package, packageLink); + await GitLabCreateReleaseLink(package, packageLink); }); /// @@ -164,7 +165,7 @@ sealed partial class Build : NukeBuild public Target GitLabPushContainer => td => td .DependsOn(CreateContainer) .OnlyWhenStatic(() => runtimeIdentifier != "win-x64") - .Executes(() => + .Executes(async () => { var tags = ContainerTags(); @@ -184,7 +185,7 @@ sealed partial class Build : NukeBuild // Create a link to the GitLab release var tagLink = GitLabAPIUrl($"?orderBy=NAME&sort=asc&search[]={tag}"); - GitLabCreateReleaseLink($"docker-{tag}", tagLink); + await GitLabCreateReleaseLink($"docker-{tag}", tagLink); } }); /// @@ -217,7 +218,7 @@ sealed partial class Build : NukeBuild return apiUrl; } - async void GitLabCreateReleaseLink(string itemName, string itemLink) + async Task GitLabCreateReleaseLink(string itemName, string itemLink) { try { diff --git a/source/Commands/NewThemeCommand.cs b/source/Commands/NewThemeCommand.cs index c138dc1de8c746e8dbfe8594b7697cb87520b3a2..d6b2bc6d828abe49bc781e97b52b7809e8bc27e6 100644 --- a/source/Commands/NewThemeCommand.cs +++ b/source/Commands/NewThemeCommand.cs @@ -34,17 +34,15 @@ public sealed partial class NewThemeCommand(NewThemeOptions options, ILogger log CreateFolders(theme.Folders); - foreach (var themeFolder in theme.Folders) - - try - { - new YAMLParser().Export(theme, themePath); - } - catch (Exception ex) - { - logger.Error("Failed to export site settings: {ex}", ex); - return 1; - } + try + { + new YAMLParser().Export(theme, themePath); + } + catch (Exception ex) + { + logger.Error("Failed to export site settings: {ex}", ex); + return 1; + } logger.Information("Done"); return 0; diff --git a/source/Commands/ServeCommand.cs b/source/Commands/ServeCommand.cs index 17fa49207e56e3b63931e7115bc75f083a3eba5b..7c9c0abce5d326ad027e11900a145a031867efe8 100644 --- a/source/Commands/ServeCommand.cs +++ b/source/Commands/ServeCommand.cs @@ -61,7 +61,7 @@ public sealed class ServeCommand : BaseGeneratorCommand, IDisposable /// The logger instance. Injectable for testing /// /// - public ServeCommand(ServeOptions options, ILogger logger, IFileWatcher fileWatcher, IFileSystem fs) + public ServeCommand(ServeOptions options, ILogger logger, IFileWatcher fileWatcher, IFileSystem fs) : base(options, logger, fs) { this.options = options ?? throw new ArgumentNullException(nameof(options)); @@ -73,13 +73,30 @@ public sealed class ServeCommand : BaseGeneratorCommand, IDisposable fileWatcher.Start(SourceAbsolutePath, OnSourceFileChanged); } + /// + /// Starts the server asynchronously. + /// + public void StartServer() + { + StartServer(baseURLDefault, portDefault); + } + + /// + /// Starts the server asynchronously. + /// + /// + public void StartServer(string baseURL) + { + StartServer(baseURL, portDefault); + } + /// /// Starts the server asynchronously. /// /// The base URL for the server. /// The port number for the server. /// A Task representing the asynchronous operation. - public void StartServer(string baseURL = baseURLDefault, int port = portDefault) + public void StartServer(string baseURL, int port) { logger.Information("Starting server..."); @@ -165,7 +182,7 @@ public sealed class ServeCommand : BaseGeneratorCommand, IDisposable // Reinitialize the site site = SiteHelper.Init(configFile, options, Parser, logger, stopwatch, fs); - StartServer(baseURLDefault, portDefault); + StartServer(); }).ConfigureAwait(false); lastRestartTask = lastRestartTask.ContinueWith(t => t.Exception != null diff --git a/source/Helpers/SiteHelper.cs b/source/Helpers/SiteHelper.cs index 39e8873450f67cf3b7d06f1c0fe1c75a5aa18ee0..6a16724faa494f5a2d0d8b9d3847da376d7ef382 100644 --- a/source/Helpers/SiteHelper.cs +++ b/source/Helpers/SiteHelper.cs @@ -29,14 +29,7 @@ public static class SiteHelper ArgumentNullException.ThrowIfNull(fs); SiteSettings siteSettings; - try - { - siteSettings = ParseSettings(configFile, options, parser, fs); - } - catch - { - throw; - } + siteSettings = ParseSettings(configFile, options, parser, fs); var site = new Site(options, siteSettings, parser, logger, null); diff --git a/source/Helpers/StopwatchReporter.cs b/source/Helpers/StopwatchReporter.cs index 8ec271b24bb52078118506cb3c890f8abaa5c2b9..62d8941940aae5c5823c2f3cc0731aaad5daa6d9 100644 --- a/source/Helpers/StopwatchReporter.cs +++ b/source/Helpers/StopwatchReporter.cs @@ -1,6 +1,7 @@ using Serilog; using System.Diagnostics; using System.Globalization; +using System.Text; namespace SuCoS.Helpers; @@ -80,25 +81,27 @@ public class StopwatchReporter var totalDurationAllSteps = stopwatches.Values.Sum(sw => sw.ElapsedMilliseconds); - var report = $@"Site '{siteTitle}' created! -═════════════════════════════════════════════"; + var report = new StringBuilder($@"Site '{siteTitle}' created! +═════════════════════════════════════════════"); for (var i = 0; i < reportData.Count; i++) { if (i == 1 || i == reportData.Count) { - report += @" -─────────────────────────────────────────────"; + report.Append(@" +─────────────────────────────────────────────"); } - report += $"\n{reportData[i].Step,-20} {reportData[i].Status,-15} {reportData[i].DurationString,-10}"; + report.Append(CultureInfo.InvariantCulture, + $"\n{reportData[i].Step,-20} {reportData[i].Status,-15} {reportData[i].DurationString,-10}"); } - report += $@" + report.Append(CultureInfo.InvariantCulture, + $@" ───────────────────────────────────────────── Total {totalDurationAllSteps} ms -═════════════════════════════════════════════"; +═════════════════════════════════════════════"); // Log the report - logger.Information(report, siteTitle); + logger.Information(report.ToString(), siteTitle); } } diff --git a/source/Models/CommandLineOptions/GenerateOptions.cs b/source/Models/CommandLineOptions/GenerateOptions.cs index d7c22639b56df98a51ee52c5a072f663b7f69bd3..752e9463b117d54f5da38d47444e5d2422605df8 100644 --- a/source/Models/CommandLineOptions/GenerateOptions.cs +++ b/source/Models/CommandLineOptions/GenerateOptions.cs @@ -14,14 +14,6 @@ public class GenerateOptions : IGenerateOptions /// public string Source => string.IsNullOrEmpty(SourceOption) ? SourceArgument : SourceOption; - /// - [Value(0)] - public string SourceArgument { private get; init; } = "./"; - - /// - [Option('s', "source", Required = false, HelpText = "Source directory path")] - public string SourceOption { private get; init; } = string.Empty; - /// [Option('d', "draft", Required = false, HelpText = "Include draft content")] public bool Draft { get; init; } @@ -33,4 +25,16 @@ public class GenerateOptions : IGenerateOptions /// [Option('e', "expired", Required = false, HelpText = "Include content with ExpiredDate dates from the past")] public bool Expired { get; init; } + + /// + /// The path of the source files + /// + [Value(0)] + public string SourceArgument { private get; init; } = "./"; + + /// + /// The path of the source files, as --source commandline option + /// + [Option('s', "source", Required = false, HelpText = "Source directory path")] + public string SourceOption { private get; init; } = string.Empty; } diff --git a/source/Models/CommandLineOptions/IGenerateOptions.cs b/source/Models/CommandLineOptions/IGenerateOptions.cs index ddac589518751f129ae2f41662f42e3bad861edd..6aa93e0e7abc7440d6558b9eafc51a4b0137eae0 100644 --- a/source/Models/CommandLineOptions/IGenerateOptions.cs +++ b/source/Models/CommandLineOptions/IGenerateOptions.cs @@ -15,16 +15,6 @@ public interface IGenerateOptions /// string Source { get; } - /// - /// The path of the source files - /// - string SourceArgument { init; } - - /// - /// The path of the source files, as --source commandline option - /// - string SourceOption { init; } - /// /// Include draft content /// diff --git a/source/Models/Site.cs b/source/Models/Site.cs index 27c326312413a640ba42042ebb9165ee21f01d47..16338f3f94947def9a03fe343d2140f8f57496f8 100644 --- a/source/Models/Site.cs +++ b/source/Models/Site.cs @@ -295,7 +295,10 @@ public class Site : ISite : markdownFiles.Where(file => file != selectedFile).ToArray(); page = ParseSourceFile(selectedFile!, parent, bundleType); - if (page is null) return; + if (page is null) + { + return; + } if (level == 0) { diff --git a/source/Parsers/YAMLParser.cs b/source/Parsers/YAMLParser.cs index 6b186f766728fcb57494232075e2a448682ac646..f2f212cdc5f028aeb604574c965bcdce7416262d 100644 --- a/source/Parsers/YAMLParser.cs +++ b/source/Parsers/YAMLParser.cs @@ -1,7 +1,5 @@ using System.Text; using FolkerKinzel.Strings; -using SuCoS.Helpers; -using SuCoS.Models; using YamlDotNet.Serialization; namespace SuCoS.Parser; @@ -27,41 +25,6 @@ public class YAMLParser : IMetadataParser .Build(); } - // /// - // public IFrontMatter ParseFrontmatterAndMarkdown( - // in string fileFullPath, - // in string fileRelativePath, - // in string fileContent - // ) - // { - // var (yaml, rawContent) = SplitFrontMatter(fileContent); - - // // Now, you can parse the YAML front matter - // var page = ParseYAML(fileFullPath, fileRelativePath, yaml, rawContent); - - // return page; - // } - - // private FrontMatter ParseYAML( - // in string fileFullPath, - // in string fileRelativePath, - // string yaml, - // in string rawContent - // ) - // { - // var frontMatter = - // deserializer.Deserialize( - // new StringReader(yaml) - // ) ?? throw new FormatException("Error parsing front matter"); - // var section = SiteHelper.GetSection(fileRelativePath); - // frontMatter.RawContent = rawContent; - // frontMatter.Section = section; - // frontMatter.SourceRelativePath = fileRelativePath; - // frontMatter.SourceFullPath = fileFullPath; - // frontMatter.Type ??= section; - // return frontMatter; - // } - /// public T Parse(string content) { @@ -78,14 +41,14 @@ public class YAMLParser : IMetadataParser /// public void Export(T data, string path) { - var deserializer = new SerializerBuilder() + var serializer = new SerializerBuilder() .IgnoreFields() .ConfigureDefaultValuesHandling( DefaultValuesHandling.OmitEmptyCollections | DefaultValuesHandling.OmitDefaults | DefaultValuesHandling.OmitNull) .Build(); - var dataString = deserializer.Serialize(data); + var dataString = serializer.Serialize(data); File.WriteAllText(path, dataString); } @@ -96,7 +59,9 @@ public class YAMLParser : IMetadataParser var frontMatterBuilder = new StringBuilder(); string? line; + // find the start of the block while ((line = content.ReadLine()) != null && line != "---") { } + // find the end of the block while ((line = content.ReadLine()) != null && line != "---") { _ = frontMatterBuilder.AppendLine(line); diff --git a/source/Program.cs b/source/Program.cs index b8a7da5d353780cdc14b845d5d8801f558ead89b..4a5e3b4e185850880ed58730fa8b976f9853c486 100644 --- a/source/Program.cs +++ b/source/Program.cs @@ -19,7 +19,7 @@ public class Program(ILogger logger) /// /// Basic logo of the program, for fun /// - public const string helloWorld = @" + private static readonly string helloWorld = @" ░█▀▀░░░░░█▀▀░░░░░█▀▀ ░▀▀█░█░█░█░░░█▀█░▀▀█ ░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀"; diff --git a/test/Commands/NewSiteCommandTests.cs b/test/Commands/NewSiteCommandTests.cs index a14823a23f9c912a277d899dc5f93d99f3d52fa8..cbb44dd3eb6f4fa26bc8d4ed599c819748c85c95 100644 --- a/test/Commands/NewSiteCommandTests.cs +++ b/test/Commands/NewSiteCommandTests.cs @@ -74,7 +74,6 @@ public class NewSiteCommandTests { // Arrange var options = new NewSiteOptions { Output = "test", Title = "Test", Description = "Test", BaseURL = "http://test.com", Force = false }; - var site = Substitute.For(); site.SourceFolders.Returns(["folder1", "folder2"]); fileSystem.FileExists(Arg.Any()).Returns(false); @@ -93,7 +92,6 @@ public class NewSiteCommandTests { // Arrange var options = new NewSiteOptions { Output = "test", Title = "Test", Description = "Test", BaseURL = "http://test.com", Force = false }; - var site = Substitute.For(); site.SourceFolders.Returns(["folder1", "folder2"]); fileSystem.FileExists(Arg.Any()).Returns(false); fileSystem.When(x => x.DirectoryCreateDirectory(Arg.Any())) diff --git a/test/Helpers/StopwatchReporterTests.cs b/test/Helpers/StopwatchReporterTests.cs index fcfc0b53cf0aa02517690d0080320657225307e6..b2e67db8e8b641d5c79422e6098b4f2ecfe757a6 100644 --- a/test/Helpers/StopwatchReporterTests.cs +++ b/test/Helpers/StopwatchReporterTests.cs @@ -12,14 +12,12 @@ namespace Tests.Helpers; public class StopwatchReporterTests { private readonly ILogger logger; - private readonly StopwatchReporter stopwatchReporter; private readonly InMemorySink inMemorySink; public StopwatchReporterTests() { inMemorySink = new InMemorySink(); logger = new LoggerConfiguration().WriteTo.Sink(inMemorySink).CreateLogger(); - stopwatchReporter = new StopwatchReporter(logger); } [Fact] @@ -50,6 +48,7 @@ public class StopwatchReporterTests var siteTitle = "TestSite"; var duration = 123; + var stopwatchReporter = new StopwatchReporter(logger); stopwatchReporter.Start(stepName); Thread.Sleep(duration); // Let's wait a bit to simulate some processing. stopwatchReporter.Stop(stepName, 1); @@ -69,6 +68,7 @@ public class StopwatchReporterTests public void Stop_ThrowsExceptionWhenStopCalledWithoutStart() { var stepName = "TestStep"; + var stopwatchReporter = new StopwatchReporter(logger); // Don't call Start for stepName diff --git a/test/ProgramTest.cs b/test/ProgramTest.cs index ae18ada644247451156d6338b73470a4c75af740..e25a052ba928dc2ad24af8cb49170c9b78fc5c88 100644 --- a/test/ProgramTest.cs +++ b/test/ProgramTest.cs @@ -1,4 +1,3 @@ -using NSubstitute; using Serilog.Events; using SuCoS; using Xunit; @@ -14,22 +13,8 @@ public class ProgramTests : TestSetup { // Act var logger = Program.CreateLogger(verbose); - - // Assert - Assert.True(logger.IsEnabled(expected)); - } - - [Fact] - public void OutputLogo_Should_LogHelloWorld() - { - // Arrange - var program = new Program(loggerMock); - - // Act - program.OutputLogo(); - program.OutputWelcome(); // Assert - loggerMock.Received(1).Information(Program.helloWorld); + Assert.True(logger.IsEnabled(expected)); } } diff --git a/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs b/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs index 33c7653474d84e051ddb075b11f38e2090bafc09..89925db4e4f5d734d98332f8a20e39b638951acd 100644 --- a/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs +++ b/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs @@ -49,8 +49,6 @@ public class RegisteredPageRequestHandlerTests : TestSetup SourceArgument = siteFullPath }; var parser = new SuCoS.Parser.YAMLParser(); - // FIXME: make it an argument - var fs = new FileSystem(); var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser, fs); site = new Site(options, siteSettings, parser, loggerMock, null); diff --git a/test/ServerHandlers/StaticFileRequestHandlerTests.cs b/test/ServerHandlers/StaticFileRequestHandlerTests.cs index c196a92c6c0d5a4d387687a7206144909b9fcd30..88a58a3f1ea738e7e25bd489ac8842cc2d1ce403 100644 --- a/test/ServerHandlers/StaticFileRequestHandlerTests.cs +++ b/test/ServerHandlers/StaticFileRequestHandlerTests.cs @@ -8,7 +8,7 @@ public class StaticFileRequestHandlerTests : TestSetup, IDisposable { private readonly string tempFilePath; - public StaticFileRequestHandlerTests() : base() + public StaticFileRequestHandlerTests() { // Creating a temporary file for testing purposes tempFilePath = Path.GetTempFileName();