Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement .editorconfig applying and result comparing #59

Merged
merged 2 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 42 additions & 11 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,48 @@ class MyClass
\```
```

### Предпросмотр изменений от editorconfig'а

Configuin генерирует список изменений, который будут получены, если к проекту применить .editorconfig.

Алгоритм:
- Запускается `dotnet format` для проекта и сохраняется список сообщений
- Подменяется .editorconfig на то, который указал пользователь
- Запускается `dotnet format` и сохраняется второй результат
- Генерируется diff между результатами
- Откатывается изменение .editorconfig'а

Пример использования:

1. Берётся проект с .editorconfig'ом
2. Копируется .editorconfig и модифицируется, например, включается CA1032
3. Запускается Configuin, указывается путь к солюшену и изменённому .editorconfig
4. Генерируется результат:

```log
[18:24:58 INF] Generate dotnet format warnings for C:\Coding\Kysect.CommonLib\Sources\Kysect.CommonLib.sln will save to output-8b107f73-6763-4c1f-b643-4e8eabac9d91.json
[18:24:58 INF] Generate warnings for C:\Coding\Kysect.CommonLib\Sources\Kysect.CommonLib.sln and write result to output-8b107f73-6763-4c1f-b643-4e8eabac9d91.json
[18:24:58 VRB] Execute cmd command cmd.exe /C dotnet format "C:\Coding\Kysect.CommonLib\Sources\Kysect.CommonLib.sln" --verify-no-changes --report "output-8b107f73-6763-4c1f-b643-4e8eabac9d91.json"
[18:25:08 INF] Remove temp file output-8b107f73-6763-4c1f-b643-4e8eabac9d91.json
[18:25:08 INF] Move C:\Users\fredi\Desktop\.editorconfig to C:\Coding\Kysect.CommonLib\Sources\.editorconfig
[18:25:08 INF] Target path already exists. Save target file to temp path C:\Coding\Kysect.CommonLib\Sources\.congifuing\.editorconfig
[18:25:08 INF] Move C:\Coding\Kysect.CommonLib\Sources\.editorconfig to C:\Coding\Kysect.CommonLib\Sources\.congifuing\.editorconfig
[18:25:08 INF] Copy C:\Users\fredi\Desktop\.editorconfig to C:\Coding\Kysect.CommonLib\Sources\.editorconfig
[18:25:08 INF] Generate dotnet format warnings for C:\Coding\Kysect.CommonLib\Sources\Kysect.CommonLib.sln will save to output-4d210962-92ec-44c8-b367-35840f6403d9.json
[18:25:08 INF] Generate warnings for C:\Coding\Kysect.CommonLib\Sources\Kysect.CommonLib.sln and write result to output-4d210962-92ec-44c8-b367-35840f6403d9.json
[18:25:08 VRB] Execute cmd command cmd.exe /C dotnet format "C:\Coding\Kysect.CommonLib\Sources\Kysect.CommonLib.sln" --verify-no-changes --report "output-4d210962-92ec-44c8-b367-35840f6403d9.json"
[18:25:16 ERR] Cmd execution finished with exit code 2.
[18:25:16 INF] Remove temp file output-4d210962-92ec-44c8-b367-35840f6403d9.json
[18:25:16 INF] Undo file move. Move backup file from C:\Coding\Kysect.CommonLib\Sources\.congifuing\.editorconfig to C:\Coding\Kysect.CommonLib\Sources\.editorconfig
[18:25:16 INF] Comparing dotnet format report
[18:25:16 INF] Same: 0, added: 2, removed: 0
[18:25:16 INF] New warnings count: 2
[18:25:16 INF] C:\Coding\Kysect.CommonLib\Sources\Kysect.CommonLib\Reflection\ReflectionException.cs
[18:25:16 INF] error CA1032: Add the following constructor to ReflectionException: public ReflectionException()
[18:25:16 INF] C:\Coding\Kysect.CommonLib\Sources\Kysect.CommonLib\Reflection\ReflectionException.cs
[18:25:16 INF] error CA1032: Add the following constructor to ReflectionException: public ReflectionException(string message)
```

### [in progress] Сравнение двух editorconfig

Configuin сравнивает два .editorconfig файла и генерирует diff между ними.
Expand All @@ -57,14 +99,3 @@ Configuin анализирует editorconfig файл и:
- Генерирует список существующих правил, которые не указаны в .editorconfig

### [in progress] Форматирование editorconfig'а

### [in progress] Предпросмотр изменений от editorconfig'а

Configuin генерирует список изменений, который будут получены, если к проекту применить .editorconfig.

Алгоритм:
- Запускается `dotnet format` для проекта и сохраняется список сообщений
- Подменяется .editorconfig на то, который указал пользователь
- Запускается `dotnet format` и сохраняется второй результат
- Генерируется diff между результатами
- Откатывается изменение .editorconfig'а
34 changes: 33 additions & 1 deletion Sources/Kysect.Configuin.Console/ConfiguinCommands.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -35,4 +42,29 @@ public void GenerateCodeStyle()
CodeStyle codeStyle = codeStyleGenerator.Generate(editorConfigSettings, roslynRules);
codeStyleWriter.Write(codeStyle);
}

public void GetEditorConfigWarningUpdates()
{
EditorConfigApplyConfiguration editorConfigApplyConfiguration = _serviceProvider.GetRequiredService<IOptions<EditorConfigApplyConfiguration>>().Value;
DotnetFormatWarningGenerator dotnetFormatWarningGenerator = _serviceProvider.GetRequiredService<DotnetFormatWarningGenerator>();
TemporaryFileMover temporaryFileMover = _serviceProvider.GetRequiredService<TemporaryFileMover>();
DotnetFormatReportComparator dotnetFormatReportComparator = _serviceProvider.GetRequiredService<DotnetFormatReportComparator>();
ILogger logger = _serviceProvider.GetRequiredService<ILogger>();

IReadOnlyCollection<DotnetFormatFileReport> originalWarnings = dotnetFormatWarningGenerator.GenerateWarnings(editorConfigApplyConfiguration.SolutionPath);
IReadOnlyCollection<DotnetFormatFileReport> newWarnings;

using (temporaryFileMover.MoveFile(editorConfigApplyConfiguration.NewEditorConfig, editorConfigApplyConfiguration.SourceEditorConfig))
newWarnings = dotnetFormatWarningGenerator.GenerateWarnings(editorConfigApplyConfiguration.SolutionPath);

CollectionDiff<DotnetFormatFileReport> 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}");
}
}
}
Original file line number Diff line number Diff line change
@@ -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!;
}
7 changes: 7 additions & 0 deletions Sources/Kysect.Configuin.Console/DependencyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,6 +27,7 @@ public static IServiceProvider InitializeServiceProvider()
IServiceCollection serviceCollection = builder.Services;

serviceCollection.AddOptionsWithValidation<ConfiguinConfiguration>(nameof(ConfiguinConfiguration));
serviceCollection.AddOptionsWithValidation<EditorConfigApplyConfiguration>(nameof(EditorConfigApplyConfiguration));
serviceCollection.AddSingleton(CreateLogger);

serviceCollection.AddSingleton<IEditorConfigContentProvider>(sp =>
Expand Down Expand Up @@ -57,6 +60,10 @@ public static IServiceProvider InitializeServiceProvider()
return new MarkdownCodeStyleWriter(options.Value.OutputPath, logger);
});

serviceCollection.AddSingleton<DotnetFormatWarningGenerator>();
serviceCollection.AddSingleton<TemporaryFileMover>();
serviceCollection.AddSingleton<DotnetFormatReportComparator>();

return serviceCollection.BuildServiceProvider();
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/Kysect.Configuin.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
IServiceProvider s = DependencyBuilder.InitializeServiceProvider();
var configuinCommands = new ConfiguinCommands(s);

configuinCommands.GenerateCodeStyle();
//configuinCommands.GenerateCodeStyle();
configuinCommands.GetEditorConfigWarningUpdates();
23 changes: 10 additions & 13 deletions Sources/Kysect.Configuin.Core/CliExecution/CmdProcess.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Kysect.CommonLib.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Runtime.InteropServices;

Expand All @@ -25,18 +24,16 @@ 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;
IReadOnlyCollection<string> errors = GetErrors(process);
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();

Expand Down Expand Up @@ -76,12 +73,12 @@ private IReadOnlyCollection<string> 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;
}
Expand Down
5 changes: 3 additions & 2 deletions Sources/Kysect.Configuin.Core/DotnetFormat/DotnetFormatCli.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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}\"");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@ public DotnetFormatReportComparator(ILogger logger)

public CollectionDiff<DotnetFormatFileReport> 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<DotnetFormatFileReport> leftReports = JsonSerializer.Deserialize<IReadOnlyCollection<DotnetFormatFileReport>>(left).ThrowIfNull();
IReadOnlyCollection<DotnetFormatFileReport> rightReports = JsonSerializer.Deserialize<IReadOnlyCollection<DotnetFormatFileReport>>(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<DotnetFormatFileReport> Compare(IReadOnlyCollection<DotnetFormatFileReport> left, IReadOnlyCollection<DotnetFormatFileReport> 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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<DotnetFormatFileReport> 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<IReadOnlyCollection<DotnetFormatFileReport>>(warningFileContent).ThrowIfNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
namespace Kysect.Configuin.Core.FileSystem;

public interface IFileMoveUndoOperation
public interface IFileMoveUndoOperation : IDisposable
{
void Execute();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
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);

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
6 changes: 3 additions & 3 deletions Sources/Kysect.Configuin.Tests/TemporaryFileMoverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class TemporaryFileMoverTests

public TemporaryFileMoverTests()
{
_sut = new TemporaryFileMover(Path.Combine(TestGenerated, "TempDir"), TestLogger.ProviderForTests());
_sut = new TemporaryFileMover(TestLogger.ProviderForTests());
}

[SetUp]
Expand All @@ -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();
}

Expand All @@ -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);
Expand Down