diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 5921b1057..6d88f3aa6 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,13 +3,13 @@ "isRoot": true, "tools": { "cake.tool": { - "version": "6.0.0", + "version": "6.1.0", "commands": [ "dotnet-cake" ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.5.1", + "version": "5.5.4", "commands": [ "reportgenerator" ], diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 04c5aa61f..f0b15ccca 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -168,7 +168,8 @@ jobs: - name: Cake Build uses: cake-build/cake-action@v3 with: - target: LatestFramework + target: PullRequest + verbosity: Verbose - name: Coverage files run: ./.github/steps/prepare-coveralls.sh - name: Coveralls diff --git a/ReleaseNotes.md b/ReleaseNotes.md index fa2169d28..9b4ac27f7 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -2,15 +2,12 @@ Tag to substitute: {0} https://www.nuget.org/packages/Ocelot/{0} --> -## Pre-release for .NET 10 SDK (version [25.0](https://www.nuget.org/packages/Ocelot/#versions-body-tab) Beta 1) +## Pre-release 2 for [.NET 10](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) SDK (version [{0}](https://www.nuget.org/packages/Ocelot/{0})) > Milestone: [.NET 10](https://github.com/ThreeMammals/Ocelot/milestone/13) -### :information_source: About -This is a pre-release for the [.NET 10](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) SDK 10.0.103. -Most features function as usual, with a minor warning for developers and teams who utilize [service discovery](https://github.com/ThreeMammals/Ocelot/blob/develop/docs/features/servicediscovery.rst) via [Kubernetes](https://github.com/ThreeMammals/Ocelot/blob/develop/docs/features/kubernetes.rst). +This is **Pre-release 2** for the [.NET 10](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) SDK. -### :warning: Warning -1. The [Ocelot.Provider.Kubernetes](https://www.nuget.org/packages/Ocelot.Provider.Kubernetes) extension package is under development. Specifically, the [PollKube](https://github.com/ThreeMammals/Ocelot/blob/develop/docs/features/kubernetes.rst#pollkube-provider-3) provider is unstable since it is still in development. -Do not upgrade to the current beta version or use it at your own risk. Other [Kubernetes](https://github.com/ThreeMammals/Ocelot/blob/develop/docs/features/kubernetes.rst) providers, such as [Kube](https://github.com/ThreeMammals/Ocelot/blob/develop/docs/features/kubernetes.rst#kube-provider) and [WatchKube](https://github.com/ThreeMammals/Ocelot/blob/develop/docs/features/kubernetes.rst#watchkube-provider-4), should function correctly. +Version [{0}](https://www.nuget.org/packages/Ocelot/{0}) includes upgraded solutions and [NuGet packages](https://www.nuget.org/profiles/ThreeMammals) based on .NET SDK [10.0.201](https://dotnet.microsoft.com/en-us/download/dotnet/10.0), released on March 12, 2026. +For more details about SDK [10.0.201](https://dotnet.microsoft.com/en-us/download/dotnet/10.0), see the [Release notes](https://github.com/dotnet/core/blob/main/release-notes/10.0/10.0.5/10.0.5.md). -2. The [Ocelot.Provider.Eureka](https://www.nuget.org/packages/Ocelot.Provider.Eureka) extension package is under development. The integrated [Steeltoe.Discovery.Eureka](https://www.nuget.org/packages/Steeltoe.Discovery.Eureka) package requires an upgrade to version 4.1.0. +Development teams can start migrating their Ocelot-based projects using this [Beta 2](https://www.nuget.org/packages/Ocelot/{0}) release to upgrade to the .NET 10 SDK with Long-Term Support (LTS). diff --git a/build.cake b/build.cake index 10c628703..248100e75 100644 --- a/build.cake +++ b/build.cake @@ -1,8 +1,9 @@ -#tool dotnet:?package=GitVersion.Tool&version=6.5.1 -#tool nuget:?package=ReportGenerator&version=5.5.1 +#tool dotnet:?package=GitVersion.Tool&version=6.6.2 +#tool nuget:?package=ReportGenerator&version=5.5.4 +#addin nuget:?package=Cake.Http #addin nuget:?package=Newtonsoft.Json&version=13.0.4 // Switch to a MS lib! -#addin nuget:?package=System.Text.Encodings.Web&version=10.0.2 +#addin nuget:?package=System.Text.Encodings.Web&version=10.0.5 #r "Spectre.Console" using Spectre.Console; @@ -12,11 +13,15 @@ using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; +using _File_ = System.IO.File; +using _Directory_ = System.IO.Directory; bool IsTechnicalRelease = false; const string Release = "Release"; // task name, target, and Release config name -const string AllFrameworks = "net8.0;net9.0;net10.0"; -const string LatestFramework = "net10.0"; +const string PullRequest = "PullRequest"; // task name, target, and PullRequest config name +const string LatestFramework = "LatestFramework"; // task name, target, and LatestFramework config name +const string AllTFMs = "net8.0;net9.0;net10.0"; +const string LatestTFM = "net10.0"; string NL = Environment.NewLine; // Create a CultureInfo object for UK English @@ -68,6 +73,7 @@ GitVersion versioning = null; var target = Argument("target", "Default"); var slnFile = "./Ocelot.slnx"; + Information($"{NL}Target: {target}"); Information($"Build: {compileConfig}"); Information($"Solution: {slnFile}"); @@ -82,6 +88,8 @@ Task("Build") .IsDependentOn("Tests"); Task("LatestFramework") .IsDependentOn("Tests"); +Task("PullRequest") + .IsDependentOn("Tests"); Task("ReleaseNotes") .IsDependentOn("CreateReleaseNotes"); @@ -124,11 +132,11 @@ Task("Compile") Configuration = compileConfig, NoRestore = true, }; - if (target == "LatestFramework") + if (target == LatestFramework || target == PullRequest) { - settings.Framework = LatestFramework; // build using .NET 10 SDK only + settings.Framework = LatestTFM; // build using .NET 10 SDK only } - string frameworkInfo = string.IsNullOrEmpty(settings.Framework) ? AllFrameworks : settings.Framework; + string frameworkInfo = string.IsNullOrEmpty(settings.Framework) ? AllTFMs : settings.Framework; Information($"Settings {nameof(DotNetBuildSettings.Framework)}: {frameworkInfo}"); Information($"Settings {nameof(DotNetBuildSettings.Configuration)}: {settings.Configuration}"); DotNetBuild(slnFile, settings); @@ -203,7 +211,7 @@ Task("CreateReleaseNotes") // Read main header from Git file, substitute version in header, and add content further... Information("{0} New release tag is " + releaseVersion); Information("{1} Last release tag is " + lastRelease); - var body = System.IO.File.ReadAllText("./ReleaseNotes.md", System.Text.Encoding.UTF8); + var body = _File_.ReadAllText("./ReleaseNotes.md", System.Text.Encoding.UTF8); var releaseHeader = string.Format(body, releaseVersion, lastRelease); releaseNotes = new List { releaseHeader }; if (IsTechnicalRelease) @@ -486,22 +494,22 @@ private void WriteReleaseNotes() { Information($"RUN {nameof(WriteReleaseNotes)} ..."); EnsureDirectoryExists(packagesDir); - System.IO.File.WriteAllLines(releaseNotesFile, releaseNotes, Encoding.UTF8); - var content = System.IO.File.ReadAllText(releaseNotesFile, Encoding.UTF8); + _File_.WriteAllLines(releaseNotesFile, releaseNotes, Encoding.UTF8); + var content = _File_.ReadAllText(releaseNotesFile, Encoding.UTF8); if (string.IsNullOrEmpty(content)) { - System.IO.File.WriteAllText(releaseNotesFile, "No commits since last release", System.Text.Encoding.UTF8); + _File_.WriteAllText(releaseNotesFile, "No commits since last release", System.Text.Encoding.UTF8); } Information("Release notes are >>>{0}<<<", NL + content); } private List GetTFMs() { - var tfms = AllFrameworks.Split(';').ToList(); - if (target == "LatestFramework" || target == "UnitTests" || target == "Release") + var tfms = AllTFMs.Split(';').ToList(); + if (target == LatestFramework || target == "UnitTests" || target == Release || target == PullRequest) { tfms.Clear(); - tfms.Add(LatestFramework); + tfms.Add(LatestTFM); } return tfms; } @@ -595,7 +603,7 @@ Task("CreateArtifacts") .Does(() => { WriteReleaseNotes(); - System.IO.File.AppendAllLines(artifactsFile, new[] { "ReleaseNotes.md" }); + _File_.AppendAllLines(artifactsFile, new[] { "ReleaseNotes.md" }); if (!IsTechnicalRelease) { @@ -603,14 +611,14 @@ Task("CreateArtifacts") var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg"); foreach(var projectFile in projectFiles) { - System.IO.File.AppendAllLines( + _File_.AppendAllLines( artifactsFile, new[] { projectFile.GetFilename().FullPath } ); } } - var artifacts = System.IO.File.ReadAllLines(artifactsFile) + var artifacts = _File_.ReadAllLines(artifactsFile) .Distinct(); Information($"Listing all {nameof(artifacts)}..."); @@ -718,7 +726,7 @@ private void PreprocessReadMe() const string RTD_Version_Latest = "[ReadTheDocs](https://readthedocs.org/projects/ocelot/badge/?version=latest&style=flat-square)"; const string RTD_Version_Develop = "[ReadTheDocs](https://readthedocs.org/projects/ocelot/badge/?version=develop&style=flat-square)"; Information($"Processing {READMEmd} ..."); - var body = System.IO.File.ReadAllText(READMEmd, System.Text.Encoding.UTF8); + var body = _File_.ReadAllText(READMEmd, System.Text.Encoding.UTF8); var RTD_IsReplaced = false; if (body.Contains(RTD_Version_Latest)) { @@ -746,13 +754,13 @@ private void PreprocessReadMe() Information($" {READMEmd}: Octocat HTML IMG-tag has been replaced with -> " + IMG_NuGet_Valid_MD); } Information($" {READMEmd}: Writing the body of the {READMEmd}..."); - System.IO.File.WriteAllText(READMEmd, body, System.Text.Encoding.UTF8); + _File_.WriteAllText(READMEmd, body, System.Text.Encoding.UTF8); Information($"DONE Processing {READMEmd}{NL}"); } private void GenerateReport(Cake.Core.IO.FilePath coverageSummaryFile) { - var dir = System.IO.Directory.GetCurrentDirectory(); + var dir = _Directory_.GetCurrentDirectory(); Information("GenerateReport: Current directory: " + dir); var reportSettings = new ProcessArgumentBuilder(); @@ -790,10 +798,10 @@ private void PersistVersion(string committedVersion, string newVersion) var file = projectFile.ToString(); Information(string.Format("Updating {0}...", file)); - var updatedProjectFile = System.IO.File.ReadAllText(file, System.Text.Encoding.UTF8) + var updatedProjectFile = _File_.ReadAllText(file, System.Text.Encoding.UTF8) .Replace(committedVersion, newVersion); - System.IO.File.WriteAllText(file, updatedProjectFile, System.Text.Encoding.UTF8); + _File_.WriteAllText(file, updatedProjectFile, System.Text.Encoding.UTF8); } } @@ -801,9 +809,7 @@ private void PersistVersion(string committedVersion, string newVersion) private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl) { Information($"{nameof(PublishPackages)}: Publishing to NuGet..."); - var artifacts = System.IO.File - .ReadAllLines(artifactsFile) - .Distinct(); + var artifacts = _File_.ReadAllLines(artifactsFile).Distinct(); var skippable = new List { "ReleaseNotes.md", // skip always @@ -822,27 +828,72 @@ private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFi }; foreach (var artifact in artifacts) { - if (skippable.Exists(x => artifact.StartsWith(x))) + if (skippable.Exists(x => artifact.StartsWith(x + "."))) continue; - //if (!includedInTheRelease.Exists(x => artifact.StartsWith(x))) - //continue; - - var codePackage = packagesDir + File(artifact); - Information($"{nameof(PublishPackages)}: Pushing package " + codePackage + "..."); - try + // if (!includedInTheRelease.Exists(x => artifact.StartsWith(x))) continue; + + var package = packagesDir + File(artifact); + Information($"{nameof(PublishPackages)}: Pushing package " + package + "..."); + bool isBeta = versioning.NuGetVersion.Contains("-beta.") && artifact.Contains("-beta."); + bool published = false; + int attempts = 0; + string theArtifact = new(artifact); + while (!published && attempts < 9) { - DotNetNuGetPush(codePackage, - new DotNetNuGetPushSettings { ApiKey = feedApiKey, Source = codeFeedUrl, SkipDuplicate = true }); - } - catch (Exception ex) - { - Information("--------------------------------------------------------------"); - Warning(ex.ToString()); - throw; // exit task with non-zero result -> failed step -> failed job in Actions + try + { + attempts++; + DotNetNuGetPush(package, + new DotNetNuGetPushSettings { ApiKey = feedApiKey, Source = codeFeedUrl, SkipDuplicate = !isBeta }); + published = true; + } + catch (Exception ex) + { + Warning(ex.ToString()); + if (!isBeta) throw; // exit task with non-zero result -> failed step -> failed job in Actions + + var match = Regex.Match(theArtifact, @"-beta\.(\d+)(?=\.nupkg$)"); + if (!match.Success) + { + Warning(" No beta version found in the artifact name, but it should be there. Artifact: " + theArtifact); + break; + } + var betaNumber = match.Groups[1].Value; + Information($"Beta number: {betaNumber}"); + int newBetaVer = int.Parse(betaNumber) + 1; // increase beta version by 1 trying to find the next free beta number + var newArtifact = Regex.Replace(theArtifact, @"-beta\.\d+(?=\.nupkg$)", "-beta." + newBetaVer); + var newPackage = packagesDir + File(newArtifact); + if (FileExists(newPackage)) + { + DeleteFile(newPackage); + } + MoveFile(package, newPackage); + Warning($" Package renamed: {package} → {newPackage} (Attempt #{attempts})"); + package = newPackage; + theArtifact = newArtifact; + System.Threading.Thread.Sleep(1000); + } } } } +private bool PackageExists(string packageId, string version) +{ + var url = $"https://api.nuget.org/v3-flatcontainer/{packageId.ToLowerInvariant()}/{version}/{packageId.ToLowerInvariant()}.{version}.nupkg"; + try + { + var response = HttpGet(url); + Information($"{nameof(PackageExists)}: Package ver.{packageId}.{version} exists"); + return true; + } + catch (Exception ex) + { + Warning(ex.ToString()); + Information($"{nameof(PackageExists)}: Package ver.{packageId}.{version} does NOT exist"); + } + return false; +} + private void SetupGitHubClient(System.Net.Http.HttpClient client) { string token = Environment.GetEnvironmentVariable("OCELOT_GITHUB_API_KEY"); @@ -877,13 +928,13 @@ private dynamic CreateGitHubRelease() private string ReleaseNotesAsJson() { - var body = System.IO.File.ReadAllText(releaseNotesFile, System.Text.Encoding.UTF8); + var body = _File_.ReadAllText(releaseNotesFile, System.Text.Encoding.UTF8); return System.Text.Encodings.Web.JavaScriptEncoder.Default.Encode(body); } private void UploadFileToGitHubRelease(dynamic release, FilePath file) { - var data = System.IO.File.ReadAllBytes(file.FullPath); + var data = _File_.ReadAllBytes(file.FullPath); var content = new System.Net.Http.ByteArrayContent(data); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");