From d189edcc75397cbb3967d071bc63d0108305986d Mon Sep 17 00:00:00 2001 From: Roman Marusyk Date: Fri, 8 Nov 2024 01:10:29 +0200 Subject: [PATCH 1/2] Add DotNetSlnRemove alias for dotnet sln remove command --- .../Sln/Remove/DotNetSlnRemoverFixture.cs | 23 +++ .../Sln/Remove/DotNetSlnRemoverTests.cs | 170 ++++++++++++++++++ src/Cake.Common/Tools/DotNet/DotNetAliases.cs | 74 ++++++++ .../Sln/Remove/DotNetSlnRemoveSettings.cs | 13 ++ .../DotNet/Sln/Remove/DotNetSlnRemover.cs | 80 +++++++++ .../Tools/DotNet/DotNetAliases.cake | 14 ++ 6 files changed, 374 insertions(+) create mode 100644 src/Cake.Common.Tests/Fixtures/Tools/DotNet/Sln/Remove/DotNetSlnRemoverFixture.cs create mode 100644 src/Cake.Common.Tests/Unit/Tools/DotNet/Sln/Remove/DotNetSlnRemoverTests.cs create mode 100644 src/Cake.Common/Tools/DotNet/Sln/Remove/DotNetSlnRemoveSettings.cs create mode 100644 src/Cake.Common/Tools/DotNet/Sln/Remove/DotNetSlnRemover.cs diff --git a/src/Cake.Common.Tests/Fixtures/Tools/DotNet/Sln/Remove/DotNetSlnRemoverFixture.cs b/src/Cake.Common.Tests/Fixtures/Tools/DotNet/Sln/Remove/DotNetSlnRemoverFixture.cs new file mode 100644 index 0000000000..6a0473f016 --- /dev/null +++ b/src/Cake.Common.Tests/Fixtures/Tools/DotNet/Sln/Remove/DotNetSlnRemoverFixture.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Cake.Common.Tools.DotNet.Sln.Remove; +using Cake.Core.IO; + +namespace Cake.Common.Tests.Fixtures.Tools.DotNet.Sln.Remove +{ + internal sealed class DotNetSlnRemoverFixture : DotNetFixture + { + public FilePath Solution { get; set; } + + public IEnumerable ProjectPath { get; set; } + + protected override void RunTool() + { + var tool = new DotNetSlnRemover(FileSystem, Environment, ProcessRunner, Tools); + tool.Remove(Solution, ProjectPath, Settings); + } + } +} diff --git a/src/Cake.Common.Tests/Unit/Tools/DotNet/Sln/Remove/DotNetSlnRemoverTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotNet/Sln/Remove/DotNetSlnRemoverTests.cs new file mode 100644 index 0000000000..408afcae04 --- /dev/null +++ b/src/Cake.Common.Tests/Unit/Tools/DotNet/Sln/Remove/DotNetSlnRemoverTests.cs @@ -0,0 +1,170 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Cake.Common.Tests.Fixtures.Tools.DotNet.Sln.Remove; +using Cake.Common.Tools.DotNet; +using Cake.Core.IO; +using Cake.Testing; +using Xunit; + +namespace Cake.Common.Tests.Unit.Tools.DotNet.Sln.Remove +{ + public sealed class DotNetSlnRemoverTests + { + public sealed class TheAddMethod + { + [Fact] + public void Should_Throw_If_Process_Was_Not_Started() + { + // Given + var fixture = new DotNetSlnRemoverFixture(); + fixture.ProjectPath = new[] { (FilePath)"./lib1.csproj" }; + fixture.GivenProcessCannotStart(); + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + AssertEx.IsCakeException(result, ".NET CLI: Process was not started."); + } + + [Fact] + public void Should_Throw_If_Process_Has_A_Non_Zero_Exit_Code() + { + // Given + var fixture = new DotNetSlnRemoverFixture(); + fixture.ProjectPath = new[] { (FilePath)"./lib1.csproj" }; + fixture.GivenProcessExitsWithCode(1); + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + AssertEx.IsCakeException(result, ".NET CLI: Process returned an error (exit code 1)."); + } + + [Fact] + public void Should_Throw_If_ProjectPath_Is_Null() + { + // Given + var fixture = new DotNetSlnRemoverFixture(); + fixture.ProjectPath = null; + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + AssertEx.IsArgumentNullException(result, "projectPath"); + } + + [Fact] + public void Should_Throw_If_ProjectPath_Is_Empty() + { + // Given + var fixture = new DotNetSlnRemoverFixture(); + fixture.ProjectPath = new FilePath[] { }; + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + AssertEx.IsArgumentNullException(result, "projectPath"); + } + + [Fact] + public void Should_Throw_If_Settings_Are_Null() + { + // Given + var fixture = new DotNetSlnRemoverFixture(); + fixture.ProjectPath = new[] { (FilePath)"./lib1.csproj" }; + fixture.Settings = null; + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + AssertEx.IsArgumentNullException(result, "settings"); + } + + [Fact] + public void Should_Add_Solution_Argument() + { + // Given + var fixture = new DotNetSlnRemoverFixture(); + fixture.Solution = (FilePath)"test.sln"; + fixture.ProjectPath = new[] { (FilePath)"./lib1.csproj" }; + + // When + var result = fixture.Run(); + + // Then + Assert.NotNull(result); + Assert.Equal("sln \"/Working/test.sln\" remove \"/Working/lib1.csproj\"", result.Args); + } + + [Fact] + public void Should_Not_Add_Solution_Argument() + { + // Given + var fixture = new DotNetSlnRemoverFixture(); + fixture.Solution = null; + fixture.ProjectPath = new[] { (FilePath)"./lib1.csproj" }; + + // When + var result = fixture.Run(); + + // Then + Assert.NotNull(result); + Assert.Equal("sln remove \"/Working/lib1.csproj\"", result.Args); + } + + [Fact] + public void Should_Add_ProjectPath_Argument() + { + // Given + var fixture = new DotNetSlnRemoverFixture(); + fixture.ProjectPath = new[] { (FilePath)"./lib1.csproj" }; + + // When + var result = fixture.Run(); + + // Then + Assert.NotNull(result); + Assert.Equal("sln remove \"/Working/lib1.csproj\"", result.Args); + } + + [Fact] + public void Should_Add_All_ProjectPath() + { + // Given + var fixture = new DotNetSlnRemoverFixture(); + fixture.ProjectPath = new[] { (FilePath)"./lib1.csproj", "./lib2.csproj", "./lib3.csproj" }; + + // When + var result = fixture.Run(); + + // Then + Assert.NotNull(result); + Assert.Equal("sln remove \"/Working/lib1.csproj\" \"/Working/lib2.csproj\" \"/Working/lib3.csproj\"", result.Args); + } + + [Fact] + public void Should_Add_Additional_Arguments() + { + // Given + var fixture = new DotNetSlnRemoverFixture(); + fixture.Solution = (FilePath)"test.sln"; + fixture.ProjectPath = new[] { (FilePath)"./lib1.csproj" }; + fixture.Settings.Verbosity = DotNetVerbosity.Detailed; + + // When + var result = fixture.Run(); + + // Then + Assert.NotNull(result); + Assert.Equal("sln \"/Working/test.sln\" remove \"/Working/lib1.csproj\" --verbosity detailed", result.Args); + } + } + } +} diff --git a/src/Cake.Common/Tools/DotNet/DotNetAliases.cs b/src/Cake.Common/Tools/DotNet/DotNetAliases.cs index 595e626d37..2d11227e97 100644 --- a/src/Cake.Common/Tools/DotNet/DotNetAliases.cs +++ b/src/Cake.Common/Tools/DotNet/DotNetAliases.cs @@ -26,6 +26,7 @@ using Cake.Common.Tools.DotNet.Restore; using Cake.Common.Tools.DotNet.Run; using Cake.Common.Tools.DotNet.SDKCheck; +using Cake.Common.Tools.DotNet.Sln.Remove; using Cake.Common.Tools.DotNet.Test; using Cake.Common.Tools.DotNet.Tool; using Cake.Common.Tools.DotNet.VSTest; @@ -2883,5 +2884,78 @@ public static DotNetPackageList DotNetListPackage(this ICakeContext context, str var lister = new DotNetPackageLister(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); return lister.List(project, settings); } + + /// + /// Removes a project or multiple projects from the solution file. + /// + /// The context. + /// The path to the project or projects to remove from the solution. + /// + /// + /// DotNetSlnRemove(GetFiles("./*.csproj")); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Sln")] + [CakeNamespaceImport("Cake.Common.Tools.DotNet.Sln.Remove")] + public static void DotNetSlnRemove(this ICakeContext context, IEnumerable projectPath) + { + context.DotNetSlnRemove(null, projectPath); + } + + /// + /// Removes a project or multiple projects from the solution file. + /// + /// The context. + /// The solution file to use. If it is unspecified, the command searches the current directory for one and fails if there are multiple solution files. + /// The path to the project or projects to remove from the solution. + /// + /// + /// DotNetSlnRemove("app.sln", GetFiles("./*.csproj")); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Sln")] + [CakeNamespaceImport("Cake.Common.Tools.DotNet.Sln.Remove")] + public static void DotNetSlnRemove(this ICakeContext context, FilePath solution, IEnumerable projectPath) + { + context.DotNetSlnRemove(solution, projectPath, null); + } + + /// + /// Removes a project or multiple projects from the solution file. + /// + /// The context. + /// The solution file to use. If it is unspecified, the command searches the current directory for one and fails if there are multiple solution files. + /// The path to the project or projects to remove from the solution. + /// The settings. + /// + /// + /// var settings = new DotNetSlnRemoveSettings + /// { + /// Verbosity = DotNetVerbosity.Diagnostic + /// }; + /// + /// DotNetSlnRemove("app.sln", GetFiles("./*.csproj"), settings); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Sln")] + [CakeNamespaceImport("Cake.Common.Tools.DotNet.Sln.Remove")] + public static void Remove(this ICakeContext context, FilePath solution, IEnumerable projectPath, DotNetSlnRemoveSettings settings) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (settings is null) + { + settings = new DotNetSlnRemoveSettings(); + } + + var remover = new DotNetSlnRemover(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); + remover.Remove(solution, projectPath, settings); + } } } diff --git a/src/Cake.Common/Tools/DotNet/Sln/Remove/DotNetSlnRemoveSettings.cs b/src/Cake.Common/Tools/DotNet/Sln/Remove/DotNetSlnRemoveSettings.cs new file mode 100644 index 0000000000..0ab6776ee1 --- /dev/null +++ b/src/Cake.Common/Tools/DotNet/Sln/Remove/DotNetSlnRemoveSettings.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Cake.Common.Tools.DotNet.Sln.Remove +{ + /// + /// Contains settings used by . + /// + public sealed class DotNetSlnRemoveSettings : DotNetSettings + { + } +} diff --git a/src/Cake.Common/Tools/DotNet/Sln/Remove/DotNetSlnRemover.cs b/src/Cake.Common/Tools/DotNet/Sln/Remove/DotNetSlnRemover.cs new file mode 100644 index 0000000000..7ab1da3fb3 --- /dev/null +++ b/src/Cake.Common/Tools/DotNet/Sln/Remove/DotNetSlnRemover.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Cake.Core; +using Cake.Core.IO; +using Cake.Core.Tooling; + +namespace Cake.Common.Tools.DotNet.Sln.Remove +{ + /// + /// .NET project remover. + /// + public sealed class DotNetSlnRemover : DotNetTool + { + private readonly ICakeEnvironment _environment; + + /// + /// Initializes a new instance of the class. + /// + /// The file system. + /// The environment. + /// The process runner. + /// The tool locator. + public DotNetSlnRemover( + IFileSystem fileSystem, + ICakeEnvironment environment, + IProcessRunner processRunner, + IToolLocator tools) : base(fileSystem, environment, processRunner, tools) + { + _environment = environment; + } + + /// + /// Removes a project or multiple projects from the solution file. + /// + /// The solution file to use. If it is unspecified, the command searches the current directory for one and fails if there are multiple solution files. + /// The path to the project or projects to remove from the solution. + /// The settings. + public void Remove(FilePath solution, IEnumerable projectPath, DotNetSlnRemoveSettings settings) + { + if (projectPath == null || !projectPath.Any()) + { + throw new ArgumentNullException(nameof(projectPath)); + } + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + RunCommand(settings, GetArguments(solution, projectPath, settings)); + } + + private ProcessArgumentBuilder GetArguments(FilePath solution, IEnumerable projectPath, DotNetSlnRemoveSettings settings) + { + var builder = CreateArgumentBuilder(settings); + + builder.Append("sln"); + + // Solution path + if (solution != null) + { + builder.AppendQuoted(solution.MakeAbsolute(_environment).FullPath); + } + + builder.Append("remove"); + + // Project path + foreach (var project in projectPath) + { + builder.AppendQuoted(project.MakeAbsolute(_environment).FullPath); + } + + return builder; + } + } +} diff --git a/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake b/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake index 0fd9f3a0ce..2793158709 100644 --- a/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake +++ b/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake @@ -437,6 +437,19 @@ Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetListPackage") Assert.Contains(result.Projects, item => item.Path == project); }); +Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetSlnRemove") + .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.Setup") + .Does(() => +{ + // Given + var path = Paths.Temp.Combine("./Cake.Common/Tools/DotNet"); + var solution = path.CombineWithFilePath("hwapp.sln"); + var project = path.CombineWithFilePath("hwapp/hwapp.csproj"); + + // When + DotNetSlnRemove(solution.FullPath, new[] { (FilePath)project.FullPath}); +}); + Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetBuildServerShutdown") .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.DotNetRestore") .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.DotNetBuild") @@ -465,6 +478,7 @@ Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetBuildServerShutdown") .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.DotNetAddReference") .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.DotNetRemoveReference") .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.DotNetListReference") + .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.DotNetSlnRemove") .Does(() => { // When From f8667df234159d02a2044860a56f34bada000839 Mon Sep 17 00:00:00 2001 From: Roman Marusyk Date: Fri, 8 Nov 2024 11:24:18 +0200 Subject: [PATCH 2/2] Fix AliasName --- src/Cake.Common/Tools/DotNet/DotNetAliases.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake.Common/Tools/DotNet/DotNetAliases.cs b/src/Cake.Common/Tools/DotNet/DotNetAliases.cs index 2d11227e97..4241d09172 100644 --- a/src/Cake.Common/Tools/DotNet/DotNetAliases.cs +++ b/src/Cake.Common/Tools/DotNet/DotNetAliases.cs @@ -2942,7 +2942,7 @@ public static void DotNetSlnRemove(this ICakeContext context, FilePath solution, [CakeMethodAlias] [CakeAliasCategory("Sln")] [CakeNamespaceImport("Cake.Common.Tools.DotNet.Sln.Remove")] - public static void Remove(this ICakeContext context, FilePath solution, IEnumerable projectPath, DotNetSlnRemoveSettings settings) + public static void DotNetSlnRemove(this ICakeContext context, FilePath solution, IEnumerable projectPath, DotNetSlnRemoveSettings settings) { if (context is null) {