From 0327c522d3c7803047936e2713d17bc1142b4338 Mon Sep 17 00:00:00 2001 From: Mattias Karlsson Date: Tue, 16 Dec 2025 22:46:17 +0100 Subject: [PATCH] Migrate to Cake SDK 6.0.0 and update build infrastructure - Migrate from Cake script-based build to Cake SDK 6.0.0 - Convert build helpers and records from .cake to .cs files - Remove dotnet-tools.json manifest (tools now installed via Cake SDK) - Update GitHub Actions workflow to use NuGet/login action for authentication - Update Cake action to use file-path instead of tool-manifest - Update Microsoft.Extensions.* package versions to 10.0.1/10.1.0 --- .config/dotnet-tools.json | 20 -- .github/workflows/build.yml | 10 +- build/helpers.cake | 56 ----- build/helpers.cs | 39 +++ build/{records.cake => records.cs} | 24 +- cake.cs | 233 ++++++++++++++++++ ...evlead.Testing.MockHttp.Dependencies.props | 12 +- src/Directory.Packages.props | 6 +- 8 files changed, 301 insertions(+), 99 deletions(-) delete mode 100644 .config/dotnet-tools.json delete mode 100644 build/helpers.cake create mode 100644 build/helpers.cs rename build/{records.cake => records.cs} (76%) create mode 100644 cake.cs diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json deleted file mode 100644 index 800d86f..0000000 --- a/.config/dotnet-tools.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "cake.tool": { - "version": "6.0.0", - "commands": [ - "dotnet-cake" - ], - "rollForward": false - }, - "dpi": { - "version": "2025.11.25.337", - "commands": [ - "dpi" - ], - "rollForward": false - } - } -} \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 08df980..53e7b67 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,6 +38,12 @@ jobs: with: global-json-file: global.json + - name: NuGet Login + uses: NuGet/login@v1 + id: login + with: + user: ${{ secrets.NUGET_USER }} + - name: Run Cake script uses: cake-build/cake-action@v3 env: @@ -45,7 +51,7 @@ jobs: NuGetReportSettings_WorkspaceId: ${{ secrets.NUGETREPORTSETTINGS_WORKSPACEID }} GH_PACKAGES_NUGET_SOURCE: ${{ secrets.GH_PACKAGES_NUGET_SOURCE }} NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} + NUGET_APIKEY: ${{ steps.login.outputs.NUGET_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} @@ -54,5 +60,5 @@ jobs: AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE_ACCOUNT }} AZURE_STORAGE_ACCOUNT_CONTAINER: ${{ secrets.AZURE_STORAGE_ACCOUNT_CONTAINER }}${{ matrix.os }} with: - cake-version: tool-manifest + file-path: cake.cs target: GitHub-Actions diff --git a/build/helpers.cake b/build/helpers.cake deleted file mode 100644 index a3c84f4..0000000 --- a/build/helpers.cake +++ /dev/null @@ -1,56 +0,0 @@ -#addin "nuget:?package=xunit.assert&version=2.9.3" -#load "records.cake" - -// Usings -using Xunit; - -/***************************** - * Helpers - *****************************/ - -private static ExtensionHelper extensionHelper; -extensionHelper = new ExtensionHelper(Task, () => RunTarget(Argument("target", "Default"))); -public static CakeTaskBuilder Then(this CakeTaskBuilder cakeTaskBuilder, string name) - => extensionHelper - .TaskCreate(name) - .IsDependentOn(cakeTaskBuilder); - - -public static CakeReport Run(this CakeTaskBuilder cakeTaskBuilder) - => extensionHelper.Run(); - -public static CakeTaskBuilder Default(this CakeTaskBuilder cakeTaskBuilder) -{ - extensionHelper - .TaskCreate("Default") - .IsDependentOn(cakeTaskBuilder); - return cakeTaskBuilder; -} - -if (BuildSystem.GitHubActions.IsRunningOnGitHubActions) -{ - TaskSetup(context=> System.Console.WriteLine($"::group::{context.Task.Name.Quote()}")); - TaskTeardown(context=>System.Console.WriteLine("::endgroup::")); -} - -public class FilePathJsonConverter : PathJsonConverter -{ - protected override FilePath ConvertFromString(string value) => FilePath.FromString(value); -} - -public abstract class PathJsonConverter : System.Text.Json.Serialization.JsonConverter where TPath : Cake.Core.IO.Path -{ - public override TPath Read(ref System.Text.Json.Utf8JsonReader reader, Type typeToConvert, System.Text.Json.JsonSerializerOptions options) - { - var value = reader.GetString(); - - return value is null ? null : ConvertFromString(value); - } - - public override void Write(System.Text.Json.Utf8JsonWriter writer, TPath value, System.Text.Json.JsonSerializerOptions options) - { - writer.WriteStringValue(value.FullPath); - } - - protected abstract TPath ConvertFromString(string value); -} \ No newline at end of file diff --git a/build/helpers.cs b/build/helpers.cs new file mode 100644 index 0000000..6029c27 --- /dev/null +++ b/build/helpers.cs @@ -0,0 +1,39 @@ +/***************************** + * Helpers + *****************************/ + +public partial class Program +{ + static void Main_SetupExtensions() + { + if (BuildSystem.GitHubActions.IsRunningOnGitHubActions) + { + TaskSetup(context => BuildSystem.GitHubActions.Commands.StartGroup(context.Task.Name)); + TaskTeardown(context => BuildSystem.GitHubActions.Commands.EndGroup()); + } + } +} + + +public static partial class CakeTaskBuilderExtensions +{ + private static ExtensionHelper extensionHelper = new (Task, () => RunTarget(Argument("target", "Default"))); + + public static CakeTaskBuilder Then(this CakeTaskBuilder cakeTaskBuilder, string name) + => extensionHelper + .TaskCreate(name) + .IsDependentOn(cakeTaskBuilder); + + + public static CakeReport Run(this CakeTaskBuilder cakeTaskBuilder) + => extensionHelper.Run(); + + public static CakeTaskBuilder Default(this CakeTaskBuilder cakeTaskBuilder) + { + extensionHelper + .TaskCreate("Default") + .IsDependentOn(cakeTaskBuilder); + return cakeTaskBuilder; + } + +} \ No newline at end of file diff --git a/build/records.cake b/build/records.cs similarity index 76% rename from build/records.cake rename to build/records.cs index 51554d7..102816b 100644 --- a/build/records.cake +++ b/build/records.cs @@ -1,4 +1,3 @@ -#load "helpers.cake" using System.Text.Json.Serialization; /***************************** @@ -22,15 +21,15 @@ DirectoryPath OutputPath public DirectoryPath BinaryOutputPath { get; } = OutputPath.Combine("bin"); public DirectoryPath IntegrationTestPath { get; } = OutputPath.Combine(IntegrationTest); - public string GitHubNuGetSource { get; } = System.Environment.GetEnvironmentVariable("GH_PACKAGES_NUGET_SOURCE"); - public string GitHubNuGetApiKey { get; } = System.Environment.GetEnvironmentVariable("GITHUB_TOKEN"); + public string? GitHubNuGetSource { get; } = System.Environment.GetEnvironmentVariable("GH_PACKAGES_NUGET_SOURCE"); + public string? GitHubNuGetApiKey { get; } = System.Environment.GetEnvironmentVariable("GITHUB_TOKEN"); public bool ShouldPushGitHubPackages() => !ShouldNotPublish && !string.IsNullOrWhiteSpace(GitHubNuGetSource) && !string.IsNullOrWhiteSpace(GitHubNuGetApiKey); - public string NuGetSource { get; } = System.Environment.GetEnvironmentVariable("NUGET_SOURCE"); - public string NuGetApiKey { get; } = System.Environment.GetEnvironmentVariable("NUGET_APIKEY"); + public string? NuGetSource { get; } = System.Environment.GetEnvironmentVariable("NUGET_SOURCE"); + public string? NuGetApiKey { get; } = System.Environment.GetEnvironmentVariable("NUGET_APIKEY"); public bool ShouldPushNuGetPackages() => IsMainBranch && !ShouldNotPublish && !string.IsNullOrWhiteSpace(NuGetSource) && @@ -49,9 +48,9 @@ public bool ShouldPushNuGetPackages() => IsMainBranch && System.Environment.GetEnvironmentVariable("AZURE_AUTHORITY_HOST") ); - public string AzureStorageAccount { get; } = System.Environment.GetEnvironmentVariable("AZURE_STORAGE_ACCOUNT"); + public string? AzureStorageAccount { get; } = System.Environment.GetEnvironmentVariable("AZURE_STORAGE_ACCOUNT"); - public string AzureStorageAccountContainer { get; } = System.Environment.GetEnvironmentVariable("AZURE_STORAGE_ACCOUNT_CONTAINER"); + public string? AzureStorageAccountContainer { get; } = System.Environment.GetEnvironmentVariable("AZURE_STORAGE_ACCOUNT_CONTAINER"); public bool ShouldRunIntegrationTests() => !string.IsNullOrWhiteSpace(AzureStorageAccount) && !string.IsNullOrWhiteSpace(AzureStorageAccountContainer) && @@ -62,10 +61,10 @@ public bool ShouldRunIntegrationTests() => !string.IsNullOrWhiteSpace(AzureStor } public record AzureCredentials( - string TenantId, - string ClientId, - string ClientSecret, - string AuthorityHost = "login.microsoftonline.com" + string? TenantId, + string? ClientId, + string? ClientSecret, + string? AuthorityHost = "login.microsoftonline.com" ) { public bool AzureCredentialsSpecified { get; } = !string.IsNullOrWhiteSpace(TenantId) && @@ -73,4 +72,5 @@ public record AzureCredentials( !string.IsNullOrWhiteSpace(ClientSecret) && !string.IsNullOrWhiteSpace(AuthorityHost); } -private record ExtensionHelper(Func TaskCreate, Func Run); +internal record ExtensionHelper(Func TaskCreate, Func Run); + diff --git a/cake.cs b/cake.cs new file mode 100644 index 0000000..1196844 --- /dev/null +++ b/cake.cs @@ -0,0 +1,233 @@ +#:sdk Cake.Sdk@6.0.0 +#:property IncludeAdditionalFiles=./build/*.cs + + +/***************************** + * Setup + *****************************/ +Setup( + static context => { + InstallTool("dotnet:https://api.nuget.org/v3/index.json?package=DPI&version=2025.11.25.337"); + InstallTool("dotnet:https://api.nuget.org/v3/index.json?package=GitVersion.Tool&version=6.5.1"); + var assertedVersions = context.GitVersion(new GitVersionSettings + { + OutputType = GitVersionOutput.Json + }); + + var branchName = assertedVersions.BranchName; + var isMainBranch = StringComparer.OrdinalIgnoreCase.Equals("main", branchName); + + var buildDate = DateTime.UtcNow; + var runNumber = GitHubActions.IsRunningOnGitHubActions + ? GitHubActions.Environment.Workflow.RunNumber + : 0; + + var suffix = runNumber == 0 + ? $"-{(short)((buildDate - buildDate.Date).TotalSeconds/3)}" + : string.Empty; + + var version = FormattableString + .Invariant($"{buildDate:yyyy.M.d}.{runNumber}{suffix}"); + + context.Information("Building version {0} (Branch: {1}, IsMain: {2})", + version, + branchName, + isMainBranch); + + var artifactsPath = context + .MakeAbsolute(context.Directory("./artifacts")); + + var projectRoot = context + .MakeAbsolute(context.Directory("./src")); + + var projectPath = projectRoot.CombineWithFilePath("Devlead.Testing.MockHttp/Devlead.Testing.MockHttp.csproj"); + + return new BuildData( + version, + isMainBranch, + !context.IsRunningOnWindows(), + BuildSystem.IsLocalBuild, + projectRoot, + projectPath, + new DotNetMSBuildSettings() + .SetConfiguration("Release") + .SetVersion(version) + .WithProperty("Copyright", $"Mattias Karlsson © {DateTime.UtcNow.Year}") + .WithProperty("Authors", "devlead") + .WithProperty("Company", "devlead") + .WithProperty("PackageLicenseExpression", "MIT") + .WithProperty("PackageTags", "testing;http") + .WithProperty("PackageDescription", ".NET Library for mocking HTTP client requests") + .WithProperty("RepositoryUrl", "https://github.com/devlead/Devlead.Testing.MockHttp.git") + .WithProperty("ContinuousIntegrationBuild", GitHubActions.IsRunningOnGitHubActions ? "true" : "false") + .WithProperty("EmbedUntrackedSources", "true"), + artifactsPath, + artifactsPath.Combine(version) + ); + } +); + +/***************************** + * Tasks + *****************************/ +Task("Clean") + .Does( + static (context, data) => context.CleanDirectories(data.DirectoryPathsToClean) + ) +.Then("Restore") + .Does( + static (context, data) => context.DotNetRestore( + data.ProjectRoot.FullPath, + new DotNetRestoreSettings { + MSBuildSettings = data.MSBuildSettings + } + ) + ) +.Then("DPI") + .Does( + static (context, data) => Command( + ["dpi", "dpi.exe"], + new ProcessArgumentBuilder() + .Append("nuget") + .Append("--silent") + .AppendSwitchQuoted("--output", "table") + .Append( + ( + !string.IsNullOrWhiteSpace(context.EnvironmentVariable("NuGetReportSettings_SharedKey")) + && + !string.IsNullOrWhiteSpace(context.EnvironmentVariable("NuGetReportSettings_WorkspaceId")) + ) + ? "report" + : "analyze" + ) + .AppendSwitchQuoted("--buildversion", data.Version) + + ) + ) +.Then("Build") + .Does( + static (context, data) => context.DotNetBuild( + data.ProjectRoot.FullPath, + new DotNetBuildSettings { + NoRestore = true, + MSBuildSettings = data.MSBuildSettings + } + ) + ) +.Then("Test") + .Does( + static (context, data) => context.DotNetTest( + data.ProjectRoot.FullPath, + new DotNetTestSettings { + NoBuild = true, + NoRestore = true, + MSBuildSettings = data.MSBuildSettings + } + ) + ) +.Then("Pack") + .Does( + static (context, data) => context.DotNetPack( + data.ProjectPath.FullPath, + new DotNetPackSettings { + NoBuild = true, + NoRestore = true, + OutputDirectory = data.NuGetOutputPath, + MSBuildSettings = data.MSBuildSettings + } + ) + ) +.Then("Upload-Artifacts") + .WithCriteria( (context, data) => data.ShouldPushGitHubPackages()) + .Does( + static (context, data) => GitHubActions + .Commands + .UploadArtifact(data.ArtifactsPath, "artifacts") + ) +.Then("Prepare-Integration-Test") + .Does( + static (context, data) => { + context.CopyDirectory(data.ProjectRoot.Combine("Devlead.Testing.MockHttp.Tests"), data.IntegrationTestPath); + context.CopyFile(data.ProjectRoot.CombineWithFilePath("Directory.Packages.props"), data.IntegrationTestPath.CombineWithFilePath("Directory.Packages.props")); + context.CopyFile("nuget.config", data.IntegrationTestPath.CombineWithFilePath("nuget.config")); + context.DotNetAddPackage( + "Devlead.Testing.MockHttp", + new DotNetPackageAddSettings { + EnvironmentVariables = { { "Configuration", "IntegrationTest" } }, + WorkingDirectory = data.IntegrationTestPath, + Version = data.Version, + Source = data.NuGetOutputPath.FullPath + } + ); + } + ) +.Then("Integration-Test") + .Does( + static (context, data) => { + context.DotNetTest( + data.IntegrationTestPath.FullPath, + new DotNetTestSettings { + Configuration = "IntegrationTest" + } + ); + } + ) + .Default() +.Then("Push-GitHub-Packages") + .WithCriteria( (context, data) => data.ShouldPushGitHubPackages()) + .DoesForEach( + static (data, context) + => context.GetFiles(data.NuGetOutputPath.FullPath + "/*.nupkg"), + static (data, item, context) + => context.DotNetNuGetPush( + item.FullPath, + new DotNetNuGetPushSettings + { + Source = data.GitHubNuGetSource, + ApiKey = data.GitHubNuGetApiKey + } + ) + ) +.Then("Push-NuGet-Packages") + .WithCriteria( (context, data) => data.ShouldPushNuGetPackages()) + .DoesForEach( + static (data, context) + => context.GetFiles(data.NuGetOutputPath.FullPath + "/*.nupkg"), + static (data, item, context) + => context.DotNetNuGetPush( + item.FullPath, + new DotNetNuGetPushSettings + { + Source = data.NuGetSource, + ApiKey = data.NuGetApiKey + } + ) + ) +.Then("Create-GitHub-Release") + .WithCriteria( (context, data) => data.ShouldPushNuGetPackages()) + .Does( + static (context, data) => context + .Command( + new CommandSettings { + ToolName = "GitHub CLI", + ToolExecutableNames = new []{ "gh.exe", "gh" }, + EnvironmentVariables = { { "GH_TOKEN", data.GitHubNuGetApiKey } } + }, + new ProcessArgumentBuilder() + .Append("release") + .Append("create") + .Append(data.Version) + .AppendSwitchQuoted("--title", data.Version) + .Append("--generate-notes") + .Append(string.Join( + ' ', + context + .GetFiles(data.NuGetOutputPath.FullPath + "/*.nupkg") + .Select(path => path.FullPath.Quote()) + )) + + ) + ) +.Then("GitHub-Actions") +.Run(); + diff --git a/src/Devlead.Testing.MockHttp/Devlead.Testing.MockHttp.Dependencies.props b/src/Devlead.Testing.MockHttp/Devlead.Testing.MockHttp.Dependencies.props index e5c9486..5c742a1 100644 --- a/src/Devlead.Testing.MockHttp/Devlead.Testing.MockHttp.Dependencies.props +++ b/src/Devlead.Testing.MockHttp/Devlead.Testing.MockHttp.Dependencies.props @@ -39,13 +39,13 @@ - - + + - - + + - - + + \ No newline at end of file diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 9bb3003..19376bf 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -8,9 +8,9 @@ - - - + + +