From 9a86af6c764a2ecc55e6c5cd997ad4f2e546a28b Mon Sep 17 00:00:00 2001 From: Fredi Kats Date: Fri, 8 Sep 2023 01:06:51 +0400 Subject: [PATCH] Implement .editorconfig applying and result comparing --- .../ConfiguinCommands.cs | 34 ++++++++++++++++++- .../EditorConfigApplyConfiguration.cs | 13 +++++++ .../DependencyBuilder.cs | 7 ++++ Sources/Kysect.Configuin.Console/Program.cs | 3 +- .../CliExecution/CmdProcess.cs | 23 ++++++------- .../DotnetFormat/DotnetFormatCli.cs | 5 +-- .../DotnetFormatReportComparator.cs | 12 +++++-- .../DotnetFormatWarningGenerator.cs | 29 ++++++++++++++++ .../FileSystem/DeleteFileOperation.cs | 2 +- .../FileSystem/IFileMoveUndoOperation.cs | 3 +- .../FileSystem/MoveBackFileOperation.cs | 2 +- .../FileSystem/TemporaryFileReplacer.cs | 16 +++++---- .../DotnetFormat/DotnetFormatCliTests.cs | 7 ++-- .../TemporaryFileMoverTests.cs | 6 ++-- 14 files changed, 125 insertions(+), 37 deletions(-) create mode 100644 Sources/Kysect.Configuin.Console/Configuration/EditorConfigApplyConfiguration.cs create mode 100644 Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatWarningGenerator.cs diff --git a/Sources/Kysect.Configuin.Console/ConfiguinCommands.cs b/Sources/Kysect.Configuin.Console/ConfiguinCommands.cs index 88f4f70..cf410b8 100644 --- a/Sources/Kysect.Configuin.Console/ConfiguinCommands.cs +++ b/Sources/Kysect.Configuin.Console/ConfiguinCommands.cs @@ -1,10 +1,17 @@ -using Kysect.Configuin.Core.CodeStyleGeneration; +using Kysect.CommonLib.Collections.Diff; +using Kysect.CommonLib.Logging; +using Kysect.Configuin.Console.Configuration; +using Kysect.Configuin.Core.CodeStyleGeneration; using Kysect.Configuin.Core.CodeStyleGeneration.Models; +using Kysect.Configuin.Core.DotnetFormat; using Kysect.Configuin.Core.EditorConfigParsing; +using Kysect.Configuin.Core.FileSystem; using Kysect.Configuin.Core.MsLearnDocumentation; using Kysect.Configuin.Core.MsLearnDocumentation.Models; using Kysect.Configuin.Core.RoslynRuleModels; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Kysect.Configuin.Console; @@ -35,4 +42,29 @@ public void GenerateCodeStyle() CodeStyle codeStyle = codeStyleGenerator.Generate(editorConfigSettings, roslynRules); codeStyleWriter.Write(codeStyle); } + + public void GetEditorConfigWarningUpdates() + { + EditorConfigApplyConfiguration editorConfigApplyConfiguration = _serviceProvider.GetRequiredService>().Value; + DotnetFormatWarningGenerator dotnetFormatWarningGenerator = _serviceProvider.GetRequiredService(); + TemporaryFileMover temporaryFileMover = _serviceProvider.GetRequiredService(); + DotnetFormatReportComparator dotnetFormatReportComparator = _serviceProvider.GetRequiredService(); + ILogger logger = _serviceProvider.GetRequiredService(); + + IReadOnlyCollection originalWarnings = dotnetFormatWarningGenerator.GenerateWarnings(editorConfigApplyConfiguration.SolutionPath); + IReadOnlyCollection newWarnings; + + using (temporaryFileMover.MoveFile(editorConfigApplyConfiguration.NewEditorConfig, editorConfigApplyConfiguration.SourceEditorConfig)) + newWarnings = dotnetFormatWarningGenerator.GenerateWarnings(editorConfigApplyConfiguration.SolutionPath); + + CollectionDiff warningDiff = dotnetFormatReportComparator.Compare(originalWarnings, newWarnings); + + logger.LogInformation($"New warnings count: {warningDiff.Added.Count}"); + foreach (DotnetFormatFileReport dotnetFormatFileReport in warningDiff.Added) + { + logger.LogTabInformation(1, $"{dotnetFormatFileReport.FilePath}"); + foreach (DotnetFormatFileChanges dotnetFormatFileChanges in dotnetFormatFileReport.FileChanges) + logger.LogTabInformation(2, $"{dotnetFormatFileChanges.FormatDescription}"); + } + } } \ No newline at end of file diff --git a/Sources/Kysect.Configuin.Console/Configuration/EditorConfigApplyConfiguration.cs b/Sources/Kysect.Configuin.Console/Configuration/EditorConfigApplyConfiguration.cs new file mode 100644 index 0000000..dae76a2 --- /dev/null +++ b/Sources/Kysect.Configuin.Console/Configuration/EditorConfigApplyConfiguration.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace Kysect.Configuin.Console.Configuration; + +public class EditorConfigApplyConfiguration +{ + [Required] + public string SolutionPath { get; init; } = null!; + [Required] + public string SourceEditorConfig { get; init; } = null!; + [Required] + public string NewEditorConfig { get; init; } = null!; +} \ No newline at end of file diff --git a/Sources/Kysect.Configuin.Console/DependencyBuilder.cs b/Sources/Kysect.Configuin.Console/DependencyBuilder.cs index 1a3e080..4a1cdcd 100644 --- a/Sources/Kysect.Configuin.Console/DependencyBuilder.cs +++ b/Sources/Kysect.Configuin.Console/DependencyBuilder.cs @@ -2,7 +2,9 @@ using Kysect.Configuin.Console.Configuration; using Kysect.Configuin.Core.CodeStyleGeneration; using Kysect.Configuin.Core.CodeStyleGeneration.Markdown; +using Kysect.Configuin.Core.DotnetFormat; using Kysect.Configuin.Core.EditorConfigParsing; +using Kysect.Configuin.Core.FileSystem; using Kysect.Configuin.Core.MarkdownParsing.TextExtractor; using Kysect.Configuin.Core.MsLearnDocumentation; using Microsoft.Extensions.Configuration; @@ -25,6 +27,7 @@ public static IServiceProvider InitializeServiceProvider() IServiceCollection serviceCollection = builder.Services; serviceCollection.AddOptionsWithValidation(nameof(ConfiguinConfiguration)); + serviceCollection.AddOptionsWithValidation(nameof(EditorConfigApplyConfiguration)); serviceCollection.AddSingleton(CreateLogger); serviceCollection.AddSingleton(sp => @@ -57,6 +60,10 @@ public static IServiceProvider InitializeServiceProvider() return new MarkdownCodeStyleWriter(options.Value.OutputPath, logger); }); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + return serviceCollection.BuildServiceProvider(); } diff --git a/Sources/Kysect.Configuin.Console/Program.cs b/Sources/Kysect.Configuin.Console/Program.cs index d5ad16b..b1228bb 100644 --- a/Sources/Kysect.Configuin.Console/Program.cs +++ b/Sources/Kysect.Configuin.Console/Program.cs @@ -3,4 +3,5 @@ IServiceProvider s = DependencyBuilder.InitializeServiceProvider(); var configuinCommands = new ConfiguinCommands(s); -configuinCommands.GenerateCodeStyle(); \ No newline at end of file +//configuinCommands.GenerateCodeStyle(); +configuinCommands.GetEditorConfigWarningUpdates(); \ No newline at end of file diff --git a/Sources/Kysect.Configuin.Core/CliExecution/CmdProcess.cs b/Sources/Kysect.Configuin.Core/CliExecution/CmdProcess.cs index 23f7605..b27f16f 100644 --- a/Sources/Kysect.Configuin.Core/CliExecution/CmdProcess.cs +++ b/Sources/Kysect.Configuin.Core/CliExecution/CmdProcess.cs @@ -1,5 +1,4 @@ -using Kysect.CommonLib.Logging; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using System.Diagnostics; using System.Runtime.InteropServices; @@ -25,6 +24,8 @@ public CmdExecutionResult ExecuteCommand(string command) process.StartInfo = startInfo; process.Start(); + // TODO: hack. Without it process will waiting for someone read the stream or write it to parent terminal + process.StandardError.ReadToEnd(); process.WaitForExit(); int exitCode = process.ExitCode; @@ -32,11 +33,7 @@ public CmdExecutionResult ExecuteCommand(string command) var cmdExecutionResult = new CmdExecutionResult(exitCode, errors); if (cmdExecutionResult.IsAnyError()) - { - _logger.LogError("Finished with {exitCode} and {errorCount} errors.", exitCode, errors.Count); - foreach (string error in cmdExecutionResult.Errors) - _logger.LogTabError(1, error); - } + _logger.LogError("Cmd execution finished with exit code {exitCode}.", exitCode); process.Close(); @@ -76,12 +73,12 @@ private IReadOnlyCollection GetErrors(Process process) // TODO: fixed error stream reading // Line splitting triggered by char limit =_= - while (!process.StandardError.EndOfStream) - { - string? line = process.StandardError.ReadLine(); - if (line is not null) - errors.Add(line); - } + //while (!process.StandardError.EndOfStream) + //{ + // string? line = process.StandardError.ReadLine(); + // if (line is not null) + // errors.Add(line); + //} return errors; } diff --git a/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatCli.cs b/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatCli.cs index 47f7991..a4af8a8 100644 --- a/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatCli.cs +++ b/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatCli.cs @@ -20,9 +20,10 @@ public void Validate() _cmdProcess.ExecuteCommand("dotnet format -h").ThrowIfAnyError(); } - public void GenerateWarnings(string pathToSolution, string pathToJson) + public void Format(string pathToSolution, string pathToJson) { _logger.LogInformation("Generate warnings for {pathToSolution} and write result to {pathToJson}", pathToSolution, pathToJson); - _cmdProcess.ExecuteCommand($"dotnet format \"{pathToSolution}\" --verify-no-changes --report \"{pathToJson}\"").ThrowIfAnyError(); + // TODO: handle exceptions in some way? + _cmdProcess.ExecuteCommand($"dotnet format \"{pathToSolution}\" --verify-no-changes --report \"{pathToJson}\""); } } \ No newline at end of file diff --git a/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatReportComparator.cs b/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatReportComparator.cs index 610f743..685d9de 100644 --- a/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatReportComparator.cs +++ b/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatReportComparator.cs @@ -16,13 +16,19 @@ public DotnetFormatReportComparator(ILogger logger) public CollectionDiff Compare(string left, string right) { - _logger.LogInformation("Comparing dotnet format report between {left} and {right}", left, right); + _logger.LogInformation("Loading warnings for {left} and {right}", left, right); IReadOnlyCollection leftReports = JsonSerializer.Deserialize>(left).ThrowIfNull(); IReadOnlyCollection rightReports = JsonSerializer.Deserialize>(right).ThrowIfNull(); - var diff = CollectionDiff.Create(leftReports, rightReports, DotnetFormatFileReportComparator.Instance); - _logger.LogInformation("Same: {same}, added: {added}, removed: {removed}", diff.Same, diff.Added, diff.Removed); + return Compare(leftReports, rightReports); + } + + public CollectionDiff Compare(IReadOnlyCollection left, IReadOnlyCollection right) + { + _logger.LogInformation("Comparing dotnet format report"); + var diff = CollectionDiff.Create(left, right, DotnetFormatFileReportComparator.Instance); + _logger.LogInformation("Same: {same}, added: {added}, removed: {removed}", diff.Same.Count, diff.Added.Count, diff.Removed.Count); return diff; } diff --git a/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatWarningGenerator.cs b/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatWarningGenerator.cs new file mode 100644 index 0000000..e4ad026 --- /dev/null +++ b/Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatWarningGenerator.cs @@ -0,0 +1,29 @@ +using Kysect.CommonLib.BaseTypes.Extensions; +using Microsoft.Extensions.Logging; +using System.Text.Json; + +namespace Kysect.Configuin.Core.DotnetFormat; + +public class DotnetFormatWarningGenerator +{ + private readonly ILogger _logger; + private readonly DotnetFormatCli _dotnetFormatCli; + + public DotnetFormatWarningGenerator(ILogger logger) + { + _logger = logger; + + _dotnetFormatCli = new DotnetFormatCli(logger); + } + + public IReadOnlyCollection GenerateWarnings(string pathToSolution) + { + string filePath = $"output-{Guid.NewGuid()}.json"; + _logger.LogInformation("Generate dotnet format warnings for {path} will save to {output}", pathToSolution, filePath); + _dotnetFormatCli.Format(pathToSolution, filePath); + string warningFileContent = File.ReadAllText(filePath); + _logger.LogInformation("Remove temp file {path}", filePath); + File.Delete(filePath); + return JsonSerializer.Deserialize>(warningFileContent).ThrowIfNull(); + } +} \ No newline at end of file diff --git a/Sources/Kysect.Configuin.Core/FileSystem/DeleteFileOperation.cs b/Sources/Kysect.Configuin.Core/FileSystem/DeleteFileOperation.cs index dba5a57..10338e2 100644 --- a/Sources/Kysect.Configuin.Core/FileSystem/DeleteFileOperation.cs +++ b/Sources/Kysect.Configuin.Core/FileSystem/DeleteFileOperation.cs @@ -13,7 +13,7 @@ public DeleteFileOperation(string path, ILogger logger) _logger = logger; } - public void Execute() + public void Dispose() { _logger.LogInformation("Undo file move. Delete {path}", _path); File.Delete(_path); diff --git a/Sources/Kysect.Configuin.Core/FileSystem/IFileMoveUndoOperation.cs b/Sources/Kysect.Configuin.Core/FileSystem/IFileMoveUndoOperation.cs index 725f501..b374838 100644 --- a/Sources/Kysect.Configuin.Core/FileSystem/IFileMoveUndoOperation.cs +++ b/Sources/Kysect.Configuin.Core/FileSystem/IFileMoveUndoOperation.cs @@ -1,6 +1,5 @@ namespace Kysect.Configuin.Core.FileSystem; -public interface IFileMoveUndoOperation +public interface IFileMoveUndoOperation : IDisposable { - void Execute(); } \ No newline at end of file diff --git a/Sources/Kysect.Configuin.Core/FileSystem/MoveBackFileOperation.cs b/Sources/Kysect.Configuin.Core/FileSystem/MoveBackFileOperation.cs index 504840f..9fc63e2 100644 --- a/Sources/Kysect.Configuin.Core/FileSystem/MoveBackFileOperation.cs +++ b/Sources/Kysect.Configuin.Core/FileSystem/MoveBackFileOperation.cs @@ -15,7 +15,7 @@ public MoveBackFileOperation(string source, string target, ILogger logger) _logger = logger; } - public void Execute() + public void Dispose() { _logger.LogInformation("Undo file move. Move backup file from {source} to {target}", _source, _target); File.Move(_source, _target, overwrite: true); diff --git a/Sources/Kysect.Configuin.Core/FileSystem/TemporaryFileReplacer.cs b/Sources/Kysect.Configuin.Core/FileSystem/TemporaryFileReplacer.cs index f81e47c..c5ffa44 100644 --- a/Sources/Kysect.Configuin.Core/FileSystem/TemporaryFileReplacer.cs +++ b/Sources/Kysect.Configuin.Core/FileSystem/TemporaryFileReplacer.cs @@ -1,23 +1,21 @@ -using Kysect.CommonLib.FileSystem.Extensions; +using Kysect.CommonLib.BaseTypes.Extensions; +using Kysect.CommonLib.FileSystem.Extensions; using Microsoft.Extensions.Logging; namespace Kysect.Configuin.Core.FileSystem; public class TemporaryFileMover { - private readonly string _tempDirectory; private readonly ILogger _logger; - public TemporaryFileMover(string tempDirectory, ILogger logger) + public TemporaryFileMover(ILogger logger) { - _tempDirectory = tempDirectory; _logger = logger; } public IFileMoveUndoOperation MoveFile(string sourcePath, string targetPath) { _logger.LogInformation("Move {sourcePath} to {targetPath}", sourcePath, targetPath); - DirectoryExtensions.EnsureFileExists(_tempDirectory); if (!File.Exists(sourcePath)) throw new FileNotFoundException(sourcePath); @@ -25,10 +23,14 @@ public IFileMoveUndoOperation MoveFile(string sourcePath, string targetPath) if (File.Exists(targetPath)) { var targetFileInfo = new FileInfo(targetPath); - string tempFilePath = Path.Combine(_tempDirectory, targetFileInfo.Name); + DirectoryInfo targetFileDirectory = targetFileInfo.Directory.ThrowIfNull(); + string tempFileDirectory = Path.Combine(targetFileDirectory.FullName, ".congifuing"); + DirectoryExtensions.EnsureFileExists(tempFileDirectory); + + string tempFilePath = Path.Combine(tempFileDirectory, targetFileInfo.Name); _logger.LogInformation("Target path already exists. Save target file to temp path {tempPath}", tempFilePath); _logger.LogInformation("Move {targetPath} to {tempFilePath}", targetPath, tempFilePath); - File.Move(targetPath, tempFilePath); + File.Move(targetPath, tempFilePath, overwrite: true); _logger.LogInformation("Copy {sourcePath} to {targetPath}", sourcePath, targetPath); File.Copy(sourcePath, targetPath, overwrite: true); return new MoveBackFileOperation(tempFilePath, targetPath, _logger); diff --git a/Sources/Kysect.Configuin.Tests/DotnetFormat/DotnetFormatCliTests.cs b/Sources/Kysect.Configuin.Tests/DotnetFormat/DotnetFormatCliTests.cs index 5ba0781..7b52b04 100644 --- a/Sources/Kysect.Configuin.Tests/DotnetFormat/DotnetFormatCliTests.cs +++ b/Sources/Kysect.Configuin.Tests/DotnetFormat/DotnetFormatCliTests.cs @@ -21,11 +21,12 @@ public void Validate_FinishedWithoutErrors() } [Test] - [Ignore("This test require infrastructure")] + //[Ignore("This test require infrastructure")] public void GenerateWarnings_CreateReportFile() { - const string pathToSln = "./../../../../"; + //const string pathToSln = "./../../../../"; + const string pathToSln = "C:\\Coding\\Kysect.PowerShellRunner\\Sources\\Kysect.PowerShellRunner.sln"; - _dotnetFormatCli.GenerateWarnings(pathToSln, "sample.json"); + _dotnetFormatCli.Format(pathToSln, "sample.json"); } } \ No newline at end of file diff --git a/Sources/Kysect.Configuin.Tests/TemporaryFileMoverTests.cs b/Sources/Kysect.Configuin.Tests/TemporaryFileMoverTests.cs index 18f6694..09135af 100644 --- a/Sources/Kysect.Configuin.Tests/TemporaryFileMoverTests.cs +++ b/Sources/Kysect.Configuin.Tests/TemporaryFileMoverTests.cs @@ -14,7 +14,7 @@ public class TemporaryFileMoverTests public TemporaryFileMoverTests() { - _sut = new TemporaryFileMover(Path.Combine(TestGenerated, "TempDir"), TestLogger.ProviderForTests()); + _sut = new TemporaryFileMover(TestLogger.ProviderForTests()); } [SetUp] @@ -38,7 +38,7 @@ public void MoveFile_WhenTargetNotExists_UndoDeleteFile() IFileMoveUndoOperation undoAction = _sut.MoveFile(fileForMove, targetPath); File.Exists(targetPath).Should().BeTrue(); - undoAction.Execute(); + undoAction.Dispose(); File.Exists(targetPath).Should().BeFalse(); } @@ -56,7 +56,7 @@ public void MoveFile_WhenTargetExists_UndoReturnOriginalFile() File.Exists(targetFile).Should().BeTrue(); File.ReadAllText(targetFile).Should().Be(fileForMove); - undoAction.Execute(); + undoAction.Dispose(); File.Exists(targetFile).Should().BeTrue(); File.ReadAllText(targetFile).Should().Be(targetFile);