From b2fb8139a191280b61266d573e522954504c2935 Mon Sep 17 00:00:00 2001 From: "C. Augusto Proiete" Date: Sat, 19 Feb 2022 20:31:23 -0400 Subject: [PATCH 01/27] (build) Updated Cake Tool to version 2.1.0 --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 31e896e989..b6cadaf67b 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "cake.tool": { - "version": "2.0.0", + "version": "2.1.0", "commands": [ "dotnet-cake" ] From abad39f4184d3407f13d8cd1c079524c03a2bd79 Mon Sep 17 00:00:00 2001 From: "C. Augusto Proiete" Date: Sat, 19 Feb 2022 22:59:56 -0400 Subject: [PATCH 02/27] (#3819) Update Git Release Manager Comment template to remove Cake NuGet package and Chocolatey portable --- GitReleaseManager.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/GitReleaseManager.yaml b/GitReleaseManager.yaml index 6b71b019b1..76c66f5ad9 100644 --- a/GitReleaseManager.yaml +++ b/GitReleaseManager.yaml @@ -22,8 +22,6 @@ close: The release is available on: - [GitHub Release](https://github.com/{owner}/{repository}/releases/tag/{milestone}) - - [NuGet Package](https://www.nuget.org/packages/Cake/{milestone}) - - [Chocolatey Package](https://chocolatey.org/packages/cake.portable/{milestone}) - - [.Net Global Tool](https://www.nuget.org/packages/Cake.Tool/{milestone}) - + - [.NET Tool](https://www.nuget.org/packages/Cake.Tool/{milestone}) + Your **[GitReleaseManager](https://github.com/GitTools/GitReleaseManager)** bot :package::rocket: \ No newline at end of file From 2e207170854bb273aa297c77903d6f3139271f04 Mon Sep 17 00:00:00 2001 From: "Wenzel, Toni" Date: Mon, 21 Feb 2022 17:02:53 +0100 Subject: [PATCH 03/27] Add PostAction property --- .../DotNetCore/Tool/DotNetCoreToolTests.cs | 21 +++++++++++++++++++ .../Tools/DotNet/DotNetSettings.cs | 7 +++++++ src/Cake.Common/Tools/DotNet/DotNetTool.cs | 6 +++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotNetCore/Tool/DotNetCoreToolTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotNetCore/Tool/DotNetCoreToolTests.cs index d4126dcdc1..a9ef001f3b 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotNetCore/Tool/DotNetCoreToolTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotNetCore/Tool/DotNetCoreToolTests.cs @@ -96,6 +96,27 @@ public void Should_Throw_If_Process_Has_A_Non_Zero_Exit_Code() // Then AssertEx.IsCakeException(result, ".NET CLI: Process returned an error (exit code 1)."); } + + [Fact] + public void Executes_PostAction() + { + var wasExecuted = false; + + // Given + var fixture = new DotNetCoreToolFixture(); + fixture.ProjectPath = "./tests/Cake.Common.Tests/"; + fixture.Command = "xunit"; + fixture.Settings = new DotNetCoreToolSettings(); + fixture.Settings.PostAction = (p) => wasExecuted = true; + + fixture.GivenProcessExitsWithCode(0); + + // When + _ = fixture.Run(); + + // Then + Assert.True(wasExecuted); + } } } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotNet/DotNetSettings.cs b/src/Cake.Common/Tools/DotNet/DotNetSettings.cs index 7ad53356f2..142c527aae 100644 --- a/src/Cake.Common/Tools/DotNet/DotNetSettings.cs +++ b/src/Cake.Common/Tools/DotNet/DotNetSettings.cs @@ -2,6 +2,8 @@ // 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 Cake.Core.IO; using Cake.Core.Tooling; namespace Cake.Common.Tools.DotNet @@ -25,5 +27,10 @@ public abstract class DotNetSettings : ToolSettings /// Gets or sets the dotnet roll forward policy. /// public DotNetRollForward? RollForward { get; set; } + + /// + /// Gets or sets a delegate which is executed after the process was started. + /// + public Action PostAction { get; set; } } } diff --git a/src/Cake.Common/Tools/DotNet/DotNetTool.cs b/src/Cake.Common/Tools/DotNet/DotNetTool.cs index 5d473e01f3..53aad48d5a 100644 --- a/src/Cake.Common/Tools/DotNet/DotNetTool.cs +++ b/src/Cake.Common/Tools/DotNet/DotNetTool.cs @@ -61,7 +61,7 @@ protected void RunCommand(TSettings settings, ProcessArgumentBuilder arguments) // add arguments common to all commands last AppendCommonArguments(arguments, settings); - Run(settings, arguments, null, null); + Run(settings, arguments, null, settings.PostAction); } /// @@ -75,7 +75,7 @@ protected void RunCommand(TSettings settings, ProcessArgumentBuilder arguments, // add arguments common to all commands last AppendCommonArguments(arguments, settings); - Run(settings, arguments, processSettings, null); + Run(settings, arguments, processSettings, settings.PostAction); } /// @@ -90,7 +90,7 @@ protected void RunCommand(TSettings settings, ProcessArgumentBuilder arguments, // add arguments common to all commands last AppendCommonArguments(arguments, settings); - Run(settings, arguments, processSettings, postAction); + Run(settings, arguments, processSettings, postAction ?? settings.PostAction); } /// From c94fa67ace91405222e703bcdbaf48be0f235990 Mon Sep 17 00:00:00 2001 From: "Wenzel, Toni" Date: Tue, 22 Feb 2022 08:38:30 +0100 Subject: [PATCH 04/27] Move to ToolSettings for general purpose --- src/Cake.Common/Tools/DotNet/DotNetSettings.cs | 5 ----- src/Cake.Common/Tools/DotNet/DotNetTool.cs | 6 +++--- src/Cake.Core/Tooling/Tool.cs | 5 ++++- src/Cake.Core/Tooling/ToolSettings.cs | 10 ++++++++++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Cake.Common/Tools/DotNet/DotNetSettings.cs b/src/Cake.Common/Tools/DotNet/DotNetSettings.cs index 142c527aae..c54d9a6051 100644 --- a/src/Cake.Common/Tools/DotNet/DotNetSettings.cs +++ b/src/Cake.Common/Tools/DotNet/DotNetSettings.cs @@ -27,10 +27,5 @@ public abstract class DotNetSettings : ToolSettings /// Gets or sets the dotnet roll forward policy. /// public DotNetRollForward? RollForward { get; set; } - - /// - /// Gets or sets a delegate which is executed after the process was started. - /// - public Action PostAction { get; set; } } } diff --git a/src/Cake.Common/Tools/DotNet/DotNetTool.cs b/src/Cake.Common/Tools/DotNet/DotNetTool.cs index 53aad48d5a..5d473e01f3 100644 --- a/src/Cake.Common/Tools/DotNet/DotNetTool.cs +++ b/src/Cake.Common/Tools/DotNet/DotNetTool.cs @@ -61,7 +61,7 @@ protected void RunCommand(TSettings settings, ProcessArgumentBuilder arguments) // add arguments common to all commands last AppendCommonArguments(arguments, settings); - Run(settings, arguments, null, settings.PostAction); + Run(settings, arguments, null, null); } /// @@ -75,7 +75,7 @@ protected void RunCommand(TSettings settings, ProcessArgumentBuilder arguments, // add arguments common to all commands last AppendCommonArguments(arguments, settings); - Run(settings, arguments, processSettings, settings.PostAction); + Run(settings, arguments, processSettings, null); } /// @@ -90,7 +90,7 @@ protected void RunCommand(TSettings settings, ProcessArgumentBuilder arguments, // add arguments common to all commands last AppendCommonArguments(arguments, settings); - Run(settings, arguments, processSettings, postAction ?? settings.PostAction); + Run(settings, arguments, processSettings, postAction); } /// diff --git a/src/Cake.Core/Tooling/Tool.cs b/src/Cake.Core/Tooling/Tool.cs index d41ad7101c..1b26013600 100644 --- a/src/Cake.Core/Tooling/Tool.cs +++ b/src/Cake.Core/Tooling/Tool.cs @@ -95,7 +95,7 @@ protected void Run( } // Post action specified? - postAction?.Invoke(process); + (postAction ?? settings.PostAction)?.Invoke(process); var exitCode = process.GetExitCode(); if (!settings.HandleExitCode?.Invoke(exitCode) ?? true) @@ -190,6 +190,9 @@ protected IProcess RunProcess( // Want to opt out of using a working directory? info.NoWorkingDirectory = settings.NoWorkingDirectory; + // Configure process settings + settings.SetupProcessSettings?.Invoke(info); + // Run the process. var process = _processRunner.Start(toolPath, info); if (process == null) diff --git a/src/Cake.Core/Tooling/ToolSettings.cs b/src/Cake.Core/Tooling/ToolSettings.cs index 49158610d3..84b30f4f68 100644 --- a/src/Cake.Core/Tooling/ToolSettings.cs +++ b/src/Cake.Core/Tooling/ToolSettings.cs @@ -109,5 +109,15 @@ public class ToolSettings /// /// public Func HandleExitCode { get; set; } + + /// + /// Gets or sets a delegate which is executed after the process was started. + /// + public Action PostAction { get; set; } + + /// + /// Gets or sets a delegate to configure the process settings. + /// + public Action SetupProcessSettings { get; set; } } } \ No newline at end of file From 0ffc3cb03646b7367499af5aef9a9a272b17e128 Mon Sep 17 00:00:00 2001 From: "Wenzel, Toni" Date: Fri, 25 Feb 2022 08:10:28 +0100 Subject: [PATCH 05/27] Move tests to ToolsTests --- .../DotNetCore/Tool/DotNetCoreToolTests.cs | 21 ----------- .../Tools/DotNet/DotNetSettings.cs | 2 -- src/Cake.Core.Tests/Unit/Tooling/ToolTests.cs | 36 +++++++++++++++++++ 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/Cake.Common.Tests/Unit/Tools/DotNetCore/Tool/DotNetCoreToolTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotNetCore/Tool/DotNetCoreToolTests.cs index a9ef001f3b..d4126dcdc1 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotNetCore/Tool/DotNetCoreToolTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotNetCore/Tool/DotNetCoreToolTests.cs @@ -96,27 +96,6 @@ public void Should_Throw_If_Process_Has_A_Non_Zero_Exit_Code() // Then AssertEx.IsCakeException(result, ".NET CLI: Process returned an error (exit code 1)."); } - - [Fact] - public void Executes_PostAction() - { - var wasExecuted = false; - - // Given - var fixture = new DotNetCoreToolFixture(); - fixture.ProjectPath = "./tests/Cake.Common.Tests/"; - fixture.Command = "xunit"; - fixture.Settings = new DotNetCoreToolSettings(); - fixture.Settings.PostAction = (p) => wasExecuted = true; - - fixture.GivenProcessExitsWithCode(0); - - // When - _ = fixture.Run(); - - // Then - Assert.True(wasExecuted); - } } } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/DotNet/DotNetSettings.cs b/src/Cake.Common/Tools/DotNet/DotNetSettings.cs index c54d9a6051..7ad53356f2 100644 --- a/src/Cake.Common/Tools/DotNet/DotNetSettings.cs +++ b/src/Cake.Common/Tools/DotNet/DotNetSettings.cs @@ -2,8 +2,6 @@ // 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 Cake.Core.IO; using Cake.Core.Tooling; namespace Cake.Common.Tools.DotNet diff --git a/src/Cake.Core.Tests/Unit/Tooling/ToolTests.cs b/src/Cake.Core.Tests/Unit/Tooling/ToolTests.cs index 072f9b3488..efc111c990 100644 --- a/src/Cake.Core.Tests/Unit/Tooling/ToolTests.cs +++ b/src/Cake.Core.Tests/Unit/Tooling/ToolTests.cs @@ -159,6 +159,42 @@ public void Should_Not_Throw_On_Invalid_ExitCode_When_HandleExitCode_Returns_Tru // Then Assert.IsNotType(result); } + + [Fact] + public void Executes_PostAction() + { + var wasExecuted = false; + + // Given + var fixture = new DummyToolFixture(); + fixture.Settings.PostAction = (p) => wasExecuted = true; + + fixture.GivenProcessExitsWithCode(0); + + // When + _ = fixture.Run(); + + // Then + Assert.True(wasExecuted); + } + + [Fact] + public void Executes_SetupProcessSettings() + { + var wasExecuted = false; + + // Given + var fixture = new DummyToolFixture(); + fixture.Settings.SetupProcessSettings = (p) => wasExecuted = true; + + fixture.GivenProcessExitsWithCode(0); + + // When + _ = fixture.Run(); + + // Then + Assert.True(wasExecuted); + } } } } \ No newline at end of file From 6761bb7b3a6ba308043796a0661c1f0df907529b Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Tue, 15 Mar 2022 18:37:18 +0100 Subject: [PATCH 06/27] (GH-3840) Update .NET SDK to 6.0.201 * fixes #3840 --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index c0d1b00472..1beb945b15 100644 --- a/global.json +++ b/global.json @@ -3,7 +3,7 @@ "src" ], "sdk": { - "version": "6.0.102", + "version": "6.0.201", "rollForward": "latestFeature" } } \ No newline at end of file From 5a7956277b6a708a11d9f0eaecdcd20191656a7e Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Sat, 19 Mar 2022 00:10:13 +0100 Subject: [PATCH 07/27] (GH-3843) Update NuGet.* to 6.1.0 * fixes #3843 --- src/Cake.NuGet/Cake.NuGet.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Cake.NuGet/Cake.NuGet.csproj b/src/Cake.NuGet/Cake.NuGet.csproj index 840767db90..4af1876bd3 100644 --- a/src/Cake.NuGet/Cake.NuGet.csproj +++ b/src/Cake.NuGet/Cake.NuGet.csproj @@ -18,12 +18,12 @@ - - - - - - + + + + + + From 97ddd4fbfbd7ad91515fd02a4e6542d34220e0aa Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Sat, 19 Mar 2022 00:11:28 +0100 Subject: [PATCH 08/27] (GH-3844) Update Microsoft.NETCore.Platforms to 6.0.2 * fixes #3844 --- src/Cake.NuGet/Cake.NuGet.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake.NuGet/Cake.NuGet.csproj b/src/Cake.NuGet/Cake.NuGet.csproj index 4af1876bd3..735f98791b 100644 --- a/src/Cake.NuGet/Cake.NuGet.csproj +++ b/src/Cake.NuGet/Cake.NuGet.csproj @@ -29,7 +29,7 @@ - + All From efff8bea2f81f64d3fc4b7773437cdad4592649f Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Sun, 20 Mar 2022 17:22:03 +0100 Subject: [PATCH 09/27] (GH-3846) Update Microsoft.CodeAnalysis.CSharp.Scripting to 4.1.0 * fixes #3846 --- src/Cake/Cake.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake/Cake.csproj b/src/Cake/Cake.csproj index 6525339ef5..838ef36a55 100644 --- a/src/Cake/Cake.csproj +++ b/src/Cake/Cake.csproj @@ -26,7 +26,7 @@ - + From 9898dd377775c10dcb538807d93ad979f555443a Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Sun, 20 Mar 2022 23:34:41 +0100 Subject: [PATCH 10/27] (GH-3848) Microsoft.NET.Test.Sdk to 17.1.0 * fixes #3848 --- src/Cake.Common.Tests/Cake.Common.Tests.csproj | 2 +- src/Cake.Core.Tests/Cake.Core.Tests.csproj | 2 +- .../Cake.DotNetTool.Module.Tests.csproj | 2 +- src/Cake.Frosting.Tests/Cake.Frosting.Tests.csproj | 4 ++-- src/Cake.NuGet.Tests/Cake.NuGet.Tests.csproj | 2 +- src/Cake.Tests/Cake.Tests.csproj | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Cake.Common.Tests/Cake.Common.Tests.csproj b/src/Cake.Common.Tests/Cake.Common.Tests.csproj index edde2300af..60e0e0a249 100644 --- a/src/Cake.Common.Tests/Cake.Common.Tests.csproj +++ b/src/Cake.Common.Tests/Cake.Common.Tests.csproj @@ -16,7 +16,7 @@ - + all diff --git a/src/Cake.Core.Tests/Cake.Core.Tests.csproj b/src/Cake.Core.Tests/Cake.Core.Tests.csproj index 8cf5514fbf..c5977c536c 100644 --- a/src/Cake.Core.Tests/Cake.Core.Tests.csproj +++ b/src/Cake.Core.Tests/Cake.Core.Tests.csproj @@ -14,7 +14,7 @@ - + all diff --git a/src/Cake.DotNetTool.Module.Tests/Cake.DotNetTool.Module.Tests.csproj b/src/Cake.DotNetTool.Module.Tests/Cake.DotNetTool.Module.Tests.csproj index 6ce529474d..96cae6ced5 100644 --- a/src/Cake.DotNetTool.Module.Tests/Cake.DotNetTool.Module.Tests.csproj +++ b/src/Cake.DotNetTool.Module.Tests/Cake.DotNetTool.Module.Tests.csproj @@ -16,7 +16,7 @@ - + all diff --git a/src/Cake.Frosting.Tests/Cake.Frosting.Tests.csproj b/src/Cake.Frosting.Tests/Cake.Frosting.Tests.csproj index d7d8625724..e2db5e902e 100644 --- a/src/Cake.Frosting.Tests/Cake.Frosting.Tests.csproj +++ b/src/Cake.Frosting.Tests/Cake.Frosting.Tests.csproj @@ -8,8 +8,8 @@ - - + + all diff --git a/src/Cake.NuGet.Tests/Cake.NuGet.Tests.csproj b/src/Cake.NuGet.Tests/Cake.NuGet.Tests.csproj index be0e14d723..0bdfc651a5 100644 --- a/src/Cake.NuGet.Tests/Cake.NuGet.Tests.csproj +++ b/src/Cake.NuGet.Tests/Cake.NuGet.Tests.csproj @@ -15,7 +15,7 @@ - + all diff --git a/src/Cake.Tests/Cake.Tests.csproj b/src/Cake.Tests/Cake.Tests.csproj index 24aeb05565..3129807382 100644 --- a/src/Cake.Tests/Cake.Tests.csproj +++ b/src/Cake.Tests/Cake.Tests.csproj @@ -10,7 +10,7 @@ - + all From 5771c27ad849ccc70d90288ffb0ab65f66cf86df Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Tue, 22 Mar 2022 17:26:52 +0100 Subject: [PATCH 11/27] (GH-3851) Update System.Reflection.Metadata to 6.0.1 * fixes #3851 --- src/Cake/Cake.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake/Cake.csproj b/src/Cake/Cake.csproj index 838ef36a55..5cf4a6061f 100644 --- a/src/Cake/Cake.csproj +++ b/src/Cake/Cake.csproj @@ -28,7 +28,7 @@ - + From 726c9e79c8bfbcf44ab12ddc30b41b5dcb1dc024 Mon Sep 17 00:00:00 2001 From: Francisco Loureiro Date: Thu, 14 Oct 2021 21:52:58 +0100 Subject: [PATCH 12/27] Add UploadFileSettings to UploadFile functions --- src/Cake.Common/Net/HttpAliases.cs | 43 ++++++++++++++++++----- src/Cake.Common/Net/UploadFileSettings.cs | 30 ++++++++++++++++ 2 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 src/Cake.Common/Net/UploadFileSettings.cs diff --git a/src/Cake.Common/Net/HttpAliases.cs b/src/Cake.Common/Net/HttpAliases.cs index 2a0b4a1288..20b346861e 100644 --- a/src/Cake.Common/Net/HttpAliases.cs +++ b/src/Cake.Common/Net/HttpAliases.cs @@ -235,15 +235,20 @@ public static void DownloadFile(this ICakeContext context, Uri address, FilePath /// /// /// var address = new Uri("http://www.example.org/upload"); - /// UploadFile(address, @"path/to/file.txt"); + /// UploadFile(address, @"path/to/file.txt", new UploadFileSettings() + /// { + /// Username = "bob", + /// Password = "builder" + /// } /// /// /// The context. /// The URL of the upload resource. /// The file to upload. + /// The settings. [CakeMethodAlias] [CakeAliasCategory("Upload")] - public static void UploadFile(this ICakeContext context, Uri address, FilePath filePath) + public static void UploadFile(this ICakeContext context, Uri address, FilePath filePath, UploadFileSettings settings) { if (context == null) { @@ -259,8 +264,17 @@ public static void UploadFile(this ICakeContext context, Uri address, FilePath f } context.Log.Verbose("Uploading file: {0}", address); - using (var client = GetHttpClient(context, false)) + using (var client = GetHttpClient(context, settings.UseDefaultCredentials)) { + if (!settings.UseDefaultCredentials) + { + if (!string.IsNullOrWhiteSpace(settings.Username) && !string.IsNullOrWhiteSpace(settings.Password)) + { + var byteArray = Encoding.ASCII.GetBytes(string.Concat(settings.Username, ":", settings.Password)); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); + } + } + client.UploadFileAsync(address, filePath.FullPath).Wait(); } context.Log.Verbose("File upload complete"); @@ -282,7 +296,7 @@ public static void UploadFile(this ICakeContext context, Uri address, FilePath f [CakeAliasCategory("Upload")] public static void UploadFile(this ICakeContext context, string address, FilePath filePath) { - UploadFile(context, new Uri(address), filePath); + UploadFile(context, new Uri(address), filePath, new UploadFileSettings()); } /// @@ -291,16 +305,20 @@ public static void UploadFile(this ICakeContext context, string address, FilePat /// /// /// var address = new Uri("http://www.example.org/upload"); - /// UploadFile(address, @"path/to/file.txt"); + /// UploadFile(address, @"path/to/file.txt", new UploadFileSettings() { + /// Username = "bob", + /// Password = "builder" + /// }); /// /// /// The context. /// The URL of the upload resource. /// The data to upload. /// The filename to give the uploaded data. + /// The settings. [CakeMethodAlias] [CakeAliasCategory("Upload")] - public static void UploadFile(this ICakeContext context, Uri address, byte[] data, string fileName) + public static void UploadFile(this ICakeContext context, Uri address, byte[] data, string fileName, UploadFileSettings settings) { if (context == null) { @@ -316,8 +334,17 @@ public static void UploadFile(this ICakeContext context, Uri address, byte[] dat } context.Log.Verbose("Uploading file: {0}", address); - using (var client = GetHttpClient(context, false)) + using (var client = GetHttpClient(context, settings.UseDefaultCredentials)) { + if (!settings.UseDefaultCredentials) + { + if (!string.IsNullOrWhiteSpace(settings.Username) && !string.IsNullOrWhiteSpace(settings.Password)) + { + var byteArray = Encoding.ASCII.GetBytes(string.Concat(settings.Username, ":", settings.Password)); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); + } + } + client.UploadFileAsync(address, data, fileName).Wait(); } context.Log.Verbose("File upload complete"); @@ -340,7 +367,7 @@ public static void UploadFile(this ICakeContext context, Uri address, byte[] dat [CakeAliasCategory("Upload")] public static void UploadFile(this ICakeContext context, string address, byte[] data, string fileName) { - UploadFile(context, new Uri(address), data, fileName); + UploadFile(context, new Uri(address), data, fileName, new UploadFileSettings()); } /// diff --git a/src/Cake.Common/Net/UploadFileSettings.cs b/src/Cake.Common/Net/UploadFileSettings.cs new file mode 100644 index 0000000000..4c6f7d66af --- /dev/null +++ b/src/Cake.Common/Net/UploadFileSettings.cs @@ -0,0 +1,30 @@ +// 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.Net +{ + /// + /// Contains settings for . + /// + public sealed class UploadFileSettings + { + /// + /// Gets or sets the username to use when uploadingthe file. + /// + public string Username { get; set; } + + /// + /// Gets or sets the password to use when uploading the file. + /// + public string Password { get; set; } + + /// + /// Gets or sets a value indicating whether default credentials are sent when uploading the file. + /// + /// + /// If set to true, any username and password that has been specified will be ignored. + /// + public bool UseDefaultCredentials { get; set; } + } +} \ No newline at end of file From 92729877dcb4067cd23768051949b1a253c23b67 Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Wed, 30 Mar 2022 14:07:24 +0200 Subject: [PATCH 13/27] (GH-3854) Update Spectre.Console to 0.44.0 * fixes #3854 --- src/Cake.Cli/Cake.Cli.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake.Cli/Cake.Cli.csproj b/src/Cake.Cli/Cake.Cli.csproj index 28ee02e248..f517071b83 100644 --- a/src/Cake.Cli/Cake.Cli.csproj +++ b/src/Cake.Cli/Cake.Cli.csproj @@ -19,6 +19,6 @@ - + \ No newline at end of file From 64e9bfaadfa49ecb1db8427ba15ee085daffdaff Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Sat, 2 Apr 2022 19:27:04 +0200 Subject: [PATCH 14/27] (GH-3823) Only load valid versioned assmblies * fixes #3823 --- .../Scripting/ReferenceAssemblyResolver.cs | 11 +++++++++-- .../Scripting/ScriptAssemblyResolver.cs | 11 +++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Cake/Infrastructure/Scripting/ReferenceAssemblyResolver.cs b/src/Cake/Infrastructure/Scripting/ReferenceAssemblyResolver.cs index d420818f87..843316378d 100644 --- a/src/Cake/Infrastructure/Scripting/ReferenceAssemblyResolver.cs +++ b/src/Cake/Infrastructure/Scripting/ReferenceAssemblyResolver.cs @@ -9,6 +9,7 @@ namespace Cake.Infrastructure.Scripting { public sealed class ReferenceAssemblyResolver : IReferenceAssemblyResolver { + private static readonly Version VersionZero = new Version(0, 0, 0, 0); private readonly ICakeLog _log; public ReferenceAssemblyResolver(ICakeLog log) @@ -36,7 +37,7 @@ IEnumerable TryGetReferenceAssemblies() } catch (Exception ex) { - _log.Debug(log => log("Failed to load {0}\r\n{1}", reference.FilePath, ex)); + _log.Debug(log => log("Failed to load {0}\r\nException: {1}", reference.FilePath, ex)); continue; } @@ -49,6 +50,12 @@ IEnumerable TryGetReferenceAssemblies() foreach (var assemblyRefName in assembly.GetReferencedAssemblies()) { + if (assemblyRefName == null || + assemblyRefName.Version == VersionZero) + { + continue; + } + Assembly assemblyRef; try { @@ -56,7 +63,7 @@ IEnumerable TryGetReferenceAssemblies() } catch (Exception ex) { - _log.Debug(log => log("Failed to load {0}\r\n{1}", reference.FilePath, ex)); + _log.Debug(log => log("Failed to load {0}\r\nReference: {1}\r\n Exception: {2}", assemblyRefName, assembly, ex)); continue; } diff --git a/src/Cake/Infrastructure/Scripting/ScriptAssemblyResolver.cs b/src/Cake/Infrastructure/Scripting/ScriptAssemblyResolver.cs index e9406473d9..44ea0e886a 100644 --- a/src/Cake/Infrastructure/Scripting/ScriptAssemblyResolver.cs +++ b/src/Cake/Infrastructure/Scripting/ScriptAssemblyResolver.cs @@ -15,6 +15,7 @@ namespace Cake.Infrastructure.Scripting public sealed class ScriptAssemblyResolver : IDisposable { private const string AssemblyResourcesExtension = ".resources"; + private static readonly Version VersionZero = new Version(0, 0, 0, 0); private readonly ICakeEnvironment _environment; private readonly ICakeLog _log; @@ -49,6 +50,11 @@ private Assembly AssemblyResolve(object sender, ResolveEventArgs args) var shortName = assemblyName.Name; var version = assemblyName.Version; + if (version == VersionZero) + { + return null; + } + // Preventing indirect recursive calls via Assembly.Load() if (!_resolvedNames.Add(shortName + version)) { @@ -65,6 +71,11 @@ private Assembly AssemblyResolve(AssemblyName assemblyName) var shortName = assemblyName.Name; var version = assemblyName.Version; + if (version == VersionZero) + { + return null; + } + Assembly assembly = null; try { From 4cb0a7a04f833b81ef04cfabb0c691a9d45a81fe Mon Sep 17 00:00:00 2001 From: Roman Marusyk Date: Tue, 25 Jan 2022 00:14:18 +0200 Subject: [PATCH 15/27] Add alias for dotnet workload search command --- .../Search/DotNetWorkloadSearcherFixture.cs | 19 ++++++ .../Search/DotNetWorkloadSearchTests.cs | 58 +++++++++++++++++++ src/Cake.Common/Tools/DotNet/DotNetAliases.cs | 42 ++++++++++++++ .../Search/DotNetWorkloadSearchSettings.cs | 13 +++++ .../Workload/Search/DotNetWorkloadSearcher.cs | 55 ++++++++++++++++++ .../Tools/DotNet/DotNetAliases.cake | 16 ++++- 6 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 src/Cake.Common.Tests/Fixtures/Tools/DotNet/Workload/Search/DotNetWorkloadSearcherFixture.cs create mode 100644 src/Cake.Common.Tests/Unit/Tools/DotNet/Workload/Search/DotNetWorkloadSearchTests.cs create mode 100644 src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearchSettings.cs create mode 100644 src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearcher.cs diff --git a/src/Cake.Common.Tests/Fixtures/Tools/DotNet/Workload/Search/DotNetWorkloadSearcherFixture.cs b/src/Cake.Common.Tests/Fixtures/Tools/DotNet/Workload/Search/DotNetWorkloadSearcherFixture.cs new file mode 100644 index 0000000000..aebbb216ae --- /dev/null +++ b/src/Cake.Common.Tests/Fixtures/Tools/DotNet/Workload/Search/DotNetWorkloadSearcherFixture.cs @@ -0,0 +1,19 @@ +// 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.Tools.DotNet.Workload.Search; + +namespace Cake.Common.Tests.Fixtures.Tools.DotNet.Workload.Search +{ + internal sealed class DotNetWorkloadSearcherFixture : DotNetFixture + { + public string SearchString { get; set; } + + protected override void RunTool() + { + var tool = new DotNetWorkloadSearcher(FileSystem, Environment, ProcessRunner, Tools); + tool.Search(SearchString); + } + } +} diff --git a/src/Cake.Common.Tests/Unit/Tools/DotNet/Workload/Search/DotNetWorkloadSearchTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotNet/Workload/Search/DotNetWorkloadSearchTests.cs new file mode 100644 index 0000000000..5ed2743d50 --- /dev/null +++ b/src/Cake.Common.Tests/Unit/Tools/DotNet/Workload/Search/DotNetWorkloadSearchTests.cs @@ -0,0 +1,58 @@ +// 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.Workload.Search; +using Cake.Testing; +using Xunit; + +namespace Cake.Common.Tests.Unit.Tools.DotNet.Workload.Search +{ + public sealed class DotNetWorkloadSearchTests + { + public sealed class TheWorkloadSearchMethod + { + [Fact] + public void Should_Throw_If_Process_Was_Not_Started() + { + // Given + var fixture = new DotNetWorkloadSearcherFixture(); + 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 DotNetWorkloadSearcherFixture(); + 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_Add_SearchString_Argument() + { + // Given + var fixture = new DotNetWorkloadSearcherFixture(); + fixture.SearchString = "maui"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("workload search maui", result.Args); + } + } + } +} diff --git a/src/Cake.Common/Tools/DotNet/DotNetAliases.cs b/src/Cake.Common/Tools/DotNet/DotNetAliases.cs index 311f0b6a67..a0f51a45ac 100644 --- a/src/Cake.Common/Tools/DotNet/DotNetAliases.cs +++ b/src/Cake.Common/Tools/DotNet/DotNetAliases.cs @@ -22,6 +22,7 @@ using Cake.Common.Tools.DotNet.Test; using Cake.Common.Tools.DotNet.Tool; using Cake.Common.Tools.DotNet.VSTest; +using Cake.Common.Tools.DotNet.Workload.Search; using Cake.Common.Tools.DotNetCore.Build; using Cake.Common.Tools.DotNetCore.BuildServer; using Cake.Common.Tools.DotNetCore.Clean; @@ -1845,5 +1846,46 @@ public static void DotNetSDKCheck(this ICakeContext context) var checker = new DotNetSDKChecker(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); checker.Check(); } + + /// + /// Lists available workloads. + /// + /// The context. + /// + /// + /// DotNetWorkloadSearch(); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Workload")] + [CakeNamespaceImport("Cake.Common.Tools.DotNet.Workload.Search")] + public static void DotNetWorkloadSearch(this ICakeContext context) + { + context.DotNetWorkloadSearch(null); + } + + /// + /// Lists available workloads by specifying all or part of the workload ID. + /// + /// The context. + /// The workload ID to search for, or part of it. + /// + /// + /// DotNetWorkloadSearch("maui"); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Workload")] + [CakeNamespaceImport("Cake.Common.Tools.DotNet.Workload.Search")] + public static void DotNetWorkloadSearch(this ICakeContext context, string searchString) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + var searcher = new DotNetWorkloadSearcher(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); + searcher.Search(searchString); + } } } diff --git a/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearchSettings.cs b/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearchSettings.cs new file mode 100644 index 0000000000..d94c6ee90d --- /dev/null +++ b/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearchSettings.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.Workload.Search +{ + /// + /// Contains settings used by . + /// + public sealed class DotNetWorkloadSearchSettings : DotNetSettings + { + } +} diff --git a/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearcher.cs b/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearcher.cs new file mode 100644 index 0000000000..6479ca29dd --- /dev/null +++ b/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearcher.cs @@ -0,0 +1,55 @@ +// 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.Core; +using Cake.Core.IO; +using Cake.Core.Tooling; + +namespace Cake.Common.Tools.DotNet.Workload.Search +{ + /// + /// .NET workloads searcher. + /// + public sealed class DotNetWorkloadSearcher : DotNetTool + { + /// + /// Initializes a new instance of the class. + /// + /// The file system. + /// The environment. + /// The process runner. + /// The tool locator. + public DotNetWorkloadSearcher( + IFileSystem fileSystem, + ICakeEnvironment environment, + IProcessRunner processRunner, + IToolLocator tools) : base(fileSystem, environment, processRunner, tools) + { + } + + /// + /// Lists the latest available version of the .NET SDK and .NET Runtime, for each feature band. + /// + /// The workload ID to search for, or part of it. + public void Search(string searchString) + { + var settings = new DotNetWorkloadSearchSettings(); + RunCommand(settings, GetArguments(searchString, settings)); + } + + private ProcessArgumentBuilder GetArguments(string searchString, DotNetWorkloadSearchSettings settings) + { + var builder = CreateArgumentBuilder(settings); + + builder.Append("workload search"); + + if (!string.IsNullOrEmpty(searchString)) + { + builder.Append(searchString); + } + + return builder; + } + } +} diff --git a/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake b/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake index a43f983bc5..74a7b07928 100644 --- a/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake +++ b/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake @@ -246,7 +246,7 @@ Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetFormat") .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.Setup") .Does(() => { - // Given + // Given var path = Paths.Temp.Combine("./Cake.Common/Tools/DotNet"); var project = path.CombineWithFilePath("hwapp/hwapp.csproj"); @@ -259,7 +259,18 @@ Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetSDKCheck") .Does(() => { // When - DotNetSDKCheck(); + DotNetSDKCheck(); +}); + +Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetWorkloadSearch") + .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.Setup") + .Does(() => +{ + // Given + var searchString = "maui"; + + // When + DotNetWorkloadSearch(searchString); }); Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetBuildServerShutdown") @@ -279,6 +290,7 @@ Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetBuildServerShutdown") .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.DotNetTest.Fail") .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.DotNetFormat") .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.DotNetSDKCheck") + .IsDependentOn("Cake.Common.Tools.DotNet.DotNetAliases.DotNetWorkloadSearch") .Does(() => { // When From d22cc84a75cfc947f503b01a630831a7925c3ed8 Mon Sep 17 00:00:00 2001 From: Roman Marusyk Date: Sun, 13 Mar 2022 00:55:46 +0200 Subject: [PATCH 16/27] Parse console output to return search result --- .../Search/DotNetWorkloadSearcherFixture.cs | 16 ++++- .../Search/DotNetWorkloadSearchTests.cs | 30 ++++++++++ src/Cake.Common/Tools/DotNet/DotNetAliases.cs | 58 +++++++++++++++++-- .../DotNet/Workload/Search/DotNetWorkload.cs | 33 +++++++++++ .../Workload/Search/DotNetWorkloadSearcher.cs | 58 ++++++++++++++++++- .../Tools/DotNet/DotNetAliases.cake | 9 ++- 6 files changed, 193 insertions(+), 11 deletions(-) create mode 100644 src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkload.cs diff --git a/src/Cake.Common.Tests/Fixtures/Tools/DotNet/Workload/Search/DotNetWorkloadSearcherFixture.cs b/src/Cake.Common.Tests/Fixtures/Tools/DotNet/Workload/Search/DotNetWorkloadSearcherFixture.cs index aebbb216ae..66cd3fbe82 100644 --- a/src/Cake.Common.Tests/Fixtures/Tools/DotNet/Workload/Search/DotNetWorkloadSearcherFixture.cs +++ b/src/Cake.Common.Tests/Fixtures/Tools/DotNet/Workload/Search/DotNetWorkloadSearcherFixture.cs @@ -2,6 +2,7 @@ // 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.Workload.Search; namespace Cake.Common.Tests.Fixtures.Tools.DotNet.Workload.Search @@ -9,11 +10,24 @@ namespace Cake.Common.Tests.Fixtures.Tools.DotNet.Workload.Search internal sealed class DotNetWorkloadSearcherFixture : DotNetFixture { public string SearchString { get; set; } + public IEnumerable Workloads { get; set; } + + public void GivenAvailableWorkloadsResult() + { + ProcessRunner.Process.SetStandardOutput(new string[] + { + "Workload ID Description", + "-----------------------------------------------------", + "maui .NET MAUI SDK for all platforms", + "maui-desktop .NET MAUI SDK for Desktop", + "maui-mobile .NET MAUI SDK for Mobile" + }); + } protected override void RunTool() { var tool = new DotNetWorkloadSearcher(FileSystem, Environment, ProcessRunner, Tools); - tool.Search(SearchString); + Workloads = tool.Search(SearchString, Settings); } } } diff --git a/src/Cake.Common.Tests/Unit/Tools/DotNet/Workload/Search/DotNetWorkloadSearchTests.cs b/src/Cake.Common.Tests/Unit/Tools/DotNet/Workload/Search/DotNetWorkloadSearchTests.cs index 5ed2743d50..d6413d83f7 100644 --- a/src/Cake.Common.Tests/Unit/Tools/DotNet/Workload/Search/DotNetWorkloadSearchTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/DotNet/Workload/Search/DotNetWorkloadSearchTests.cs @@ -53,6 +53,36 @@ public void Should_Add_SearchString_Argument() // Then Assert.Equal("workload search maui", result.Args); } + + [Fact] + public void Should_Return_Correct_List_Of_Workloads() + { + // Given + var fixture = new DotNetWorkloadSearcherFixture(); + fixture.SearchString = "maui"; + fixture.GivenAvailableWorkloadsResult(); + + // When + var result = fixture.Run(); + + // Then + Assert.Collection(fixture.Workloads, + item => + { + Assert.Equal(item.Id, "maui"); + Assert.Equal(item.Description, ".NET MAUI SDK for all platforms"); + }, + item => + { + Assert.Equal(item.Id, "maui-desktop"); + Assert.Equal(item.Description, ".NET MAUI SDK for Desktop"); + }, + item => + { + Assert.Equal(item.Id, "maui-mobile"); + Assert.Equal(item.Description, ".NET MAUI SDK for Mobile"); + }); + } } } } diff --git a/src/Cake.Common/Tools/DotNet/DotNetAliases.cs b/src/Cake.Common/Tools/DotNet/DotNetAliases.cs index a0f51a45ac..4e712b9db4 100644 --- a/src/Cake.Common/Tools/DotNet/DotNetAliases.cs +++ b/src/Cake.Common/Tools/DotNet/DotNetAliases.cs @@ -1851,17 +1851,47 @@ public static void DotNetSDKCheck(this ICakeContext context) /// Lists available workloads. /// /// The context. + /// The list of available workloads. /// /// - /// DotNetWorkloadSearch(); + /// var workloads = DotNetWorkloadSearch(); + /// + /// foreach (var workload in workloads) + /// { + /// Information($"Id: {workload.Id}, Description: {workload.Description}"); + /// } + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("Workload")] + [CakeNamespaceImport("Cake.Common.Tools.DotNet.Workload.Search")] + public static IEnumerable DotNetWorkloadSearch(this ICakeContext context) + { + return context.DotNetWorkloadSearch(null); + } + + /// + /// Lists available workloads by specifying all or part of the workload ID. + /// + /// The context. + /// The workload ID to search for, or part of it. + /// The list of available workloads. + /// + /// + /// var workloads = DotNetWorkloadSearch("maui"); + /// + /// foreach (var workload in workloads) + /// { + /// Information($"Id: {workload.Id}, Description: {workload.Description}"); + /// } /// /// [CakeMethodAlias] [CakeAliasCategory("Workload")] [CakeNamespaceImport("Cake.Common.Tools.DotNet.Workload.Search")] - public static void DotNetWorkloadSearch(this ICakeContext context) + public static IEnumerable DotNetWorkloadSearch(this ICakeContext context, string searchString) { - context.DotNetWorkloadSearch(null); + return context.DotNetWorkloadSearch(searchString, null); } /// @@ -1869,23 +1899,39 @@ public static void DotNetWorkloadSearch(this ICakeContext context) /// /// The context. /// The workload ID to search for, or part of it. + /// The list of available workloads. /// /// - /// DotNetWorkloadSearch("maui"); + /// var settings = new DotNetWorkloadSearchSettings + /// { + /// Verbosity = Detailed + /// }; + /// + /// var workloads = DotNetWorkloadSearch("maui", settings); + /// + /// foreach (var workload in workloads) + /// { + /// Information($"Id: {workload.Id}, Description: {workload.Description}"); + /// } /// /// [CakeMethodAlias] [CakeAliasCategory("Workload")] [CakeNamespaceImport("Cake.Common.Tools.DotNet.Workload.Search")] - public static void DotNetWorkloadSearch(this ICakeContext context, string searchString) + public static IEnumerable DotNetWorkloadSearch(this ICakeContext context, string searchString, DotNetWorkloadSearchSettings settings) { if (context is null) { throw new ArgumentNullException(nameof(context)); } + if (settings == null) + { + settings = new DotNetWorkloadSearchSettings(); + } + var searcher = new DotNetWorkloadSearcher(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); - searcher.Search(searchString); + return searcher.Search(searchString, settings); } } } diff --git a/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkload.cs b/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkload.cs new file mode 100644 index 0000000000..4ebd1583b1 --- /dev/null +++ b/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkload.cs @@ -0,0 +1,33 @@ +// 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.Workload.Search +{ + /// + /// Workload information. + /// + public class DotNetWorkload + { + /// + /// Initializes a new instance of the class. + /// + /// The workload Id. + /// The workload description. + public DotNetWorkload(string id, string description) + { + Id = id; + Description = description; + } + + /// + /// Gets the workload Id. + /// + public string Id { get; } + + /// + /// Gets the workload description. + /// + public string Description { get; } + } +} diff --git a/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearcher.cs b/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearcher.cs index 6479ca29dd..e24df64f52 100644 --- a/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearcher.cs +++ b/src/Cake.Common/Tools/DotNet/Workload/Search/DotNetWorkloadSearcher.cs @@ -2,6 +2,9 @@ // 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; @@ -32,10 +35,25 @@ public DotNetWorkloadSearcher( /// Lists the latest available version of the .NET SDK and .NET Runtime, for each feature band. /// /// The workload ID to search for, or part of it. - public void Search(string searchString) + /// The settings. + /// The list of available workloads. + public IEnumerable Search(string searchString, DotNetWorkloadSearchSettings settings) { - var settings = new DotNetWorkloadSearchSettings(); - RunCommand(settings, GetArguments(searchString, settings)); + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + var processSettings = new ProcessSettings + { + RedirectStandardOutput = true + }; + + IEnumerable result = null; + RunCommand(settings, GetArguments(searchString, settings), processSettings, + process => result = process.GetStandardOutput()); + + return ParseResult(result).ToList(); } private ProcessArgumentBuilder GetArguments(string searchString, DotNetWorkloadSearchSettings settings) @@ -51,5 +69,39 @@ private ProcessArgumentBuilder GetArguments(string searchString, DotNetWorkloadS return builder; } + + private static IEnumerable ParseResult(IEnumerable result) + { + bool first = true; + int descriptionIndex = -1; + foreach (var line in result) + { + if (first) + { + if (line?.StartsWith("Workload ID") == true + && (descriptionIndex = line?.IndexOf("Description") ?? -1) > 11) + { + first = false; + } + continue; + } + + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + var trimmedLine = line.Trim(); + + if (trimmedLine.Trim().All(c => c == '-')) + { + continue; + } + + yield return new DotNetWorkload( + string.Concat(trimmedLine.Take(descriptionIndex)).TrimEnd(), + string.Concat(trimmedLine.Skip(descriptionIndex))); + } + } } } diff --git a/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake b/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake index 74a7b07928..c60942f118 100644 --- a/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake +++ b/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake @@ -270,7 +270,14 @@ Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetWorkloadSearch") var searchString = "maui"; // When - DotNetWorkloadSearch(searchString); + var workloads = DotNetWorkloadSearch(searchString); + + // Then + foreach(var workload in workloads) + { + Assert.Contains(workload.Id, "maui"); + Assert.Contains(workload.Description, ".NET MAUI SDK"); + } }); Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetBuildServerShutdown") From c0e9140d69cb7edfd883eb1c7f65f812b95db5c6 Mon Sep 17 00:00:00 2001 From: Roman Marusyk Date: Sun, 13 Mar 2022 01:40:20 +0200 Subject: [PATCH 17/27] Add XML description of settings param --- src/Cake.Common/Tools/DotNet/DotNetAliases.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Cake.Common/Tools/DotNet/DotNetAliases.cs b/src/Cake.Common/Tools/DotNet/DotNetAliases.cs index 4e712b9db4..0a84cb7bca 100644 --- a/src/Cake.Common/Tools/DotNet/DotNetAliases.cs +++ b/src/Cake.Common/Tools/DotNet/DotNetAliases.cs @@ -1899,6 +1899,7 @@ public static IEnumerable DotNetWorkloadSearch(this ICakeContext /// /// The context. /// The workload ID to search for, or part of it. + /// The settings. /// The list of available workloads. /// /// From 3d91bb0006f7c23ec52327e1c17cc940825742fd Mon Sep 17 00:00:00 2001 From: Roman Marusyk Date: Sun, 13 Mar 2022 03:09:44 +0200 Subject: [PATCH 18/27] Fix integration tests for DotNetWorkloadSearch --- tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake b/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake index c60942f118..4afd636041 100644 --- a/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake +++ b/tests/integration/Cake.Common/Tools/DotNet/DotNetAliases.cake @@ -275,8 +275,8 @@ Task("Cake.Common.Tools.DotNet.DotNetAliases.DotNetWorkloadSearch") // Then foreach(var workload in workloads) { - Assert.Contains(workload.Id, "maui"); - Assert.Contains(workload.Description, ".NET MAUI SDK"); + Assert.Contains("maui", workload.Id); + Assert.Contains(".NET MAUI SDK", workload.Description); } }); From f4c9ac70c3a1bb4335948b2fffc9ada28459aadf Mon Sep 17 00:00:00 2001 From: Todd Stewart Date: Sat, 29 May 2021 16:50:19 -0500 Subject: [PATCH 19/27] GH-2099: Cache compiled script on disk * Enable * Config: Settings.EnableScriptCache=true * Arg: --settings_enablescriptcache=true * Env: CAKE_SETTINGS_ENABLESCRIPTCACHE=true * Cache Location * Config: Paths.Cache = /path/to/cache * Arg: --paths_cache=/path/to/cache * Env: CAKE_PATHS_CACHE=/path/to/cache * fixes #2099 Co-authored-by: devlead@users.noreply.github.com --- src/Cake/Commands/DefaultCommandSettings.cs | 4 + .../CakeConfigurationExtensions.cs | 34 +++++ src/Cake/Infrastructure/Constants.cs | 24 ++++ .../Infrastructure/IScriptHostSettings.cs | 4 + .../Scripting/RoslynScriptSession.cs | 76 +++++++++- src/Cake/Infrastructure/Utilities/FastHash.cs | 67 +++++++++ tests/integration/Cake/ScriptCache.cake | 136 ++++++++++++++++++ tests/integration/build.cake | 5 + .../resources/Cake/ScriptCache/build.cake | 1 + 9 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 src/Cake/Infrastructure/CakeConfigurationExtensions.cs create mode 100644 src/Cake/Infrastructure/Constants.cs create mode 100644 src/Cake/Infrastructure/Utilities/FastHash.cs create mode 100644 tests/integration/Cake/ScriptCache.cake create mode 100644 tests/integration/resources/Cake/ScriptCache/build.cake diff --git a/src/Cake/Commands/DefaultCommandSettings.cs b/src/Cake/Commands/DefaultCommandSettings.cs index 88d0c43270..5e7fce5691 100644 --- a/src/Cake/Commands/DefaultCommandSettings.cs +++ b/src/Cake/Commands/DefaultCommandSettings.cs @@ -59,5 +59,9 @@ public sealed class DefaultCommandSettings : CommandSettings [CommandOption("--info")] [Description("Displays additional information about Cake.")] public bool ShowInfo { get; set; } + + [CommandOption("--" + Infrastructure.Constants.Cache.InvalidateScriptCache)] + [Description("Forces the script to be recompiled if caching is enabled.")] + public bool Recompile { get; set; } } } diff --git a/src/Cake/Infrastructure/CakeConfigurationExtensions.cs b/src/Cake/Infrastructure/CakeConfigurationExtensions.cs new file mode 100644 index 0000000000..eebc9e9b64 --- /dev/null +++ b/src/Cake/Infrastructure/CakeConfigurationExtensions.cs @@ -0,0 +1,34 @@ +// 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.Core; +using Cake.Core.Configuration; +using Cake.Core.IO; + +namespace Cake.Infrastructure +{ + /// + /// Contains extension methods for . + /// + internal static class CakeConfigurationExtensions + { + /// + /// Gets the script cache directory path. + /// + /// The Cake configuration. + /// The default root path. + /// The environment. + /// The script cache directory path. + public static DirectoryPath GetScriptCachePath(this ICakeConfiguration configuration, DirectoryPath defaultRoot, ICakeEnvironment environment) + { + var cachePath = configuration.GetValue(Constants.Paths.Cache); + if (!string.IsNullOrWhiteSpace(cachePath)) + { + return new DirectoryPath(cachePath).MakeAbsolute(environment); + } + var toolPath = configuration.GetToolPath(defaultRoot, environment); + return toolPath.Combine("cache").Collapse(); + } + } +} diff --git a/src/Cake/Infrastructure/Constants.cs b/src/Cake/Infrastructure/Constants.cs new file mode 100644 index 0000000000..f3af4a73db --- /dev/null +++ b/src/Cake/Infrastructure/Constants.cs @@ -0,0 +1,24 @@ +// 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.Infrastructure +{ + internal static class Constants + { + public static class Settings + { + public const string EnableScriptCache = "Settings_EnableScriptCache"; + } + + public static class Paths + { + public const string Cache = "Paths_Cache"; + } + + public static class Cache + { + public const string InvalidateScriptCache = "invalidate-script-cache"; + } + } +} diff --git a/src/Cake/Infrastructure/IScriptHostSettings.cs b/src/Cake/Infrastructure/IScriptHostSettings.cs index c9d34d39f5..3755facfa8 100644 --- a/src/Cake/Infrastructure/IScriptHostSettings.cs +++ b/src/Cake/Infrastructure/IScriptHostSettings.cs @@ -2,10 +2,14 @@ // 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.Core.IO; + namespace Cake.Infrastructure { public interface IScriptHostSettings { bool Debug { get; } + + FilePath Script { get; } } } diff --git a/src/Cake/Infrastructure/Scripting/RoslynScriptSession.cs b/src/Cake/Infrastructure/Scripting/RoslynScriptSession.cs index a610344540..6153104195 100644 --- a/src/Cake/Infrastructure/Scripting/RoslynScriptSession.cs +++ b/src/Cake/Infrastructure/Scripting/RoslynScriptSession.cs @@ -5,14 +5,18 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Reflection; +using System.Text; +using System.Threading.Tasks; using Cake.Core; using Cake.Core.Configuration; using Cake.Core.Diagnostics; using Cake.Core.IO; using Cake.Core.Reflection; using Cake.Core.Scripting; +using Cake.Infrastructure.Utilities; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Scripting; @@ -21,11 +25,16 @@ namespace Cake.Infrastructure.Scripting public sealed class RoslynScriptSession : IScriptSession { private readonly IScriptHost _host; + private readonly IFileSystem _fileSystem; private readonly IAssemblyLoader _loader; private readonly ICakeLog _log; private readonly ICakeConfiguration _configuration; private readonly IScriptHostSettings _settings; + private readonly bool _scriptCacheEnabled; + private readonly bool _regenerateCache; + private readonly DirectoryPath _scriptCachePath; + public HashSet ReferencePaths { get; } public HashSet References { get; } @@ -40,6 +49,7 @@ public RoslynScriptSession( IScriptHostSettings settings) { _host = host; + _fileSystem = host.Context.FileSystem; _loader = loader; _log = log; _configuration = configuration; @@ -48,6 +58,11 @@ public RoslynScriptSession( ReferencePaths = new HashSet(PathComparer.Default); References = new HashSet(); Namespaces = new HashSet(StringComparer.Ordinal); + + var cacheEnabled = configuration.GetValue(Constants.Settings.EnableScriptCache) ?? bool.FalseString; + _scriptCacheEnabled = cacheEnabled.Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase); + _regenerateCache = host.Context.Arguments.HasArgument(Constants.Cache.InvalidateScriptCache); + _scriptCachePath = configuration.GetScriptCachePath(settings.Script.GetDirectory(), host.Context.Environment); } public void AddReference(Assembly assembly) @@ -82,6 +97,30 @@ public void ImportNamespace(string @namespace) public void Execute(Script script) { + var scriptName = _settings.Script.GetFilename(); + var cacheDLLFileName = $"{scriptName}.dll"; + var cacheHashFileName = $"{scriptName}.hash"; + var cachedAssembly = _scriptCachePath.CombineWithFilePath(cacheDLLFileName); + var hashFile = _scriptCachePath.CombineWithFilePath(cacheHashFileName); + string scriptHash = default; + if (_scriptCacheEnabled && _fileSystem.Exist(cachedAssembly) && !_regenerateCache) + { + _log.Verbose($"Cache enabled: Checking cache build script ({cacheDLLFileName})"); + scriptHash = FastHash.GenerateHash(Encoding.UTF8.GetBytes(string.Concat(script.Lines))); + var cachedHash = _fileSystem.Exist(hashFile) + ? _fileSystem.GetFile(hashFile).ReadLines(Encoding.UTF8).FirstOrDefault() + : string.Empty; + if (scriptHash.Equals(cachedHash, StringComparison.Ordinal)) + { + _log.Verbose("Running cached build script..."); + RunScriptAssembly(cachedAssembly.FullPath); + return; + } + else + { + _log.Verbose("Cache check failed."); + } + } // Generate the script code. var generator = new RoslynCodeGenerator(); var code = generator.Generate(script); @@ -159,7 +198,42 @@ public void Execute(Script script) throw new CakeException(message); } - roslynScript.RunAsync(_host).Wait(); + if (_scriptCacheEnabled) + { + // Verify cache directory exists + if (!_fileSystem.GetDirectory(_scriptCachePath).Exists) + { + _fileSystem.GetDirectory(_scriptCachePath).Create(); + } + if (string.IsNullOrWhiteSpace(scriptHash)) + { + scriptHash = FastHash.GenerateHash(Encoding.UTF8.GetBytes(string.Concat(script.Lines))); + } + var emitResult = compilation.Emit(cachedAssembly.FullPath); + + if (emitResult.Success) + { + using (var stream = _fileSystem.GetFile(hashFile).OpenWrite()) + using (var writer = new StreamWriter(stream, Encoding.UTF8)) + { + writer.Write(scriptHash); + } + RunScriptAssembly(cachedAssembly.FullPath); + } + } + else + { + roslynScript.RunAsync(_host).GetAwaiter().GetResult(); + } + } + + private void RunScriptAssembly(string assemblyPath) + { + var assembly = _loader.Load(assemblyPath, false); + var type = assembly.GetType("Submission#0"); + var factoryMethod = type.GetMethod("", new[] { typeof(object[]) }); + var task = (Task)factoryMethod.Invoke(null, new object[] { new object[] { _host, null } }); + task.GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Cake/Infrastructure/Utilities/FastHash.cs b/src/Cake/Infrastructure/Utilities/FastHash.cs new file mode 100644 index 0000000000..6fec2a5439 --- /dev/null +++ b/src/Cake/Infrastructure/Utilities/FastHash.cs @@ -0,0 +1,67 @@ +// 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.Security.Cryptography; +using System.Text; + +namespace Cake.Infrastructure.Utilities +{ + /// + /// Optimized hash generator. Using SHA512 since it is FIPS compliant. + /// + internal static class FastHash + { + /// + /// Generates a hash of the passed byte arrays. + /// + /// The binary data to hash. + /// The hash value. + public static string GenerateHash(byte[] input) + { + using (var sha512 = SHA512.Create()) + { + sha512.TransformBlock(input, 0, input.Length, input, 0); + + // Just finalize with empty bytes so we don't have to iterate over the enumerable multiple times + sha512.TransformFinalBlock(Encoding.UTF8.GetBytes(string.Empty), 0, 0); + // Convert to hex string; This method is supposedly faster than the usual StringBuilder approach + return ConvertBits(sha512.Hash); + } + } + + /// + /// Generates a hash of the passed byte arrays. + /// + /// The binary data to hash. + /// The hash value. + public static string GenerateHash(IEnumerable inputs) + { + using (var sha512 = SHA512.Create()) + { + foreach (var input in inputs) + { + sha512.TransformBlock(input, 0, input.Length, input, 0); + } + + // Just finalize with empty bytes so we don't have to iterate over the enumerable multiple times + sha512.TransformFinalBlock(Encoding.UTF8.GetBytes(string.Empty), 0, 0); + // Convert to hex string; This method is supposedly faster than the usual StringBuilder approach + return ConvertBits(sha512.Hash); + } + } + + private static string ConvertBits(byte[] hash) + { +#if NETCOREAPP3_1 + return BitConverter.ToString(hash) + // without dashes + .Replace("-", string.Empty); +#else + return Convert.ToHexString(hash); +#endif + } + } +} diff --git a/tests/integration/Cake/ScriptCache.cake b/tests/integration/Cake/ScriptCache.cake new file mode 100644 index 0000000000..22d51fb6b7 --- /dev/null +++ b/tests/integration/Cake/ScriptCache.cake @@ -0,0 +1,136 @@ +#load "./../../../utilities/xunit.cake" +#load "./../../../utilities/paths.cake" +using System.Diagnostics; + +public class ScriptCacheData +{ + public FilePath ScriptPath { get; } + public FilePath ScriptCacheAssemblyPath { get; } + public FilePath ScriptCacheHashPath { get; } + public (TimeSpan Elapsed, string Hash) CompileResult { get; set; } + public (TimeSpan Elapsed, string Hash) ExecuteResult { get; set; } + public (TimeSpan Elapsed, string Hash) ReCompileResult { get; set; } + public CakeSettings Settings { get; } + private Action CakeExecuteScript { get; } + private Func CalculateFileHash { get; } + + public TimeSpan Time(Action action) + { + var stopwatch = Stopwatch.StartNew(); + try + { + action(); + } + finally + { + stopwatch.Stop(); + } + return stopwatch.Elapsed; + } + + public (TimeSpan Elapsed, string Hash) TimeCakeExecuteScript() => TimeCakeExecuteScript(args => args); + + public (TimeSpan Elapsed, string Hash) TimeCakeExecuteScript(Func argumentCustomization) => + ( + Time( + () => { + Settings.ArgumentCustomization = argumentCustomization; + CakeExecuteScript( + ScriptPath, + Settings); + }), + CalculateFileHash(ScriptCacheAssemblyPath).ToHex() + ); + + public ScriptCacheData( + DirectoryPath scriptDirectoryPath, + Action cakeExecuteScript, + Func calculateFileHash + ) + { + ScriptPath = scriptDirectoryPath.CombineWithFilePath("build.cake"); + var cacheDirectoryPath = scriptDirectoryPath.Combine("tools").Combine("cache"); + ScriptCacheAssemblyPath = cacheDirectoryPath.CombineWithFilePath("build.cake.dll"); + ScriptCacheHashPath = cacheDirectoryPath.CombineWithFilePath("build.cake.hash"); + Settings = new CakeSettings { + EnvironmentVariables = new Dictionary { + { "CAKE_SETTINGS_ENABLESCRIPTCACHE", "true" } + }, + Verbosity = Verbosity.Quiet + }; + CakeExecuteScript = cakeExecuteScript; + CalculateFileHash = calculateFileHash; + } +} + +Setup(context => + new ScriptCacheData( + Paths.Temp + .Combine("./Cake/ScriptCache"), + context.CakeExecuteScript, + context.CalculateFileHash + )); + +Task("Cake.ScriptCache.Setup") + .Does(() => +{ + var sourcePath = Paths.Resources.Combine("./Cake/ScriptCache"); + var targetPath = Paths.Temp.Combine("./Cake/ScriptCache"); + EnsureDirectoryExists(targetPath.Combine("../").Collapse()); + if (DirectoryExists(targetPath)) + { + DeleteDirectory( + targetPath, + new DeleteDirectorySettings { + Recursive = true, + Force = true + }); + } + CopyDirectory(sourcePath, targetPath); +}); + +Task("Cake.ScriptCache.Compile") + .IsDependentOn("Cake.ScriptCache.Setup") + .Does((context, data) => +{ + // Given / When + data.CompileResult = data.TimeCakeExecuteScript(); + + // Then + Assert.True(FileExists(data.ScriptCacheAssemblyPath), $"Script Cache Assembly Path {data.ScriptCacheAssemblyPath} missing."); + Assert.True(FileExists(data.ScriptCacheAssemblyPath), $"Script Cache Hash Path {data.ScriptCacheHashPath} missing."); +}); + +var scriptCacheExecute = Task("Cake.ScriptCache.Execute"); +for(var index = 1; index <= 5; index++) +{ + scriptCacheExecute.IsDependentOn( + Task($"Cake.ScriptCache.Execute.{index}") + .Does((context, data) => + { + // Given / When + data.ExecuteResult = data.TimeCakeExecuteScript(); + + // Then + Assert.True(data.CompileResult.Elapsed > data.ExecuteResult.Elapsed, $"Compile time {data.CompileResult.Elapsed} should be greater than execute time {data.ExecuteResult.Elapsed}."); + Assert.Equal(data.CompileResult.Hash, data.ExecuteResult.Hash); + }) + ); +} + +Task("Cake.ScriptCache.ReCompile") + .IsDependentOn("Cake.ScriptCache.Execute") + .Does((context, data) => { + // Given / When + data.ReCompileResult = data.TimeCakeExecuteScript(args => args.Append("--invalidate-script-cache")); + + // Then + Assert.True(data.ReCompileResult.Elapsed> data.ExecuteResult.Elapsed, $"ReCompileTime time {data.ReCompileResult.Elapsed} should be greater than execute time {data.ExecuteResult.Elapsed}."); + Assert.NotEqual(data.CompileResult.Hash , data.ReCompileResult.Hash); + }); + +Task("Cake.ScriptCache") + .IsDependentOn("Cake.ScriptCache.Setup") + .IsDependentOn("Cake.ScriptCache.Compile") + .IsDependentOn("Cake.ScriptCache.Execute") + .IsDependentOn("Cake.ScriptCache.ReCompile"); \ No newline at end of file diff --git a/tests/integration/build.cake b/tests/integration/build.cake index 143f685e50..3f7a2c536a 100644 --- a/tests/integration/build.cake +++ b/tests/integration/build.cake @@ -6,6 +6,7 @@ // Tests #load "setup.cake" #load "teardown.cake" +#load "./Cake/ScriptCache.cake" #load "./Cake.Common/ArgumentAliases.cake" #load "./Cake.Common/Build/BuildSystemAliases.cake" #load "./Cake.Common/EnvironmentAliases.cake" @@ -54,6 +55,9 @@ var target = Argument("target", "Run-All-Tests"); // TARGETS ////////////////////////////////////////////////// +Task("Cake") + .IsDependentOn("Cake.ScriptCache"); + Task("Cake.Core") .IsDependentOn("Cake.Core.Diagnostics") .IsDependentOn("Cake.Core.IO.Path") @@ -100,6 +104,7 @@ Task("Cake.Chocolatey") Task("Run-All-Tests") .IsDependentOn("Setup-Tests") + .IsDependentOn("Cake") .IsDependentOn("Cake.Core") .IsDependentOn("Cake.Common") .IsDependentOn("Cake.DotNetTool.Module") diff --git a/tests/integration/resources/Cake/ScriptCache/build.cake b/tests/integration/resources/Cake/ScriptCache/build.cake new file mode 100644 index 0000000000..e98d44e8e9 --- /dev/null +++ b/tests/integration/resources/Cake/ScriptCache/build.cake @@ -0,0 +1 @@ +Information("Hello from compiled script!"); From d57b3611f0ed88ef71432a7b2f5f0caadf99554e Mon Sep 17 00:00:00 2001 From: Ronald Jimmy Headrick <6085143+LagunaElectric@users.noreply.github.com> Date: Mon, 10 May 2021 23:02:08 -0400 Subject: [PATCH 20/27] (#2763) Added GetParent() method to DirectoryPath and associated tests --- .../Unit/IO/DirectoryPathTests.cs | 88 +++++++++++++++++++ src/Cake.Core/IO/DirectoryPath.cs | 18 ++++ 2 files changed, 106 insertions(+) diff --git a/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs b/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs index fb972ff85f..d67c0b37b3 100644 --- a/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs +++ b/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs @@ -255,6 +255,94 @@ public void Can_Not_Combine_Directory_Path_With_Absolute_Windows_Directory_Path( } } + public sealed class TheGetParentMethod + { + public sealed class InWindowsFormat + { + [WindowsTheory] + [InlineData("C:/Data", "C:/")] + [InlineData("C:/Data/Work", "C:/Data")] + [InlineData("C:/Data/Work/file.txt", "C:/Data/Work")] + [InlineData("../../Work", "../..")] + [InlineData("Data/../Work", "Data/..")] + [InlineData(@"\\A\B\C", @"\\A\B")] + [InlineData(@"\\A\B\c.txt", @"\\A\B")] + public void Should_Return_Parent_Directory(string directoryPath, string parentPath) + { + // Given + var path = new DirectoryPath(directoryPath); + + // When + var result = path.GetParent(); + + // Then + Assert.Equal(parentPath, result.FullPath); + } + + [WindowsTheory] + [InlineData("C:/")] + [InlineData("C:")] + [InlineData(@"\\A\")] + [InlineData(@"\\A")] + [InlineData("..")] + [InlineData("/..")] + [InlineData("/../")] + public void Should_Return_Null_If_No_Parent(string directoryPath) + { + // Given + var path = new DirectoryPath(directoryPath); + + // When + var result = path.GetParent(); + + // Then + Assert.Equal(result, null); + } + + public sealed class InUnixFormat + { + [Theory] + [InlineData("/C/Data", "/C")] + [InlineData("/C/Data/Work", "/C/Data")] + [InlineData("../../Work", "../..")] + [InlineData("Data/../Work", "Data/..")] + [InlineData("/C/Data/Work/file.txt", "/C/Data/Work")] + public void Should_Return_Parent_Directory(string directoryPath, string parentPath) + { + // Given + var path = new DirectoryPath(directoryPath); + + // When + var result = path.GetParent(); + + // Then + Assert.Equal(parentPath, result.FullPath); + } + + [Theory] + [InlineData("C")] + [InlineData("/C")] + [InlineData("/C/")] + [InlineData(@"\\A")] + [InlineData(@"\\A\")] + [InlineData("..")] + [InlineData("/..")] + [InlineData("/../")] + public void Should_Return_Null_If_No_Parent(string directoryPath) + { + // Given + var path = new DirectoryPath(directoryPath); + + // When + var result = path.GetParent(); + + // Then + Assert.Equal(result, null); + } + } + } + } + public sealed class TheMakeAbsoluteMethod { public sealed class ThatTakesAnEnvironment diff --git a/src/Cake.Core/IO/DirectoryPath.cs b/src/Cake.Core/IO/DirectoryPath.cs index 884661d97d..3adcd47b3a 100644 --- a/src/Cake.Core/IO/DirectoryPath.cs +++ b/src/Cake.Core/IO/DirectoryPath.cs @@ -55,6 +55,24 @@ public FilePath GetFilePath(FilePath path) return new FilePath(PathHelper.Combine(FullPath, path.GetFilename().FullPath)); } + /// + /// Gets the directory path of a . + /// + /// A to the parent directory of the given . + public DirectoryPath GetParent() + { + var segments = Segments.Take(Segments.Length - 1).ToArray(); + var path = PathHelper.Combine(segments); + + // If it was an absolute path with no parent. + if (path == @"\\") + { + return null; + } + + return (path != string.Empty) ? new DirectoryPath(path) : null; + } + /// /// Combines the current path with a . /// The provided must be relative. From caf40c0651bfbdfca4a3046059be9118e5b8705d Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Mon, 4 Apr 2022 11:20:31 +0200 Subject: [PATCH 21/27] Added some more tests and fixed the code accordingly. --- .../Unit/IO/DirectoryPathTests.cs | 133 ++++++++++++------ src/Cake.Core/IO/DirectoryPath.cs | 56 ++++++-- src/Cake.Testing.Xunit/WindowsTheory.cs | 6 +- 3 files changed, 139 insertions(+), 56 deletions(-) diff --git a/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs b/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs index d67c0b37b3..79acf04b50 100644 --- a/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs +++ b/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs @@ -257,16 +257,70 @@ public void Can_Not_Combine_Directory_Path_With_Absolute_Windows_Directory_Path( public sealed class TheGetParentMethod { + public sealed class InUncFormat + { + [Theory] + [InlineData(@"\\server\share\folder", @"\\server\share")] + public void Should_Return_Parent_Directory(string directoryPath, string parentPath) + { + // Given + var path = new DirectoryPath(directoryPath); + + // When + var result = path.GetParent(); + + // Then + Assert.Equal(parentPath, result.FullPath); + } + + [Theory] + [InlineData(@"\\Server\")] + [InlineData(@"\\Server")] + [InlineData(@"\\Server\Share")] + public void Should_Return_Null_If_No_Parent(string directoryPath) + { + // Given + var path = new DirectoryPath(directoryPath); + + // When + var result = path.GetParent(); + + // Then + Assert.Equal(null, result); + } + } + public sealed class InRelativeFormat + { + [Theory] + [InlineData("foo\\bar", "foo")] + [InlineData("foo\\bar\\baz\\..\\..\\Work", "foo")] + [InlineData("foo/bar/baz/../../Work", "foo")] + [InlineData("foo/bar", "foo")] + [InlineData("Data\\Work\\..\\foo", "Data")] + [InlineData("Data/Work/../foo", "Data")] + [InlineData("someFolder", ".")] + [InlineData("..", ".")] // a bit unexpected, but due to the way "Collapse" works. + [InlineData("./", ".")] // a bit unexpected, but due to the way "Collapse" works. + public void Should_Return_Parent_Directory(string directoryPath, string parentPath) + { + // Given + var path = new DirectoryPath(directoryPath); + + // When + var result = path.GetParent(); + + // Then + Assert.Equal(parentPath, result.FullPath); + } + } + public sealed class InWindowsFormat { [WindowsTheory] [InlineData("C:/Data", "C:/")] [InlineData("C:/Data/Work", "C:/Data")] [InlineData("C:/Data/Work/file.txt", "C:/Data/Work")] - [InlineData("../../Work", "../..")] - [InlineData("Data/../Work", "Data/..")] - [InlineData(@"\\A\B\C", @"\\A\B")] - [InlineData(@"\\A\B\c.txt", @"\\A\B")] + [InlineData("C:\\folder\\foo\\..", "C:/")] public void Should_Return_Parent_Directory(string directoryPath, string parentPath) { // Given @@ -282,11 +336,7 @@ public void Should_Return_Parent_Directory(string directoryPath, string parentPa [WindowsTheory] [InlineData("C:/")] [InlineData("C:")] - [InlineData(@"\\A\")] - [InlineData(@"\\A")] - [InlineData("..")] - [InlineData("/..")] - [InlineData("/../")] + [InlineData("C:/..")] public void Should_Return_Null_If_No_Parent(string directoryPath) { // Given @@ -296,49 +346,44 @@ public void Should_Return_Null_If_No_Parent(string directoryPath) var result = path.GetParent(); // Then - Assert.Equal(result, null); + Assert.Equal(null, result); } + } - public sealed class InUnixFormat + public sealed class InUnixFormat + { + [WindowsTheory(true)] + [InlineData("/C", "/")] + [InlineData("/C/", "/")] + [InlineData("/C/Data", "/C")] + [InlineData("/C/Data/Work", "/C/Data")] + [InlineData("/C/Data/Work/file.txt", "/C/Data/Work")] + [InlineData("/folder/foo/..", "/")] + [InlineData("/..", ".")] // a bit unexpected, but due to the way "Collapse" works. + public void Should_Return_Parent_Directory(string directoryPath, string parentPath) { - [Theory] - [InlineData("/C/Data", "/C")] - [InlineData("/C/Data/Work", "/C/Data")] - [InlineData("../../Work", "../..")] - [InlineData("Data/../Work", "Data/..")] - [InlineData("/C/Data/Work/file.txt", "/C/Data/Work")] - public void Should_Return_Parent_Directory(string directoryPath, string parentPath) - { - // Given - var path = new DirectoryPath(directoryPath); + // Given + var path = new DirectoryPath(directoryPath); - // When - var result = path.GetParent(); + // When + var result = path.GetParent(); - // Then - Assert.Equal(parentPath, result.FullPath); - } + // Then + Assert.Equal(parentPath, result.FullPath); + } - [Theory] - [InlineData("C")] - [InlineData("/C")] - [InlineData("/C/")] - [InlineData(@"\\A")] - [InlineData(@"\\A\")] - [InlineData("..")] - [InlineData("/..")] - [InlineData("/../")] - public void Should_Return_Null_If_No_Parent(string directoryPath) - { - // Given - var path = new DirectoryPath(directoryPath); + [WindowsTheory(true)] + [InlineData("/")] + public void Should_Return_Null_If_No_Parent(string directoryPath) + { + // Given + var path = new DirectoryPath(directoryPath); - // When - var result = path.GetParent(); + // When + var result = path.GetParent(); - // Then - Assert.Equal(result, null); - } + // Then + Assert.Equal(null, result); } } } diff --git a/src/Cake.Core/IO/DirectoryPath.cs b/src/Cake.Core/IO/DirectoryPath.cs index 3adcd47b3a..79a892143e 100644 --- a/src/Cake.Core/IO/DirectoryPath.cs +++ b/src/Cake.Core/IO/DirectoryPath.cs @@ -7,6 +7,7 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; +using Cake.Core.Polyfill; namespace Cake.Core.IO { @@ -61,16 +62,53 @@ public FilePath GetFilePath(FilePath path) /// A to the parent directory of the given . public DirectoryPath GetParent() { - var segments = Segments.Take(Segments.Length - 1).ToArray(); - var path = PathHelper.Combine(segments); + var collapsed = this.Collapse(); - // If it was an absolute path with no parent. - if (path == @"\\") + if (collapsed.Segments.Length == 0) { return null; } - return (path != string.Empty) ? new DirectoryPath(path) : null; + if (IsUNC && collapsed.FullPath.StartsWith("//")) + { + // workaround for GH-3859 + collapsed = new DirectoryPath(collapsed.FullPath.Replace("/", "\\")); + } + + if (collapsed.IsUNC && collapsed.Segments.Length < 4) + { + // UNC is special: \\server\share makes 3 (!) Segments + // Also, \\server\share simply has no parent + return null; + } + + if (collapsed.Segments.Length == 1) + { + if (collapsed.IsRelative) + { + // something like "relativeFolder/", whose parent is simply "." + return new DirectoryPath("."); + } + + // one segment on Windows is e.g. "C:/" + // on all other systems one segment is e.g "/home" + if (EnvironmentHelper.GetPlatformFamily() == PlatformFamily.Windows) + { + // no more parents + return null; + } + + // root ("/") is not really a segment for Cake, + // so we return that directly. + return new DirectoryPath("/"); + } + + var segments = collapsed.Segments.Take(collapsed.Segments.Length - 1); + var fullPath = collapsed.IsUNC + ? @"\\" + string.Join(Separator.ToString(), segments.Skip(1)) + : string.Join(Separator.ToString(), segments); + + return new DirectoryPath(fullPath); } /// @@ -204,7 +242,7 @@ public FilePath GetRelativePath(FilePath to) } /// - /// Determines wheter two instances are equal. + /// Determines whether two instances are equal. /// /// the to compare. /// True if other is equal to current object, False otherwise. @@ -212,7 +250,7 @@ public bool Equals(DirectoryPath other) => PathComparer.Default.Equals(this, other); /// - /// Determines wheter two instances are equal. + /// Determines whether two instances are equal. /// /// the to compare. /// True if other is equal to current object, False otherwise. @@ -220,7 +258,7 @@ public override bool Equals(object other) => Equals(other as DirectoryPath); /// - /// Determines wheter two instances are equal. + /// Determines whether two instances are equal. /// /// left side . /// right side . @@ -230,7 +268,7 @@ public override bool Equals(object other) || directoryPath?.Equals(otherDirectoryPath) == true; /// - /// Determines wheter two instances are different. + /// Determines whether two instances are different. /// /// left side . /// right side . diff --git a/src/Cake.Testing.Xunit/WindowsTheory.cs b/src/Cake.Testing.Xunit/WindowsTheory.cs index 8f1091fcab..26d6b19494 100644 --- a/src/Cake.Testing.Xunit/WindowsTheory.cs +++ b/src/Cake.Testing.Xunit/WindowsTheory.cs @@ -18,11 +18,11 @@ static WindowsTheory() } // ReSharper disable once UnusedParameter.Local - public WindowsTheory(string reason = null) + public WindowsTheory(bool invert = false, string reason = null) { - if (_family != PlatformFamily.Windows) + if ((_family != PlatformFamily.Windows) ^ invert) { - Skip = reason ?? "Windows test."; + Skip = reason ?? (invert ? "Non-Windows test" : "Windows test."); } } } From 8f563cbb4d626bfbe9d9ad6b51d717328a83f447 Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Tue, 5 Apr 2022 21:22:36 +0200 Subject: [PATCH 22/27] Converted the WindowsTheory from an class with an optional invert-parameter that was hard to read into a baseClass and two subClasses: WindowsTheoryAttribute and NonWindowsTheoryAttribute --- .../Unit/IO/DirectoryPathTests.cs | 4 +- .../NonWindowsTheoryAttribute.cs | 18 +++++++ .../PlatformRestrictedTheoryAttribute.cs | 52 +++++++++++++++++++ src/Cake.Testing.Xunit/WindowsTheory.cs | 29 ----------- .../WindowsTheoryAttribute.cs | 18 +++++++ 5 files changed, 90 insertions(+), 31 deletions(-) create mode 100644 src/Cake.Testing.Xunit/NonWindowsTheoryAttribute.cs create mode 100644 src/Cake.Testing.Xunit/PlatformRestrictedTheoryAttribute.cs delete mode 100644 src/Cake.Testing.Xunit/WindowsTheory.cs create mode 100644 src/Cake.Testing.Xunit/WindowsTheoryAttribute.cs diff --git a/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs b/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs index 79acf04b50..b402c440ea 100644 --- a/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs +++ b/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs @@ -352,7 +352,7 @@ public void Should_Return_Null_If_No_Parent(string directoryPath) public sealed class InUnixFormat { - [WindowsTheory(true)] + [NonWindowsTheory] [InlineData("/C", "/")] [InlineData("/C/", "/")] [InlineData("/C/Data", "/C")] @@ -372,7 +372,7 @@ public void Should_Return_Parent_Directory(string directoryPath, string parentPa Assert.Equal(parentPath, result.FullPath); } - [WindowsTheory(true)] + [NonWindowsTheory] [InlineData("/")] public void Should_Return_Null_If_No_Parent(string directoryPath) { diff --git a/src/Cake.Testing.Xunit/NonWindowsTheoryAttribute.cs b/src/Cake.Testing.Xunit/NonWindowsTheoryAttribute.cs new file mode 100644 index 0000000000..ce2cc692bb --- /dev/null +++ b/src/Cake.Testing.Xunit/NonWindowsTheoryAttribute.cs @@ -0,0 +1,18 @@ +// 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 Cake.Core; + +namespace Cake.Testing.Xunit +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public sealed class NonWindowsTheoryAttribute : PlatformRestrictedTheoryAttribute + { + public NonWindowsTheoryAttribute(string reason = null) + : base(PlatformFamily.Windows, true, reason) + { + } + } +} \ No newline at end of file diff --git a/src/Cake.Testing.Xunit/PlatformRestrictedTheoryAttribute.cs b/src/Cake.Testing.Xunit/PlatformRestrictedTheoryAttribute.cs new file mode 100644 index 0000000000..fd5bdab5e1 --- /dev/null +++ b/src/Cake.Testing.Xunit/PlatformRestrictedTheoryAttribute.cs @@ -0,0 +1,52 @@ +// 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 Cake.Core; +using Cake.Core.Polyfill; +using Xunit; + +namespace Cake.Testing.Xunit +{ + public abstract class PlatformRestrictedTheoryAttribute : TheoryAttribute + { + private static readonly PlatformFamily _family; + private string _skip; + + static PlatformRestrictedTheoryAttribute() + { + _family = EnvironmentHelper.GetPlatformFamily(); + } + + protected PlatformRestrictedTheoryAttribute( + PlatformFamily requiredFamily, + bool invert, + string reason = null) + { + if ((requiredFamily != _family) ^ invert) + { + if (string.IsNullOrEmpty(reason)) + { + var platformName = Enum.GetName(typeof(PlatformFamily), requiredFamily); + if (invert) + { + platformName = $"Non-{platformName}"; + } + + reason = $"{platformName} test."; + } + + Reason = reason; + } + } + + private string Reason { get; } + + public override string Skip + { + get => _skip ?? Reason; + set => _skip = value; + } + } +} \ No newline at end of file diff --git a/src/Cake.Testing.Xunit/WindowsTheory.cs b/src/Cake.Testing.Xunit/WindowsTheory.cs deleted file mode 100644 index 26d6b19494..0000000000 --- a/src/Cake.Testing.Xunit/WindowsTheory.cs +++ /dev/null @@ -1,29 +0,0 @@ -// 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.Core; -using Cake.Core.Polyfill; -using Xunit; - -namespace Cake.Testing.Xunit -{ - public sealed class WindowsTheory : TheoryAttribute - { - private static readonly PlatformFamily _family; - - static WindowsTheory() - { - _family = EnvironmentHelper.GetPlatformFamily(); - } - - // ReSharper disable once UnusedParameter.Local - public WindowsTheory(bool invert = false, string reason = null) - { - if ((_family != PlatformFamily.Windows) ^ invert) - { - Skip = reason ?? (invert ? "Non-Windows test" : "Windows test."); - } - } - } -} \ No newline at end of file diff --git a/src/Cake.Testing.Xunit/WindowsTheoryAttribute.cs b/src/Cake.Testing.Xunit/WindowsTheoryAttribute.cs new file mode 100644 index 0000000000..4baf2082b0 --- /dev/null +++ b/src/Cake.Testing.Xunit/WindowsTheoryAttribute.cs @@ -0,0 +1,18 @@ +// 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 Cake.Core; + +namespace Cake.Testing.Xunit +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public sealed class WindowsTheoryAttribute : PlatformRestrictedTheoryAttribute + { + public WindowsTheoryAttribute(string reason = null) + : base(PlatformFamily.Windows, false, reason) + { + } + } +} \ No newline at end of file From c9cb03495669b081414da9046ab02c4f3a5dc07c Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Wed, 6 Apr 2022 13:14:28 +0200 Subject: [PATCH 23/27] (GH-2099) Fix and add script cache integration test * relates to #2099 --- tests/integration/Cake/ScriptCache.cake | 37 ++++++++++++++++--- .../Cake/ScriptCache/Config/build.cake | 1 + .../Cake/ScriptCache/Config/cake.config | 2 + 3 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 tests/integration/resources/Cake/ScriptCache/Config/build.cake create mode 100644 tests/integration/resources/Cake/ScriptCache/Config/cake.config diff --git a/tests/integration/Cake/ScriptCache.cake b/tests/integration/Cake/ScriptCache.cake index 22d51fb6b7..f97268f064 100644 --- a/tests/integration/Cake/ScriptCache.cake +++ b/tests/integration/Cake/ScriptCache.cake @@ -7,9 +7,14 @@ public class ScriptCacheData public FilePath ScriptPath { get; } public FilePath ScriptCacheAssemblyPath { get; } public FilePath ScriptCacheHashPath { get; } + public FilePath ConfigScriptPath { get; } + public DirectoryPath ConfigScriptCachePath { get; } + public FilePath ConfigScriptCacheAssemblyPath { get; } + public FilePath ConfigScriptCacheHashPath { get; } public (TimeSpan Elapsed, string Hash) CompileResult { get; set; } public (TimeSpan Elapsed, string Hash) ExecuteResult { get; set; } public (TimeSpan Elapsed, string Hash) ReCompileResult { get; set; } + public (TimeSpan Elapsed, string Hash) ConfigCompileResult { get; set; } public CakeSettings Settings { get; } private Action CakeExecuteScript { get; } private Func CalculateFileHash { get; } @@ -28,15 +33,16 @@ public class ScriptCacheData return stopwatch.Elapsed; } - public (TimeSpan Elapsed, string Hash) TimeCakeExecuteScript() => TimeCakeExecuteScript(args => args); + public (TimeSpan Elapsed, string Hash) TimeCakeExecuteScript(FilePath scriptPath = null) + => TimeCakeExecuteScript(args => args, scriptPath); - public (TimeSpan Elapsed, string Hash) TimeCakeExecuteScript(Func argumentCustomization) => + public (TimeSpan Elapsed, string Hash) TimeCakeExecuteScript(Func argumentCustomization, FilePath scriptPath = null) => ( Time( () => { Settings.ArgumentCustomization = argumentCustomization; CakeExecuteScript( - ScriptPath, + scriptPath ?? ScriptPath, Settings); }), CalculateFileHash(ScriptCacheAssemblyPath).ToHex() @@ -52,9 +58,17 @@ public class ScriptCacheData var cacheDirectoryPath = scriptDirectoryPath.Combine("tools").Combine("cache"); ScriptCacheAssemblyPath = cacheDirectoryPath.CombineWithFilePath("build.cake.dll"); ScriptCacheHashPath = cacheDirectoryPath.CombineWithFilePath("build.cake.hash"); + var configScriptDirectoryPath = scriptDirectoryPath.Combine("Config"); + ConfigScriptPath = configScriptDirectoryPath.CombineWithFilePath("build.cake"); + var configCacheRootPath = configScriptDirectoryPath.Combine("CacheRootPath"); + ConfigScriptCachePath = configCacheRootPath.Combine("cake-build").Combine("CacheLeafPath"); + ConfigScriptCacheAssemblyPath = ConfigScriptCachePath.CombineWithFilePath("build.cake.dll"); + ConfigScriptCacheHashPath = ConfigScriptCachePath.CombineWithFilePath("build.cake.hash"); Settings = new CakeSettings { EnvironmentVariables = new Dictionary { - { "CAKE_SETTINGS_ENABLESCRIPTCACHE", "true" } + { "CAKE_SETTINGS_ENABLESCRIPTCACHE", "true" }, + { "TEST_ROOT_PATH", configCacheRootPath.FullPath }, + { "TEST_LEAF_PATH", "CacheLeafPath" } }, Verbosity = Verbosity.Quiet }; @@ -98,7 +112,7 @@ Task("Cake.ScriptCache.Compile") // Then Assert.True(FileExists(data.ScriptCacheAssemblyPath), $"Script Cache Assembly Path {data.ScriptCacheAssemblyPath} missing."); - Assert.True(FileExists(data.ScriptCacheAssemblyPath), $"Script Cache Hash Path {data.ScriptCacheHashPath} missing."); + Assert.True(FileExists(data.ScriptCacheHashPath), $"Script Cache Hash Path {data.ScriptCacheHashPath} missing."); }); var scriptCacheExecute = Task("Cake.ScriptCache.Execute"); @@ -129,8 +143,19 @@ Task("Cake.ScriptCache.ReCompile") Assert.NotEqual(data.CompileResult.Hash , data.ReCompileResult.Hash); }); +Task("Cake.ScriptCache.Config") + .Does((context, data) => { + // Given / When + data.ConfigCompileResult = data.TimeCakeExecuteScript(data.ConfigScriptPath); + + // Then + Assert.True(FileExists(data.ConfigScriptCacheAssemblyPath), $"Script Cache Assembly Path {data.ConfigScriptCacheAssemblyPath} missing."); + Assert.True(FileExists(data.ConfigScriptCacheHashPath), $"Script Cache Hash Path {data.ConfigScriptCacheHashPath} missing."); + }); + Task("Cake.ScriptCache") .IsDependentOn("Cake.ScriptCache.Setup") .IsDependentOn("Cake.ScriptCache.Compile") .IsDependentOn("Cake.ScriptCache.Execute") - .IsDependentOn("Cake.ScriptCache.ReCompile"); \ No newline at end of file + .IsDependentOn("Cake.ScriptCache.ReCompile") + .IsDependentOn("Cake.ScriptCache.Config"); \ No newline at end of file diff --git a/tests/integration/resources/Cake/ScriptCache/Config/build.cake b/tests/integration/resources/Cake/ScriptCache/Config/build.cake new file mode 100644 index 0000000000..e98d44e8e9 --- /dev/null +++ b/tests/integration/resources/Cake/ScriptCache/Config/build.cake @@ -0,0 +1 @@ +Information("Hello from compiled script!"); diff --git a/tests/integration/resources/Cake/ScriptCache/Config/cake.config b/tests/integration/resources/Cake/ScriptCache/Config/cake.config new file mode 100644 index 0000000000..af28129718 --- /dev/null +++ b/tests/integration/resources/Cake/ScriptCache/Config/cake.config @@ -0,0 +1,2 @@ +[Paths] +Cache=%TEST_ROOT_PATH%/cake-build/%TEST_LEAF_PATH% \ No newline at end of file From 1cb1692534a2424a8ef1d7acde786ecb4db0c6fb Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Mon, 4 Apr 2022 23:22:22 +0200 Subject: [PATCH 24/27] (#3858, #3859) Made PathCollapser aware of different formats especially different calculations of the root of a path for windows, non-windows and UNC like paths. To this end, I re-structured the unit test. There were no test removed that previously existed, but only new tests added. Also, instead of joining together the path with one specific separator ('/'), I re-used the existing separator of the original path. (This was the main-fix for #3859.) --- .../Unit/IO/DirectoryPathTests.cs | 2 +- .../Unit/IO/PathCollapserTests.cs | 248 ++++++++++++------ src/Cake.Core/IO/DirectoryPath.cs | 6 - src/Cake.Core/IO/PathCollapser.cs | 45 +++- .../NonWindowsFactAttribute.cs | 18 ++ .../PlatformRestrictedFactAttribute.cs | 52 ++++ 6 files changed, 274 insertions(+), 97 deletions(-) create mode 100644 src/Cake.Testing.Xunit/NonWindowsFactAttribute.cs create mode 100644 src/Cake.Testing.Xunit/PlatformRestrictedFactAttribute.cs diff --git a/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs b/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs index b402c440ea..8ba9a9ea81 100644 --- a/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs +++ b/src/Cake.Core.Tests/Unit/IO/DirectoryPathTests.cs @@ -359,7 +359,6 @@ public sealed class InUnixFormat [InlineData("/C/Data/Work", "/C/Data")] [InlineData("/C/Data/Work/file.txt", "/C/Data/Work")] [InlineData("/folder/foo/..", "/")] - [InlineData("/..", ".")] // a bit unexpected, but due to the way "Collapse" works. public void Should_Return_Parent_Directory(string directoryPath, string parentPath) { // Given @@ -374,6 +373,7 @@ public void Should_Return_Parent_Directory(string directoryPath, string parentPa [NonWindowsTheory] [InlineData("/")] + [InlineData("/..")] public void Should_Return_Null_If_No_Parent(string directoryPath) { // Given diff --git a/src/Cake.Core.Tests/Unit/IO/PathCollapserTests.cs b/src/Cake.Core.Tests/Unit/IO/PathCollapserTests.cs index 8287931256..f5ef71dd09 100644 --- a/src/Cake.Core.Tests/Unit/IO/PathCollapserTests.cs +++ b/src/Cake.Core.Tests/Unit/IO/PathCollapserTests.cs @@ -22,103 +22,177 @@ public void Should_Throw_If_Path_Is_Null() AssertEx.IsArgumentNullException(result, "path"); } - [Fact] - public void Should_Collapse_Relative_Path() - { - // Given, When - var path = PathCollapser.Collapse(new DirectoryPath("hello/temp/test/../../world")); - - // Then - Assert.Equal("hello/world", path); - } - - [Fact] - public void Should_Collapse_Path_With_Separated_Ellipsis() + public sealed class WithPathsInRelativeFormat { - // Given, When - var path = PathCollapser.Collapse(new DirectoryPath("hello/temp/../temp2/../world")); - - // Then - Assert.Equal("hello/world", path); + [Fact] + public void Should_Collapse_Relative_Path() + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath("hello/temp/test/../../world")); + + // Then + Assert.Equal("hello/world", path); + } + + [Fact] + public void Should_Collapse_Path_With_Separated_Ellipsis() + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath("hello/temp/../temp2/../world")); + + // Then + Assert.Equal("hello/world", path); + } + + [Theory] + [InlineData("./foo/..", ".")] + [InlineData("foo/..", ".")] + public void Should_Collapse_To_Dot_When_Only_One_Folder_Is_Followed_By_Ellipsis(string input, + string expected) + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath(input)); + + // Then + Assert.Equal(expected, path); + } + + [Theory] + [InlineData(".")] + [InlineData("./")] + [InlineData("")] + public void Should_Collapse_Single_Dot_To_Single_Dot(string uncollapsedPath) + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath(uncollapsedPath)); + + // Then + Assert.Equal(".", path); + } + + [Fact] + public void Should_Collapse_Single_Dot_With_Ellipsis() + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath("./..")); + + // Then + Assert.Equal(".", path); + } + + [Theory] + [InlineData("./a", "a")] + [InlineData("a/./b", "a/b")] + [InlineData("a/b/.", "a/b")] + public void Should_Collapse_Single_Dot(string uncollapsedPath, string collapsedPath) + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath(uncollapsedPath)); + + // Then + Assert.Equal(collapsedPath, path); + } } - [WindowsFact] - public void Should_Collapse_Path_With_Windows_Root() + public sealed class WithPathsInUncFormat { - // Given, When - var path = PathCollapser.Collapse(new DirectoryPath("c:/hello/temp/test/../../world")); - - // Then - Assert.Equal("c:/hello/world", path); + [Theory] + [InlineData(@"\\server\share\folder\..", @"\\server\share")] + [InlineData(@"\\server\share\folder\..\..\..\..", @"\\server")] + [InlineData(@"\\server\share\folder\..\..\..\..\foo", @"\\server\foo")] + public void Should_Collapse_Ellipsis(string input, + string expected) + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath(input)); + + // Then + Assert.Equal(expected, path); + } } - [Fact] - public void Should_Collapse_Path_With_Non_Windows_Root() + public sealed class WithPathsInNonWindowsFormat { - // Given, When - var path = PathCollapser.Collapse(new DirectoryPath("/hello/temp/test/../../world")); - - // Then - Assert.Equal("/hello/world", path); + [Fact] + public void Should_Collapse_Path_With_Non_Windows_Root() + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath("/hello/temp/test/../../world")); + + // Then + Assert.Equal("/hello/world", path); + } + + [NonWindowsFact] + public void Should_Stop_Collapsing_When_Root_Is_Reached() + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath("/hello/../../../../../../temp")); + + // Then + Assert.Equal("/temp", path); + } + + [NonWindowsTheory] + [InlineData("/foo/..", "/")] + [InlineData("/..", "/")] + public void Should_Collapse_To_Root_When_Only_One_Folder_Is_Followed_By_Ellipsis(string input, + string expected) + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath(input)); + + // Then + Assert.Equal(expected, path); + } + + [Theory] + [InlineData("/a/./b", "/a/b")] + [InlineData("/a/b/.", "/a/b")] + [InlineData("/./a/b", "/a/b")] + public void Should_Collapse_Single_Dot(string uncollapsedPath, string collapsedPath) + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath(uncollapsedPath)); + + // Then + Assert.Equal(collapsedPath, path); + } } - [WindowsFact] - public void Should_Stop_Collapsing_When_Windows_Root_Is_Reached() + public sealed class WithPathsInWindowsFormat { - // Given, When - var path = PathCollapser.Collapse(new DirectoryPath("c:/../../../../../../temp")); - - // Then - Assert.Equal("c:/temp", path); - } - - [Fact] - public void Should_Stop_Collapsing_When_Root_Is_Reached() - { - // Given, When - var path = PathCollapser.Collapse(new DirectoryPath("/hello/../../../../../../temp")); - - // Then - Assert.Equal("/temp", path); - } - - [Theory] - [InlineData(".")] - [InlineData("./")] - [InlineData("/.")] - public void Should_Collapse_Single_Dot_To_Single_Dot(string uncollapsedPath) - { - // Given, When - var path = PathCollapser.Collapse(new DirectoryPath(uncollapsedPath)); - - // Then - Assert.Equal(".", path); - } - - [Fact] - public void Should_Collapse_Single_Dot_With_Ellipsis() - { - // Given, When - var path = PathCollapser.Collapse(new DirectoryPath("./..")); - - // Then - Assert.Equal(".", path); - } - - [Theory] - [InlineData("./a", "a")] - [InlineData("a/./b", "a/b")] - [InlineData("/a/./b", "/a/b")] - [InlineData("a/b/.", "a/b")] - [InlineData("/a/b/.", "/a/b")] - [InlineData("/./a/b", "/a/b")] - public void Should_Collapse_Single_Dot(string uncollapsedPath, string collapsedPath) - { - // Given, When - var path = PathCollapser.Collapse(new DirectoryPath(uncollapsedPath)); - - // Then - Assert.Equal(collapsedPath, path); + [Fact] + public void Should_Collapse_Path_With_Windows_Root() + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath("c:/hello/temp/test/../../world")); + + // Then + Assert.Equal("c:/hello/world", path); + } + + [WindowsFact] + public void Should_Stop_Collapsing_When_Windows_Root_Is_Reached() + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath("c:/../../../../../../temp")); + + // Then + Assert.Equal("c:/temp", path); + } + + [Theory] + [InlineData("C:/foo/..", "C:")] + public void Should_Collapse_To_Root_When_Only_One_Folder_Is_Followed_By_Ellipsis(string input, + string expected) + { + // Given, When + var path = PathCollapser.Collapse(new DirectoryPath(input)); + + // Then + Assert.Equal(expected, path); + } } } } diff --git a/src/Cake.Core/IO/DirectoryPath.cs b/src/Cake.Core/IO/DirectoryPath.cs index 79a892143e..5be5f29a7b 100644 --- a/src/Cake.Core/IO/DirectoryPath.cs +++ b/src/Cake.Core/IO/DirectoryPath.cs @@ -69,12 +69,6 @@ public DirectoryPath GetParent() return null; } - if (IsUNC && collapsed.FullPath.StartsWith("//")) - { - // workaround for GH-3859 - collapsed = new DirectoryPath(collapsed.FullPath.Replace("/", "\\")); - } - if (collapsed.IsUNC && collapsed.Segments.Length < 4) { // UNC is special: \\server\share makes 3 (!) Segments diff --git a/src/Cake.Core/IO/PathCollapser.cs b/src/Cake.Core/IO/PathCollapser.cs index b4c3e5e311..5e86198729 100644 --- a/src/Cake.Core/IO/PathCollapser.cs +++ b/src/Cake.Core/IO/PathCollapser.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Cake.Core.Polyfill; namespace Cake.Core.IO { @@ -16,8 +17,31 @@ public static string Collapse(Path path) { throw new ArgumentNullException(nameof(path)); } + + var isUncPath = path.IsUNC; + var isWindowsPlatform = EnvironmentHelper.IsWindows(EnvironmentHelper.GetPlatformFamily()); + var minStackHeight = 0; var stack = new Stack(); var segments = path.FullPath.Split('/', '\\'); + if (!path.IsRelative) + { + if (isUncPath) + { + // first two segments are string.Empty, followed by server and share + minStackHeight = 3; + } + else if (isWindowsPlatform) + { + // first segment is c: + minStackHeight = 1; + } + else + { + // first segment is string.Empty + minStackHeight = 1; + } + } + foreach (var segment in segments) { if (segment == ".") @@ -26,7 +50,7 @@ public static string Collapse(Path path) } if (segment == "..") { - if (stack.Count > 1) + if (stack.Count > minStackHeight) { stack.Pop(); } @@ -34,8 +58,23 @@ public static string Collapse(Path path) } stack.Push(segment); } - string collapsed = string.Join("/", stack.Reverse()); - return collapsed == string.Empty ? "." : collapsed; + var collapsed = string.Join(path.Separator.ToString(), stack.Reverse()); + if (collapsed != string.Empty) + { + return collapsed; + } + + if (path.IsRelative) + { + return "."; + } + + if (isUncPath) + { + return @"\\"; + } + + return isWindowsPlatform ? path.Segments[0] : "/"; } } } \ No newline at end of file diff --git a/src/Cake.Testing.Xunit/NonWindowsFactAttribute.cs b/src/Cake.Testing.Xunit/NonWindowsFactAttribute.cs new file mode 100644 index 0000000000..64d1c7269f --- /dev/null +++ b/src/Cake.Testing.Xunit/NonWindowsFactAttribute.cs @@ -0,0 +1,18 @@ +// 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 Cake.Core; + +namespace Cake.Testing.Xunit +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public sealed class NonWindowsFactAttribute : PlatformRestrictedFactAttribute + { + public NonWindowsFactAttribute(string reason = null) + : base(PlatformFamily.Windows, true, reason) + { + } + } +} \ No newline at end of file diff --git a/src/Cake.Testing.Xunit/PlatformRestrictedFactAttribute.cs b/src/Cake.Testing.Xunit/PlatformRestrictedFactAttribute.cs new file mode 100644 index 0000000000..43ebfffa27 --- /dev/null +++ b/src/Cake.Testing.Xunit/PlatformRestrictedFactAttribute.cs @@ -0,0 +1,52 @@ +// 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 Cake.Core; +using Cake.Core.Polyfill; +using Xunit; + +namespace Cake.Testing.Xunit +{ + public abstract class PlatformRestrictedFactAttribute : FactAttribute + { + private static readonly PlatformFamily _family; + private string _skip; + + static PlatformRestrictedFactAttribute() + { + _family = EnvironmentHelper.GetPlatformFamily(); + } + + protected PlatformRestrictedFactAttribute( + PlatformFamily requiredFamily, + bool invert, + string reason = null) + { + if ((requiredFamily != _family) ^ invert) + { + if (string.IsNullOrEmpty(reason)) + { + var platformName = Enum.GetName(typeof(PlatformFamily), requiredFamily); + if (invert) + { + platformName = $"Non-{platformName}"; + } + + reason = $"{platformName} test."; + } + + Reason = reason; + } + } + + private string Reason { get; } + + public override string Skip + { + get => _skip ?? Reason; + set => _skip = value; + } + } +} \ No newline at end of file From 734cfce2553477a5e3909551678a458901609553 Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Thu, 14 Apr 2022 22:22:30 +0200 Subject: [PATCH 25/27] (GH-3866) Update Microsoft.NETCore.Platforms 6.0.3 * fixes #3866 --- src/Cake.NuGet/Cake.NuGet.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cake.NuGet/Cake.NuGet.csproj b/src/Cake.NuGet/Cake.NuGet.csproj index 735f98791b..863c0d1c02 100644 --- a/src/Cake.NuGet/Cake.NuGet.csproj +++ b/src/Cake.NuGet/Cake.NuGet.csproj @@ -29,7 +29,7 @@ - + All From 7ae36b91c680b57ee2b0daee0ac052ac122b4023 Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Thu, 14 Apr 2022 22:47:12 +0200 Subject: [PATCH 26/27] (GH-3868) Update .NET SDK to 6.0.202 * fixes #3868 --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 1beb945b15..65c27ed7be 100644 --- a/global.json +++ b/global.json @@ -3,7 +3,7 @@ "src" ], "sdk": { - "version": "6.0.201", + "version": "6.0.202", "rollForward": "latestFeature" } } \ No newline at end of file From f38bb05ea68c1f3f8853c15d47a5d26a39d5a034 Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Fri, 15 Apr 2022 00:35:14 +0200 Subject: [PATCH 27/27] (build) Updated version and release notes --- ReleaseNotes.md | 19 +++++++++++++++++++ src/SolutionInfo.cs | 6 +++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index cafe213900..3695d197c3 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,3 +1,22 @@ +### New in 2.2.0 (Released 2022/04/15) + +* 3821 PostAction is not setable on DotNetSettings. +* 3485 Add alias for dotnet workload search command. +* 2099 Cache compiled script on disk. +* 3866 Update Microsoft.NETCore.Platforms to 6.0.3. +* 3854 Update Spectre.Console to 0.44.0. +* 3851 Update System.Reflection.Metadata to 6.0.1. +* 3846 Update Microsoft.CodeAnalysis.CSharp.Scripting to 4.1.0. +* 3844 Update Microsoft.NETCore.Platforms to 6.0.2. +* 3843 Update NuGet.* to 6.1.0. +* 2763 Provide property to return parent directory on DirectoryPath. +* 2431 UploadFile should support option of username/password. +* 3819 Update Git Release Manager Comment template to remove Cake NuGet package and Chocolatey portable. +* 3859 PathCollapser.Collapse breaks UNC paths. +* 3858 PathCollapser.Collapse shows wrong output for if .. is the second segment in the path. +* 3823 Executing a cake script leads to System.IO.FileNotFoundException for several System.(...) assemblies. +* 3735 Incorrect warnings in diagnostic logs. + ### New in 2.1.0 (Released 2022/02/19) * 2524 XmlTransform support for xsl arguments diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index d022b6a603..9ebd693775 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -10,7 +10,7 @@ using System.Reflection; [assembly: AssemblyProduct("Cake")] -[assembly: AssemblyVersion("2.1.0.0")] -[assembly: AssemblyFileVersion("2.1.0.0")] -[assembly: AssemblyInformationalVersion("2.1.0-beta.1+0.Branch.release-2.1.0.Sha.d21c59a7546b49e37c063982b85b9281dec0183a")] +[assembly: AssemblyVersion("2.2.0.0")] +[assembly: AssemblyFileVersion("2.2.0.0")] +[assembly: AssemblyInformationalVersion("2.2.0-beta.1+0.Branch.release-2.2.0.Sha.8861b463a4eb651f9e5149ee7f7ada47d03ae1ad")] [assembly: AssemblyCopyright("Copyright (c) .NET Foundation and Contributors")]