From 9616a80e78711de32e5e79d01b16d8199dd514ae Mon Sep 17 00:00:00 2001 From: zitzlthotietoevry Date: Mon, 24 Apr 2023 07:53:56 +0200 Subject: [PATCH 1/6] simple json output, skip not supported projects --- global.json | 2 +- src/Snitch/Analysis/ProjectAnalyzerResult.cs | 1 + src/Snitch/Analysis/ProjectReporter.cs | 21 ++++++++++++++++++++ src/Snitch/Commands/AnalyzeCommand.cs | 20 +++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/global.json b/global.json index 6e4fd73..336164b 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "projects": [ "src" ], "sdk": { - "version": "6.0.100", + "version": "6.0.400", "rollForward": "latestPatch" } } diff --git a/src/Snitch/Analysis/ProjectAnalyzerResult.cs b/src/Snitch/Analysis/ProjectAnalyzerResult.cs index 0e6503c..84fe6c7 100644 --- a/src/Snitch/Analysis/ProjectAnalyzerResult.cs +++ b/src/Snitch/Analysis/ProjectAnalyzerResult.cs @@ -10,6 +10,7 @@ internal sealed class ProjectAnalyzerResult private readonly List _packages; public string Project => _project.Name; + public string ProjectPath => _project.Path; public IReadOnlyList CanBeRemoved { get; } public IReadOnlyList MightBeRemoved { get; } public IReadOnlyList PreReleasePackages { get; } diff --git a/src/Snitch/Analysis/ProjectReporter.cs b/src/Snitch/Analysis/ProjectReporter.cs index ac4eab2..85a6622 100644 --- a/src/Snitch/Analysis/ProjectReporter.cs +++ b/src/Snitch/Analysis/ProjectReporter.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; +using System.Text.Json; using Spectre.Console; namespace Snitch.Analysis @@ -133,5 +135,24 @@ public void WriteToConsole([NotNull] List results, bool n .RoundedBorder() .BorderColor(Color.Grey)); } + + internal void WriteToFile(List analyzerResults, string outputFileName) + { + var removeResult = + analyzerResults + .Where(x => x.CanBeRemoved.Count > 0) + .Select(x => new + { + x.Project, + x.ProjectPath, + CanBeRemoved = x.CanBeRemoved.Select(y => y.Package.Name), + }); + + using FileStream createStream = File.Create(outputFileName); + JsonSerializer.Serialize(createStream, removeResult, new JsonSerializerOptions() + { + WriteIndented = true + }); + } } } diff --git a/src/Snitch/Commands/AnalyzeCommand.cs b/src/Snitch/Commands/AnalyzeCommand.cs index 099d666..335749a 100644 --- a/src/Snitch/Commands/AnalyzeCommand.cs +++ b/src/Snitch/Commands/AnalyzeCommand.cs @@ -44,6 +44,10 @@ public sealed class Settings : CommandSettings [CommandOption("--no-prerelease")] [Description("Verifies that all package references are not pre-releases.")] public bool NoPreRelease { get; set; } + + [CommandOption("-o|--out ")] + [Description("The ouput file to write.")] + public string? OutputFileName { get; set; } } public AnalyzeCommand(IAnsiConsole console) @@ -79,6 +83,16 @@ public override int Execute([NotNull] CommandContext context, [NotNull] Settings foreach (var projectToAnalyze in projectsToAnalyze) { + if (!projectToAnalyze.EndsWith("csproj", StringComparison.OrdinalIgnoreCase) + && !projectToAnalyze.EndsWith("fsproj", StringComparison.OrdinalIgnoreCase)) + { + var projectName = Path.GetFileNameWithoutExtension(projectToAnalyze); + _console.MarkupLine($"Skipping Non .NET Project [aqua]{projectName}[/]"); + _console.WriteLine(); + + continue; + } + // Perform a design time build of the project. var buildResult = _builder.Build( projectToAnalyze, @@ -107,6 +121,12 @@ public override int Execute([NotNull] CommandContext context, [NotNull] Settings // Write the report to the console _reporter.WriteToConsole(analyzerResults, settings.NoPreRelease); + if (settings.OutputFileName != null) + { + // Write the report to a file. + _reporter.WriteToFile(analyzerResults, settings.OutputFileName); + } + // Return the correct exit code. return GetExitCode(settings, analyzerResults); }); From 09045c346d28fcfa66a9ae1f5d77565987b20b65 Mon Sep 17 00:00:00 2001 From: zitzlthotietoevry Date: Mon, 24 Apr 2023 14:35:48 +0200 Subject: [PATCH 2/6] json exporter --- src/Snitch/Analysis/ProjectAnalyzerResult.cs | 1 - src/Snitch/Analysis/ProjectFileReporter.cs | 44 ++++++++++++++++++++ src/Snitch/Analysis/ProjectReporter.cs | 21 ---------- src/Snitch/Commands/AnalyzeCommand.cs | 6 ++- 4 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 src/Snitch/Analysis/ProjectFileReporter.cs diff --git a/src/Snitch/Analysis/ProjectAnalyzerResult.cs b/src/Snitch/Analysis/ProjectAnalyzerResult.cs index 84fe6c7..0e6503c 100644 --- a/src/Snitch/Analysis/ProjectAnalyzerResult.cs +++ b/src/Snitch/Analysis/ProjectAnalyzerResult.cs @@ -10,7 +10,6 @@ internal sealed class ProjectAnalyzerResult private readonly List _packages; public string Project => _project.Name; - public string ProjectPath => _project.Path; public IReadOnlyList CanBeRemoved { get; } public IReadOnlyList MightBeRemoved { get; } public IReadOnlyList PreReleasePackages { get; } diff --git a/src/Snitch/Analysis/ProjectFileReporter.cs b/src/Snitch/Analysis/ProjectFileReporter.cs new file mode 100644 index 0000000..00817e3 --- /dev/null +++ b/src/Snitch/Analysis/ProjectFileReporter.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Text.Json; +using Spectre.Console; + +namespace Snitch.Analysis +{ + internal class ProjectFileReporter + { + public ProjectFileReporter() + { + } + + internal void WriteToFile(List analyzerResults, string outputFileName) + { + var removeResult = + analyzerResults + .Where(x => x.CanBeRemoved.Count > 0) + .Select(x => new + { + x.Project, + CanBeRemoved = x.CanBeRemoved.Select(y => new + { + PackageName = y.Package.Name, + ReferencedBy = y.Original.Project.Name, + }), + MightBeRemoved = x.MightBeRemoved.Select(y => new + { + PackageName = y.Package.Name, + ReferencedBy = y.Original.Project.Name, + }), + }); + + using FileStream createStream = File.Create(outputFileName); + JsonSerializer.Serialize(createStream, removeResult, new JsonSerializerOptions() + { + WriteIndented = true, + }); + } + } +} diff --git a/src/Snitch/Analysis/ProjectReporter.cs b/src/Snitch/Analysis/ProjectReporter.cs index 85a6622..ac4eab2 100644 --- a/src/Snitch/Analysis/ProjectReporter.cs +++ b/src/Snitch/Analysis/ProjectReporter.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Linq; -using System.Text.Json; using Spectre.Console; namespace Snitch.Analysis @@ -135,24 +133,5 @@ public void WriteToConsole([NotNull] List results, bool n .RoundedBorder() .BorderColor(Color.Grey)); } - - internal void WriteToFile(List analyzerResults, string outputFileName) - { - var removeResult = - analyzerResults - .Where(x => x.CanBeRemoved.Count > 0) - .Select(x => new - { - x.Project, - x.ProjectPath, - CanBeRemoved = x.CanBeRemoved.Select(y => y.Package.Name), - }); - - using FileStream createStream = File.Create(outputFileName); - JsonSerializer.Serialize(createStream, removeResult, new JsonSerializerOptions() - { - WriteIndented = true - }); - } } } diff --git a/src/Snitch/Commands/AnalyzeCommand.cs b/src/Snitch/Commands/AnalyzeCommand.cs index 335749a..20ff79a 100644 --- a/src/Snitch/Commands/AnalyzeCommand.cs +++ b/src/Snitch/Commands/AnalyzeCommand.cs @@ -18,6 +18,7 @@ public sealed class AnalyzeCommand : Command private readonly ProjectBuilder _builder; private readonly ProjectAnalyzer _analyzer; private readonly ProjectReporter _reporter; + private readonly ProjectFileReporter _fileReporter; public sealed class Settings : CommandSettings { @@ -46,7 +47,7 @@ public sealed class Settings : CommandSettings public bool NoPreRelease { get; set; } [CommandOption("-o|--out ")] - [Description("The ouput file to write.")] + [Description("The name of the json output file to write.")] public string? OutputFileName { get; set; } } @@ -56,6 +57,7 @@ public AnalyzeCommand(IAnsiConsole console) _builder = new ProjectBuilder(console); _analyzer = new ProjectAnalyzer(); _reporter = new ProjectReporter(console); + _fileReporter = new ProjectFileReporter(); } public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings) @@ -124,7 +126,7 @@ public override int Execute([NotNull] CommandContext context, [NotNull] Settings if (settings.OutputFileName != null) { // Write the report to a file. - _reporter.WriteToFile(analyzerResults, settings.OutputFileName); + _fileReporter.WriteToFile(analyzerResults, settings.OutputFileName); } // Return the correct exit code. From 41e94e9ace85521e7b8b2dfcee751912386f587b Mon Sep 17 00:00:00 2001 From: Thomas Zitzler Date: Tue, 25 Apr 2023 13:31:23 +0200 Subject: [PATCH 3/6] trycatch --- src/Snitch/Analysis/ProjectBuilder.cs | 24 +++++++---- src/Snitch/Analysis/ProjectFileReporter.cs | 2 + src/Snitch/Commands/AnalyzeCommand.cs | 49 +++++++++++++--------- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/Snitch/Analysis/ProjectBuilder.cs b/src/Snitch/Analysis/ProjectBuilder.cs index 84802ed..6fc9e4f 100644 --- a/src/Snitch/Analysis/ProjectBuilder.cs +++ b/src/Snitch/Analysis/ProjectBuilder.cs @@ -153,18 +153,26 @@ private Project Build( ? $"{prefix}Analyzing [aqua]{project.Name}[/]..." : $"{prefix}Analyzing [aqua]{project.Name}[/] [grey]({tfm})[/]..."; - _console.MarkupLine(status); + try + { + _console.MarkupLine(status); + + var projectAnalyzer = manager.GetProject(project.Path); + var results = (IEnumerable)projectAnalyzer.Build(); - var projectAnalyzer = manager.GetProject(project.Path); - var results = (IEnumerable)projectAnalyzer.Build(); + if (!string.IsNullOrWhiteSpace(tfm)) + { + var closest = results.GetNearestFrameworkMoniker(tfm); + results = results.Where(p => p.TargetFramework.Equals(closest, StringComparison.OrdinalIgnoreCase)); + } - if (!string.IsNullOrWhiteSpace(tfm)) + return results.FirstOrDefault(); + } + catch (Exception ex) { - var closest = results.GetNearestFrameworkMoniker(tfm); - results = results.Where(p => p.TargetFramework.Equals(closest, StringComparison.OrdinalIgnoreCase)); + _console.MarkupLine($"{prefix} [red]ERROR:[/] {ex.Message}"); + return null; } - - return results.FirstOrDefault(); } } } diff --git a/src/Snitch/Analysis/ProjectFileReporter.cs b/src/Snitch/Analysis/ProjectFileReporter.cs index 00817e3..5f26653 100644 --- a/src/Snitch/Analysis/ProjectFileReporter.cs +++ b/src/Snitch/Analysis/ProjectFileReporter.cs @@ -30,7 +30,9 @@ internal void WriteToFile(List analyzerResults, string ou MightBeRemoved = x.MightBeRemoved.Select(y => new { PackageName = y.Package.Name, + PackageVersion = y.Package.Version, ReferencedBy = y.Original.Project.Name, + ReferencePackageVersion = y.Original.Package.Version, }), }); diff --git a/src/Snitch/Commands/AnalyzeCommand.cs b/src/Snitch/Commands/AnalyzeCommand.cs index 20ff79a..0c37d0e 100644 --- a/src/Snitch/Commands/AnalyzeCommand.cs +++ b/src/Snitch/Commands/AnalyzeCommand.cs @@ -95,29 +95,40 @@ public override int Execute([NotNull] CommandContext context, [NotNull] Settings continue; } - // Perform a design time build of the project. - var buildResult = _builder.Build( - projectToAnalyze, - targetFramework, - settings.Skip, - projectCache); - - // Update the cache of built projects. - projectCache.Add(buildResult.Project); - foreach (var item in buildResult.Dependencies) + try { - projectCache.Add(item); + // Perform a design time build of the project. + var buildResult = _builder.Build( + projectToAnalyze, + targetFramework, + settings.Skip, + projectCache); + + // Update the cache of built projects. + projectCache.Add(buildResult.Project); + foreach (var item in buildResult.Dependencies) + { + projectCache.Add(item); + } + + // Analyze the project. + var analyzeResult = _analyzer.Analyze(buildResult.Project); + if (settings.Exclude?.Length > 0) + { + // Filter packages that should be excluded. + analyzeResult = analyzeResult.Filter(settings.Exclude); + } + + analyzerResults.Add(analyzeResult); } - - // Analyze the project. - var analyzeResult = _analyzer.Analyze(buildResult.Project); - if (settings.Exclude?.Length > 0) + catch (Exception ex) { - // Filter packages that should be excluded. - analyzeResult = analyzeResult.Filter(settings.Exclude); + _console.MarkupLine($" [red]ERROR:[/] {ex.Message}"); + if (settings.Strict) + { + return -1; + } } - - analyzerResults.Add(analyzeResult); } // Write the report to the console From 88461762e75e90d10b3a96cdadfefd1369dfc7f1 Mon Sep 17 00:00:00 2001 From: Thomas Zitzler Date: Thu, 27 Apr 2023 08:20:27 +0200 Subject: [PATCH 4/6] add tests and documentation --- README.md | 13 ++++ src/Snitch.Tests.Fixtures/FooBar/Class1.cs | 8 +++ .../FooBar/FooBar.csproj | 8 +++ .../Expectations/FooBar.Default.verified.txt | 6 ++ .../Expectations/FooBar.Strict.verified.txt | 4 ++ src/Snitch.Tests/ProgramTests.cs | 71 ++++++++++++------- src/Snitch.Tests/VerifyConfiguration.cs | 2 +- src/Snitch/Analysis/ProjectFileReporter.cs | 21 ++++-- src/Snitch/Analysis/ProjectReporter.cs | 2 +- src/Snitch/Commands/AnalyzeCommand.cs | 4 +- src/Snitch/Program.cs | 1 + 11 files changed, 104 insertions(+), 36 deletions(-) create mode 100644 src/Snitch.Tests.Fixtures/FooBar/Class1.cs create mode 100644 src/Snitch.Tests.Fixtures/FooBar/FooBar.csproj create mode 100644 src/Snitch.Tests/Expectations/FooBar.Default.verified.txt create mode 100644 src/Snitch.Tests/Expectations/FooBar.Strict.verified.txt diff --git a/README.md b/README.md index 6171a4d..c1f2f17 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,19 @@ _Examine a specific project or solution to make sure there are no pre-release pa > snitch MyProject.csproj --no-prerelease ``` +_Examine a specific project or solution and export the result to a json file + +``` +> snitch MyProject.csproj --out c:\temp\snitch.json +``` + +That json file can be also used e.g. to auto-uninstall the detected packages in package manager console in Visual Studio: +``` +function Uninstall-FromSnitch { param($filename) (Get-Content $filename | ConvertFrom-Json) | %{ $p = $_.Project; foreach ($c in $_.CanBeRemoved) { Uninstall-Package $c.PackageName -ProjectName $p } } } +Uninstall-FromSnitch C:\temp\snitch.json +``` + + ## Building Snitch from source ``` diff --git a/src/Snitch.Tests.Fixtures/FooBar/Class1.cs b/src/Snitch.Tests.Fixtures/FooBar/Class1.cs new file mode 100644 index 0000000..8b7292a --- /dev/null +++ b/src/Snitch.Tests.Fixtures/FooBar/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace Baz +{ + public class Class1 + { + } +} diff --git a/src/Snitch.Tests.Fixtures/FooBar/FooBar.csproj b/src/Snitch.Tests.Fixtures/FooBar/FooBar.csproj new file mode 100644 index 0000000..6b9d0d9 --- /dev/null +++ b/src/Snitch.Tests.Fixtures/FooBar/FooBar.csproj @@ -0,0 +1,8 @@ + + + + Silverlight + v5.0 + + + diff --git a/src/Snitch.Tests/Expectations/FooBar.Default.verified.txt b/src/Snitch.Tests/Expectations/FooBar.Default.verified.txt new file mode 100644 index 0000000..4b61be2 --- /dev/null +++ b/src/Snitch.Tests/Expectations/FooBar.Default.verified.txt @@ -0,0 +1,6 @@ +Analyzing... +Analyzing FooBar.csproj +Analyzing FooBar... + ERROR: Value cannot be null. (Parameter 'folderName') + +Everything looks good! \ No newline at end of file diff --git a/src/Snitch.Tests/Expectations/FooBar.Strict.verified.txt b/src/Snitch.Tests/Expectations/FooBar.Strict.verified.txt new file mode 100644 index 0000000..c0a2d6a --- /dev/null +++ b/src/Snitch.Tests/Expectations/FooBar.Strict.verified.txt @@ -0,0 +1,4 @@ +Analyzing... +Analyzing FooBar.csproj +Analyzing FooBar... + ERROR: Value cannot be null. (Parameter 'folderName') \ No newline at end of file diff --git a/src/Snitch.Tests/ProgramTests.cs b/src/Snitch.Tests/ProgramTests.cs index f264bcd..1e08420 100644 --- a/src/Snitch.Tests/ProgramTests.cs +++ b/src/Snitch.Tests/ProgramTests.cs @@ -1,5 +1,4 @@ using Shouldly; -using Snitch; using System; using System.IO; using System.Threading.Tasks; @@ -9,7 +8,7 @@ using Xunit; using VerifyXunit; -namespace Sntich.Tests +namespace Snitch.Tests { [UsesVerify] public class ProgramTests @@ -19,7 +18,6 @@ public class ProgramTests public async Task Should_Return_Expected_Result_For_Baz_Not_Specifying_Framework() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("Baz/Baz.csproj"); // When @@ -35,7 +33,6 @@ public async Task Should_Return_Expected_Result_For_Baz_Not_Specifying_Framework public async Task Should_Return_Expected_Result_For_Solution_Not_Specifying_Framework() { // Given - var fixture = new Fixture(); var solution = Fixture.GetPath("Snitch.Tests.Fixtures.sln"); // When @@ -51,7 +48,6 @@ public async Task Should_Return_Expected_Result_For_Solution_Not_Specifying_Fram public async Task Should_Return_Expected_Result_For_Baz_Specifying_Framework() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("Baz/Baz.csproj"); // When @@ -67,7 +63,6 @@ public async Task Should_Return_Expected_Result_For_Baz_Specifying_Framework() public async Task Should_Return_Non_Zero_Exit_Code_For_Baz_When_Running_With_Strict() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("Baz/Baz.csproj"); // When @@ -83,7 +78,6 @@ public async Task Should_Return_Non_Zero_Exit_Code_For_Baz_When_Running_With_Str public async Task Should_Return_Expected_Result_For_Baz_When_Excluding_Library() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("Baz/Baz.csproj"); // When @@ -99,7 +93,6 @@ public async Task Should_Return_Expected_Result_For_Baz_When_Excluding_Library() public async Task Should_Return_Expected_Result_For_Baz_When_Skipping_Project() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("Baz/Baz.csproj"); // When @@ -115,7 +108,6 @@ public async Task Should_Return_Expected_Result_For_Baz_When_Skipping_Project() public async Task Should_Return_Expected_Result_For_Baz_When_Skipping_Project_And_NoReleases() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("Baz/Baz.csproj"); // When @@ -131,7 +123,6 @@ public async Task Should_Return_Expected_Result_For_Baz_When_Skipping_Project_An public async Task Should_Return_Non_Zero_Exit_Code_For_Baz_When_Running_With_Strict_And_NoPreRelease() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("Baz/Baz.csproj"); // When @@ -147,7 +138,6 @@ public async Task Should_Return_Non_Zero_Exit_Code_For_Baz_When_Running_With_Str public async Task Should_Return_Non_Zero_Exit_Code_For_Thud_When_Running_With_Strict_And_NoPreRelease() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("Thud/Thud.csproj"); // When @@ -163,7 +153,6 @@ public async Task Should_Return_Non_Zero_Exit_Code_For_Thud_When_Running_With_St public async Task Should_Return_Non_Zero_Exit_Code_For_Thuuud_When_Running_With_Strict_And_NoPreRelease() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("Thuuud/Thuuud.csproj"); // When @@ -179,7 +168,6 @@ public async Task Should_Return_Non_Zero_Exit_Code_For_Thuuud_When_Running_With_ public async Task Should_Return_Zero_Exit_Code_For_Thuuud_When_Running_With_NoPreRelease() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("Thuuud/Thuuud.csproj"); // When @@ -190,21 +178,34 @@ public async Task Should_Return_Zero_Exit_Code_For_Thuuud_When_Running_With_NoPr await Verifier.Verify(output); } - public sealed class Fixture + [Fact] + [Expectation("FooBar", "Default")] + public async Task Should_Print_Error_For_FooBar() { - public static string GetPath(string path) - { - var workingDirectory = Environment.CurrentDirectory; - var solutionDirectory = Path.GetFullPath(Path.Combine(workingDirectory, "../../../../Snitch.Tests.Fixtures")); - return Path.GetFullPath(Path.Combine(solutionDirectory, path)); - } + // Given + var project = Fixture.GetPath("FooBar/FooBar.csproj"); - public static async Task<(int exitCode, string output)> Run(params string[] args) - { - var console = new TestConsole { EmitAnsiSequences = false }; - var exitCode = await Program.Run(args, c => c.ConfigureConsole(console)); - return (exitCode, console.Output.Trim()); - } + // When + var (exitCode, output) = await Fixture.Run(project); + + // Then + exitCode.ShouldBe(0); + await Verifier.Verify(output); + } + + [Fact] + [Expectation("FooBar", "Strict")] + public async Task Should_Return_NonZero_Exit_Code_For_FooBar_When_Running_With_Strict() + { + // Given + var project = Fixture.GetPath("FooBar/FooBar.csproj"); + + // When + var (exitCode, output) = await Fixture.Run(project, "--strict"); + + // Then + exitCode.ShouldBe(-1); + await Verifier.Verify(output); } [Fact] @@ -212,7 +213,6 @@ public static string GetPath(string path) public async Task Should_Return_Expected_Result_For_FSharp_Not_Specifying_Framework() { // Given - var fixture = new Fixture(); var project = Fixture.GetPath("FSharp/FSharp.fsproj"); // When @@ -222,5 +222,22 @@ public async Task Should_Return_Expected_Result_For_FSharp_Not_Specifying_Framew exitCode.ShouldBe(0); await Verifier.Verify(output); } + + private static class Fixture + { + public static string GetPath(string path) + { + var workingDirectory = Environment.CurrentDirectory; + var solutionDirectory = Path.GetFullPath(Path.Combine(workingDirectory, "../../../../Snitch.Tests.Fixtures")); + return Path.GetFullPath(Path.Combine(solutionDirectory, path)); + } + + public static async Task<(int exitCode, string output)> Run(params string[] args) + { + var console = new TestConsole { EmitAnsiSequences = false }; + var exitCode = await Program.Run(args, c => c.ConfigureConsole(console)); + return (exitCode, console.Output.Trim()); + } + } } } diff --git a/src/Snitch.Tests/VerifyConfiguration.cs b/src/Snitch.Tests/VerifyConfiguration.cs index f775c83..7d49787 100644 --- a/src/Snitch.Tests/VerifyConfiguration.cs +++ b/src/Snitch.Tests/VerifyConfiguration.cs @@ -1,7 +1,7 @@ using System.Runtime.CompilerServices; using VerifyTests; -namespace Sntich.Tests +namespace Snitch.Tests { public static class VerifyConfiguration { diff --git a/src/Snitch/Analysis/ProjectFileReporter.cs b/src/Snitch/Analysis/ProjectFileReporter.cs index 5f26653..4170ab0 100644 --- a/src/Snitch/Analysis/ProjectFileReporter.cs +++ b/src/Snitch/Analysis/ProjectFileReporter.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Text.Json; @@ -10,13 +9,16 @@ namespace Snitch.Analysis { internal class ProjectFileReporter { - public ProjectFileReporter() + private readonly IAnsiConsole _console; + + public ProjectFileReporter(IAnsiConsole console) { + _console = console ?? throw new ArgumentNullException(nameof(console)); } - internal void WriteToFile(List analyzerResults, string outputFileName) + internal void WriteToFile(List analyzerResults, string outputFileName, bool noPreRelease) { - var removeResult = + var results = analyzerResults .Where(x => x.CanBeRemoved.Count > 0) .Select(x => new @@ -34,13 +36,22 @@ internal void WriteToFile(List analyzerResults, string ou ReferencedBy = y.Original.Project.Name, ReferencePackageVersion = y.Original.Package.Version, }), + PreRelease = noPreRelease ? null : x.PreReleasePackages.Select(y => new + { + PackageName = y.Name, + Version = y.Version, + }), }); using FileStream createStream = File.Create(outputFileName); - JsonSerializer.Serialize(createStream, removeResult, new JsonSerializerOptions() + JsonSerializer.Serialize(createStream, results, new JsonSerializerOptions() { WriteIndented = true, }); + + _console.WriteLine(); + _console.MarkupLine($"[green]Results written to {outputFileName}![/]"); + _console.WriteLine(); } } } diff --git a/src/Snitch/Analysis/ProjectReporter.cs b/src/Snitch/Analysis/ProjectReporter.cs index ac4eab2..7afd2eb 100644 --- a/src/Snitch/Analysis/ProjectReporter.cs +++ b/src/Snitch/Analysis/ProjectReporter.cs @@ -104,7 +104,7 @@ public void WriteToConsole([NotNull] List results, bool n if (noPreRelease && resultsWithPreReleases.Count > 0) { report.AddEmptyRow(); - report.AddRow($" [yellow]Projects with pre-release package references:[/]"); + report.AddRow(" [yellow]Projects with pre-release package references:[/]"); var packagesByProject = resultsWithPreReleases.SelectMany(x => x.PreReleasePackages, (project, package) => new { Project = project.Project, diff --git a/src/Snitch/Commands/AnalyzeCommand.cs b/src/Snitch/Commands/AnalyzeCommand.cs index 0c37d0e..f6ba670 100644 --- a/src/Snitch/Commands/AnalyzeCommand.cs +++ b/src/Snitch/Commands/AnalyzeCommand.cs @@ -57,7 +57,7 @@ public AnalyzeCommand(IAnsiConsole console) _builder = new ProjectBuilder(console); _analyzer = new ProjectAnalyzer(); _reporter = new ProjectReporter(console); - _fileReporter = new ProjectFileReporter(); + _fileReporter = new ProjectFileReporter(console); } public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings) @@ -137,7 +137,7 @@ public override int Execute([NotNull] CommandContext context, [NotNull] Settings if (settings.OutputFileName != null) { // Write the report to a file. - _fileReporter.WriteToFile(analyzerResults, settings.OutputFileName); + _fileReporter.WriteToFile(analyzerResults, settings.OutputFileName, settings.NoPreRelease); } // Return the correct exit code. diff --git a/src/Snitch/Program.cs b/src/Snitch/Program.cs index 65b2914..098da42 100644 --- a/src/Snitch/Program.cs +++ b/src/Snitch/Program.cs @@ -37,6 +37,7 @@ public static async Task Run(string[] args, Action? configat config.AddExample(new[] { "Solution.sln", "-e", "Foo", "-e", "Bar" }); config.AddExample(new[] { "Solution.sln", "--tfm", "net462" }); config.AddExample(new[] { "Solution.sln", "--tfm", "net462", "--strict" }); + config.AddExample(new[] { "Solution.sln", "--tfm", "net462", "--out", "c:\\temp\\snitch.json" }); config.AddCommand("version"); }); From 0572721efc950e06e0e76d239f520cd8615a331a Mon Sep 17 00:00:00 2001 From: thomaszitzlertietoevry Date: Thu, 27 Apr 2023 12:10:32 +0200 Subject: [PATCH 5/6] tests --- .../Expectations/Baz.Json.verified.json | 14 +++ .../Expectations/Solution.Json.verified.json | 87 +++++++++++++++++++ src/Snitch.Tests/ProgramTests.cs | 50 +++++++++++ src/Snitch/Analysis/ProjectFileReporter.cs | 9 +- 4 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 src/Snitch.Tests/Expectations/Baz.Json.verified.json create mode 100644 src/Snitch.Tests/Expectations/Solution.Json.verified.json diff --git a/src/Snitch.Tests/Expectations/Baz.Json.verified.json b/src/Snitch.Tests/Expectations/Baz.Json.verified.json new file mode 100644 index 0000000..9c2697a --- /dev/null +++ b/src/Snitch.Tests/Expectations/Baz.Json.verified.json @@ -0,0 +1,14 @@ +[ + { + "Project": "Baz", + "CanBeRemoved": [ + { + "PackageName": "Autofac", + "PackageVersion": "4.9.4", + "ReferencedBy": "Foo" + } + ], + "MightBeRemoved": [], + "PreRelease": [] + } +] \ No newline at end of file diff --git a/src/Snitch.Tests/Expectations/Solution.Json.verified.json b/src/Snitch.Tests/Expectations/Solution.Json.verified.json new file mode 100644 index 0000000..3c60f28 --- /dev/null +++ b/src/Snitch.Tests/Expectations/Solution.Json.verified.json @@ -0,0 +1,87 @@ +[ + { + "Project": "Bar", + "CanBeRemoved": [ + { + "PackageName": "Autofac", + "PackageVersion": "4.9.4", + "ReferencedBy": "Foo" + } + ], + "MightBeRemoved": [], + "PreRelease": [] + }, + { + "Project": "Baz", + "CanBeRemoved": [ + { + "PackageName": "Autofac", + "PackageVersion": "4.9.4", + "ReferencedBy": "Foo" + } + ], + "MightBeRemoved": [], + "PreRelease": [] + }, + { + "Project": "Qux", + "CanBeRemoved": [], + "MightBeRemoved": [ + { + "PackageName": "Autofac", + "PackageVersion": "4.9.3", + "ReferencedBy": "Foo", + "ReferencePackageVersion": "4.9.4" + } + ], + "PreRelease": [] + }, + { + "Project": "Zap", + "CanBeRemoved": [], + "MightBeRemoved": [ + { + "PackageName": "Newtonsoft.Json", + "PackageVersion": "12.0.3", + "ReferencedBy": "Foo", + "ReferencePackageVersion": "12.0.1" + }, + { + "PackageName": "Autofac", + "PackageVersion": "4.9.3", + "ReferencedBy": "Foo", + "ReferencePackageVersion": "4.9.4" + } + ], + "PreRelease": [] + }, + { + "Project": "Thud", + "CanBeRemoved": [], + "MightBeRemoved": [], + "PreRelease": [ + { + "PackageName": "Newtonsoft.Json", + "PackageVersion": "13.0.2-beta2" + } + ] + }, + { + "Project": "Thuuud", + "CanBeRemoved": [], + "MightBeRemoved": [ + { + "PackageName": "Newtonsoft.Json", + "PackageVersion": "13.0.2-beta2", + "ReferencedBy": "Foo", + "ReferencePackageVersion": "12.0.1" + } + ], + "PreRelease": [ + { + "PackageName": "Newtonsoft.Json", + "PackageVersion": "13.0.2-beta2" + } + ] + } +] \ No newline at end of file diff --git a/src/Snitch.Tests/ProgramTests.cs b/src/Snitch.Tests/ProgramTests.cs index 1e08420..117987f 100644 --- a/src/Snitch.Tests/ProgramTests.cs +++ b/src/Snitch.Tests/ProgramTests.cs @@ -28,6 +28,22 @@ public async Task Should_Return_Expected_Result_For_Baz_Not_Specifying_Framework await Verifier.Verify(output); } + [Fact] + [Expectation("Baz", "Json")] + public async Task Should_Return_Expected_Json_For_Baz_Not_Specifying_Framework() + { + // Given + var project = Fixture.GetPath("Baz/Baz.csproj"); + + // When + using var tempFileScope = new TempFileScope(); + var (exitCode, _) = await Fixture.Run(project, "--out", tempFileScope.FileName); + + // Then + exitCode.ShouldBe(0); + await Verifier.VerifyFile(tempFileScope.FileName); + } + [Fact] [Expectation("Solution", "Default")] public async Task Should_Return_Expected_Result_For_Solution_Not_Specifying_Framework() @@ -43,6 +59,22 @@ public async Task Should_Return_Expected_Result_For_Solution_Not_Specifying_Fram await Verifier.Verify(output); } + [Fact] + [Expectation("Solution", "Json")] + public async Task Should_Return_Expected_Json_For_Solution_Not_Specifying_Framework() + { + // Given + var solution = Fixture.GetPath("Snitch.Tests.Fixtures.sln"); + + // When + using var tempFileScope = new TempFileScope(); + var (exitCode, _) = await Fixture.Run(solution, "--out", tempFileScope.FileName); + + // Then + exitCode.ShouldBe(0); + await Verifier.VerifyFile(tempFileScope.FileName); + } + [Fact] [Expectation("Baz", "netstandard2.0")] public async Task Should_Return_Expected_Result_For_Baz_Specifying_Framework() @@ -239,5 +271,23 @@ public static string GetPath(string path) return (exitCode, console.Output.Trim()); } } + + private sealed class TempFileScope : IDisposable + { + public TempFileScope() + { + this.FileName = Path.GetTempFileName() + ".json"; + } + + public string FileName { get; } + + public void Dispose() + { + if (File.Exists(this.FileName)) + { + File.Delete(this.FileName); + } + } + } } } diff --git a/src/Snitch/Analysis/ProjectFileReporter.cs b/src/Snitch/Analysis/ProjectFileReporter.cs index 4170ab0..6a51108 100644 --- a/src/Snitch/Analysis/ProjectFileReporter.cs +++ b/src/Snitch/Analysis/ProjectFileReporter.cs @@ -20,26 +20,27 @@ internal void WriteToFile(List analyzerResults, string ou { var results = analyzerResults - .Where(x => x.CanBeRemoved.Count > 0) + .Where(x => x.CanBeRemoved.Count > 0 || x.MightBeRemoved.Count > 0 || x.HasPreReleases) .Select(x => new { x.Project, CanBeRemoved = x.CanBeRemoved.Select(y => new { PackageName = y.Package.Name, + PackageVersion = y.Package.Version?.OriginalVersion, ReferencedBy = y.Original.Project.Name, }), MightBeRemoved = x.MightBeRemoved.Select(y => new { PackageName = y.Package.Name, - PackageVersion = y.Package.Version, + PackageVersion = y.Package.Version?.OriginalVersion, ReferencedBy = y.Original.Project.Name, - ReferencePackageVersion = y.Original.Package.Version, + ReferencePackageVersion = y.Original.Package.Version?.OriginalVersion, }), PreRelease = noPreRelease ? null : x.PreReleasePackages.Select(y => new { PackageName = y.Name, - Version = y.Version, + PackageVersion = y.Version?.OriginalVersion, }), }); From 41be11d45d60d8d6a7811d95bb2765ce1722c370 Mon Sep 17 00:00:00 2001 From: thomaszitzlertietoevry Date: Thu, 27 Apr 2023 12:12:51 +0200 Subject: [PATCH 6/6] Update global.json --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 336164b..6e4fd73 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "projects": [ "src" ], "sdk": { - "version": "6.0.400", + "version": "6.0.100", "rollForward": "latestPatch" } }