From 9c3bb65ac1d59af05333f8e48efc50a81a94fd99 Mon Sep 17 00:00:00 2001 From: Nick Josevski Date: Fri, 7 Aug 2026 15:42:29 +1000 Subject: [PATCH 1/9] Build on the .NET 10 SDK (product code still net8) .NET 8 reaches end of support on 10 November 2026. Moving the SDK and moving the product target framework are two separate risks. Doing both together makes a failure hard to attribute. This commit moves only the toolchain. global.json goes to SDK 10.0.302. The Nuke build project targets net10.0. Frameworks.cs gains Net100 constants alongside the existing ones. The .NET 10 SDK audits transitive packages during restore. Auditing surfaced vulnerable dependencies reached through Octopus.Nuke.Common. TreatWarningsAsErrors turns those advisories into build errors. Fixed versions are pinned rather than the audit being suppressed. Signing.cs suppresses SYSLIB0057 at one call site. X509CertificateLoader only loads certificate files. No replacement exists for reading an Authenticode signature out of a signed PE. Verified: source/Calamari.sln builds at 164 warnings and 0 errors, identical to the pre-change baseline. Co-Authored-By: Claude Opus 5 (1M context) --- build/Frameworks.cs | 2 ++ build/Signing.cs | 5 +++++ build/_build.csproj | 7 ++++++- global.json | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/build/Frameworks.cs b/build/Frameworks.cs index f5d02a7a0b..678371913e 100644 --- a/build/Frameworks.cs +++ b/build/Frameworks.cs @@ -6,5 +6,7 @@ public static class Frameworks { public const string Net80 = "net8.0"; public const string Net80Windows = "net8.0-windows"; + public const string Net100 = "net10.0"; + public const string Net100Windows = "net10.0-windows"; } } \ No newline at end of file diff --git a/build/Signing.cs b/build/Signing.cs index 88676b54e0..9f7d0fceff 100644 --- a/build/Signing.cs +++ b/build/Signing.cs @@ -90,7 +90,12 @@ static bool HasAuthenticodeSignature(string filePath) { try { + // SYSLIB0057 points at X509CertificateLoader, but that only loads certificate *files*. + // There is no replacement for reading the Authenticode signature embedded in a signed + // PE file, so this API is still the only way to do this check. +#pragma warning disable SYSLIB0057 X509Certificate.CreateFromSignedFile(filePath); +#pragma warning restore SYSLIB0057 return true; } catch diff --git a/build/_build.csproj b/build/_build.csproj index 1c2f2a7e5c..22f3e7567a 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net10.0 win-x64;linux-x64;linux-arm;linux-arm64 Calamari.Build CS0649;CS0169 @@ -25,6 +25,11 @@ + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/global.json b/global.json index 90faf1b627..d99e6aa850 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.419", + "version": "10.0.302", "rollForward": "latestFeature", "allowPrerelease": false } From a22fca29768a5344f1c592b9673101677d05eac1 Mon Sep 17 00:00:00 2001 From: Nick Josevski Date: Fri, 7 Aug 2026 15:42:29 +1000 Subject: [PATCH 2/9] Remove the dead .NET Framework reference assemblies Calamari.csproj still referenced Microsoft.NETFramework.ReferenceAssemblies.net462. That reference has done nothing since net462 was dropped as a target framework in #1669. Narrowed from a larger cleanup. The other framework-provided package references this commit used to remove are removed by #2120 instead, so they are left out here to avoid touching the same lines twice. Co-Authored-By: Claude Opus 5 (1M context) --- source/Calamari/Calamari.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source/Calamari/Calamari.csproj b/source/Calamari/Calamari.csproj index 2dea41e44f..05ab0375b1 100644 --- a/source/Calamari/Calamari.csproj +++ b/source/Calamari/Calamari.csproj @@ -31,10 +31,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - From f2a11e58adacf76348f96dc6683eed3cde84baa1 Mon Sep 17 00:00:00 2001 From: Nick Josevski Date: Fri, 7 Aug 2026 15:42:29 +1000 Subject: [PATCH 3/9] Upgrade Autofac 4.8.0 -> 9.3.1 Autofac 4.8.0 dates from 2018 and is five majors behind. Octopus Server already runs 9.3.1 on net10. Done while Calamari still targets net8. Autofac 9.3.1 resolves on net8. The DI upgrade is therefore verified against a known-good baseline instead of being entangled with the framework change. Notes for reviewers. ContainerBuilder.Update() is the headline Autofac 5 removal and was never used here. The .Update call sites in this repo belong to LibGit2Sharp. The API surface in use is mainstream and unchanged across the version range. The custom RegisterPrioritisedList extension builds on Meta and WithMetadata. Both APIs are stable. Verified: clean rebuild with no new warnings. 26 tests pass covering the custom registration ordering and flavour resolution. Co-Authored-By: Claude Opus 5 (1M context) --- source/Calamari.Common/Calamari.Common.csproj | 2 +- source/Calamari/Calamari.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Calamari.Common/Calamari.Common.csproj b/source/Calamari.Common/Calamari.Common.csproj index 2978c14e22..33957753eb 100644 --- a/source/Calamari.Common/Calamari.Common.csproj +++ b/source/Calamari.Common/Calamari.Common.csproj @@ -19,7 +19,7 @@ - + diff --git a/source/Calamari/Calamari.csproj b/source/Calamari/Calamari.csproj index 05ab0375b1..3fa2a5a8db 100644 --- a/source/Calamari/Calamari.csproj +++ b/source/Calamari/Calamari.csproj @@ -41,7 +41,7 @@ - + From 1d30ebefaf913a6fb8fa8d766f0e49f2d07b09b7 Mon Sep 17 00:00:00 2001 From: Nick Josevski Date: Fri, 7 Aug 2026 15:42:29 +1000 Subject: [PATCH 4/9] Target net10.0 Flips 41 projects to net10.0 and net10.0-windows. Also updates the build scripts and source/Directory.Build.props. Directory.Build.props compares the target framework as a string. Calamari.AzureWebApp.NetCoreShim stays on net462. Web Deploy is .NET Framework only. OctoVersion.Tool 1.0.50 already ships a net10.0 folder. No tool bump was needed. System.Text.Json and System.Text.Encoding.CodePages are removed here rather than earlier. Both raise NU1510 on net10. Both were genuinely required on net8. .NET 10 audits transitive packages during restore. Auditing surfaced vulnerable dependencies that net8 never reported. The direct references that pulled them in were upgraded rather than the audit being suppressed. WireMock.Net goes to 2.13.0. System.DirectoryServices.AccountManagement goes to 10.0.10. RestSharp is pinned to 112.1.0. Serilog goes to 4.4.0 for Serilog.Extensions.Logging 10.0.0. Certificate loading moves off the constructors obsoleted by SYSLIB0057. All four call sites load .pfx files. X509CertificateLoader.LoadPkcs12FromFile and LoadPkcs12CollectionFromFile are direct equivalents with no behaviour change. AzureWebAppBehaviour suppresses SYSLIB0014 around its ServicePointManager callback and carries a TODO. The callback looks like dead code. Web Deploy runs in a separate net462 child process. A callback registered in the parent cannot affect the child. The behaviour predates this change. Removing security-adjacent code deserves its own reviewed commit. Verified: 171 warnings and 0 errors on net10. build/_build.csproj clean. 24 DI and structured-variable tests pass against net10.0. Co-Authored-By: Claude Opus 5 (1M context) --- build/Build.PackageCalamariProjects.cs | 4 ++-- build/Build.cs | 2 +- build/Frameworks.cs | 2 -- source/Calamari.Aws/Calamari.Aws.csproj | 2 +- source/Calamari.Azure/Calamari.Azure.csproj | 2 +- .../Calamari.AzureAppService.Tests.csproj | 3 +-- .../Calamari.AzureAppService.csproj | 2 +- .../Calamari.AzureResourceGroup.Tests.csproj | 2 +- .../Calamari.AzureResourceGroup.csproj | 2 +- .../Calamari.AzureScripting.Tests.csproj | 2 +- .../Calamari.AzureScripting.csproj | 2 +- .../Calamari.AzureScripting/CalamariCertificateStore.cs | 2 +- .../Calamari.AzureServiceFabric.Tests.csproj | 2 +- .../Calamari.AzureServiceFabric.csproj | 2 +- .../CalamariCertificateStore.cs | 2 +- .../Calamari.AzureWebApp.Tests.csproj | 2 +- source/Calamari.AzureWebApp/AzureWebAppBehaviour.cs | 9 +++++++++ source/Calamari.AzureWebApp/Calamari.AzureWebApp.csproj | 2 +- .../Calamari.CloudAccounts/Calamari.CloudAccounts.csproj | 2 +- source/Calamari.Common/Calamari.Common.csproj | 2 +- .../Calamari.ConsolidateCalamariPackages.Api.csproj | 2 +- .../Calamari.ConsolidateCalamariPackages.Tests.csproj | 2 +- .../Calamari.ConsolidateCalamariPackages.csproj | 2 +- source/Calamari.Contracts/Calamari.Contracts.csproj | 2 +- .../Calamari.DockerCredentialHelper.csproj | 8 +------- .../Calamari.GoogleCloudAccounts.csproj | 2 +- .../Calamari.GoogleCloudScripting.Tests.csproj | 2 +- .../Calamari.GoogleCloudScripting.csproj | 2 +- .../Calamari.Scripting.Tests.csproj | 2 +- source/Calamari.Scripting/Calamari.Scripting.csproj | 2 +- source/Calamari.Shared/Calamari.Shared.csproj | 2 +- .../Calamari.Terraform.Tests.csproj | 2 +- source/Calamari.Terraform/Calamari.Terraform.csproj | 2 +- source/Calamari.Testing/Calamari.Testing.csproj | 5 +++-- source/Calamari.Tests/Calamari.Tests.csproj | 2 +- .../Acme.JsonFileOutput/Acme.JsonFileOutput.csproj | 2 +- .../Deployment/Packages/Acme.Package/Acme.Package.csproj | 2 +- .../Acme.PackageBilingual/Acme.PackageBilingual.csproj | 2 +- .../Deployment/Packages/Acme.Service/Acme.Service.csproj | 2 +- .../Acme.StructuredConfigFiles.csproj | 2 +- .../Packages/Acme.Web.Tests/Acme.Web.Tests.csproj | 2 +- .../Deployment/Packages/Acme.Web/Acme.Web.Nix.csproj | 2 +- .../Deployment/Packages/Acme.Web/Acme.Web.csproj | 2 +- .../Octopus.Sample.AzureCloudService.csproj | 2 +- source/Calamari.Tests/Fixtures/Nginx/NginxFixture.cs | 3 +-- .../Helpers/Certificates/SampleCertificate.cs | 2 +- source/Calamari/Calamari.csproj | 2 +- source/Directory.Build.props | 2 +- 48 files changed, 58 insertions(+), 58 deletions(-) diff --git a/build/Build.PackageCalamariProjects.cs b/build/Build.PackageCalamariProjects.cs index 000ae8b3a9..a8c734802c 100644 --- a/build/Build.PackageCalamariProjects.cs +++ b/build/Build.PackageCalamariProjects.cs @@ -37,7 +37,7 @@ public partial class Build .Select(rid => { //we are making the bold assumption all projects only have a single target framework - var framework = project.GetTargetFrameworks()?.Single() ?? Frameworks.Net80; + var framework = project.GetTargetFrameworks()?.Single() ?? Frameworks.Net100; return new CalamariPackageMetadata(project, framework, rid); })) .ToList(); @@ -109,7 +109,7 @@ public partial class Build DotNetPublish(s => s .SetConfiguration(Configuration) .SetProject(helperProject) - .SetFramework(Frameworks.Net80) + .SetFramework(Frameworks.Net100) .SetRuntime(rid) .SetVersion(NugetVersion.Value) .SetInformationalVersion(OctoVersionInfo.Value?.InformationalVersion) diff --git a/build/Build.cs b/build/Build.cs index b8bc47e337..6f30215822 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -60,7 +60,7 @@ public Build() // Mimic the behaviour of this attribute, but lazily so we don't pay the OctoVersion cost when it isn't needed OctoVersionInfo = new Lazy(() => { - var attribute = new OctoVersionAttribute { BranchMember = nameof(BranchName), Framework = "net8.0"}; + var attribute = new OctoVersionAttribute { BranchMember = nameof(BranchName), Framework = Frameworks.Net100}; // the Attribute does all the work such as calling TeamCity.Instance?.SetBuildNumber for us var version = attribute.GetValue(null!, this); diff --git a/build/Frameworks.cs b/build/Frameworks.cs index 678371913e..427e4920ae 100644 --- a/build/Frameworks.cs +++ b/build/Frameworks.cs @@ -4,8 +4,6 @@ namespace Calamari.Build { public static class Frameworks { - public const string Net80 = "net8.0"; - public const string Net80Windows = "net8.0-windows"; public const string Net100 = "net10.0"; public const string Net100Windows = "net10.0-windows"; } diff --git a/source/Calamari.Aws/Calamari.Aws.csproj b/source/Calamari.Aws/Calamari.Aws.csproj index bf30c58d0c..c2a19af229 100644 --- a/source/Calamari.Aws/Calamari.Aws.csproj +++ b/source/Calamari.Aws/Calamari.Aws.csproj @@ -18,7 +18,7 @@ Calamari.Aws.exe.manifest - net8.0 + net10.0 true diff --git a/source/Calamari.Azure/Calamari.Azure.csproj b/source/Calamari.Azure/Calamari.Azure.csproj index 35b6599c7f..627c21f2e8 100644 --- a/source/Calamari.Azure/Calamari.Azure.csproj +++ b/source/Calamari.Azure/Calamari.Azure.csproj @@ -5,7 +5,7 @@ Library Octopus Deploy Octopus Deploy Pty Ltd - net8.0 + net10.0 true diff --git a/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj b/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj index 12fcc3fea8..b52f72fccd 100644 --- a/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj +++ b/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj @@ -5,7 +5,7 @@ Calamari.AzureAppService.Tests false win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 - net8.0 + net10.0 true @@ -18,7 +18,6 @@ - diff --git a/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj b/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj index dde4314b95..a4d87e9d40 100644 --- a/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj +++ b/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj @@ -8,7 +8,7 @@ Exe win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 NU5104 - net8.0 + net10.0 true diff --git a/source/Calamari.AzureResourceGroup.Tests/Calamari.AzureResourceGroup.Tests.csproj b/source/Calamari.AzureResourceGroup.Tests/Calamari.AzureResourceGroup.Tests.csproj index c8fee844e7..95f4cbbe5a 100644 --- a/source/Calamari.AzureResourceGroup.Tests/Calamari.AzureResourceGroup.Tests.csproj +++ b/source/Calamari.AzureResourceGroup.Tests/Calamari.AzureResourceGroup.Tests.csproj @@ -4,7 +4,7 @@ Calamari.AzureResourceGroup.Tests false win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 - net8.0 + net10.0 true diff --git a/source/Calamari.AzureResourceGroup/Calamari.AzureResourceGroup.csproj b/source/Calamari.AzureResourceGroup/Calamari.AzureResourceGroup.csproj index 3864ecf36b..26b02d850f 100644 --- a/source/Calamari.AzureResourceGroup/Calamari.AzureResourceGroup.csproj +++ b/source/Calamari.AzureResourceGroup/Calamari.AzureResourceGroup.csproj @@ -7,7 +7,7 @@ false false win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 - net8.0 + net10.0 diff --git a/source/Calamari.AzureScripting.Tests/Calamari.AzureScripting.Tests.csproj b/source/Calamari.AzureScripting.Tests/Calamari.AzureScripting.Tests.csproj index eebed3764a..39a9321ca3 100644 --- a/source/Calamari.AzureScripting.Tests/Calamari.AzureScripting.Tests.csproj +++ b/source/Calamari.AzureScripting.Tests/Calamari.AzureScripting.Tests.csproj @@ -6,7 +6,7 @@ enable win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 false - net8.0 + net10.0 true diff --git a/source/Calamari.AzureScripting/Calamari.AzureScripting.csproj b/source/Calamari.AzureScripting/Calamari.AzureScripting.csproj index 2f6fb89ccd..7582e84206 100644 --- a/source/Calamari.AzureScripting/Calamari.AzureScripting.csproj +++ b/source/Calamari.AzureScripting/Calamari.AzureScripting.csproj @@ -8,7 +8,7 @@ win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 true false - net8.0 + net10.0 true diff --git a/source/Calamari.AzureScripting/CalamariCertificateStore.cs b/source/Calamari.AzureScripting/CalamariCertificateStore.cs index d7140ad9d8..c7d2df6be1 100644 --- a/source/Calamari.AzureScripting/CalamariCertificateStore.cs +++ b/source/Calamari.AzureScripting/CalamariCertificateStore.cs @@ -146,7 +146,7 @@ static bool HasPrivateKey(X509Certificate2 certificate2) { try { - var cert = new X509Certificate2(file, (string)null!, flags); + var cert = X509CertificateLoader.LoadPkcs12FromFile(file, null, flags); // ReSharper disable once InvertIf if (!HasPrivateKey(cert) && requirePrivateKey) diff --git a/source/Calamari.AzureServiceFabric.Tests/Calamari.AzureServiceFabric.Tests.csproj b/source/Calamari.AzureServiceFabric.Tests/Calamari.AzureServiceFabric.Tests.csproj index e0942a69eb..7185f57712 100644 --- a/source/Calamari.AzureServiceFabric.Tests/Calamari.AzureServiceFabric.Tests.csproj +++ b/source/Calamari.AzureServiceFabric.Tests/Calamari.AzureServiceFabric.Tests.csproj @@ -4,7 +4,7 @@ Calamari.AzureServiceFabric.Tests Calamari.AzureServiceFabric.Tests false - net8.0-windows + net10.0-windows win-x64 true diff --git a/source/Calamari.AzureServiceFabric/Calamari.AzureServiceFabric.csproj b/source/Calamari.AzureServiceFabric/Calamari.AzureServiceFabric.csproj index d6734fe7e2..8b433793c2 100644 --- a/source/Calamari.AzureServiceFabric/Calamari.AzureServiceFabric.csproj +++ b/source/Calamari.AzureServiceFabric/Calamari.AzureServiceFabric.csproj @@ -5,7 +5,7 @@ true false Exe - net8.0-windows + net10.0-windows win-x64 true diff --git a/source/Calamari.AzureServiceFabric/CalamariCertificateStore.cs b/source/Calamari.AzureServiceFabric/CalamariCertificateStore.cs index 623c4e4a0b..e617160899 100644 --- a/source/Calamari.AzureServiceFabric/CalamariCertificateStore.cs +++ b/source/Calamari.AzureServiceFabric/CalamariCertificateStore.cs @@ -160,7 +160,7 @@ static X509Certificate2 TryLoadCertificate(string file, X509KeyStorageFlags flag { try { - var cert = new X509Certificate2(file, password, flags); + var cert = X509CertificateLoader.LoadPkcs12FromFile(file, password, flags); // ReSharper disable once InvertIf if (!HasPrivateKey(cert) && requirePrivateKey) diff --git a/source/Calamari.AzureWebApp.Tests/Calamari.AzureWebApp.Tests.csproj b/source/Calamari.AzureWebApp.Tests/Calamari.AzureWebApp.Tests.csproj index a251ddc298..891b17b7b7 100644 --- a/source/Calamari.AzureWebApp.Tests/Calamari.AzureWebApp.Tests.csproj +++ b/source/Calamari.AzureWebApp.Tests/Calamari.AzureWebApp.Tests.csproj @@ -2,7 +2,7 @@ Calamari.AzureWebApp.Tests Calamari.AzureWebApp.Tests - net8.0 + net10.0 win-x64 false true diff --git a/source/Calamari.AzureWebApp/AzureWebAppBehaviour.cs b/source/Calamari.AzureWebApp/AzureWebAppBehaviour.cs index 5f18d72172..843db4cbe2 100644 --- a/source/Calamari.AzureWebApp/AzureWebAppBehaviour.cs +++ b/source/Calamari.AzureWebApp/AzureWebAppBehaviour.cs @@ -62,6 +62,14 @@ public async Task Execute(RunningDeployment deployment) RemoteCertificateValidationCallback originalServerCertificateValidationCallback = null; try { + // TODO: this callback appears to be dead code and should be removed or reinstated properly. + // NetCoreWebDeploymentExecutor runs Web Deploy in a separate net462 child process (the + // NetCoreShim), so a ServicePointManager callback registered here cannot affect the TLS + // validation the child performs. That has been true since Web Deploy moved into the shim, + // not something .NET 10 changed - but .NET 10's obsoletion message says these settings no + // longer affect SslStream or HttpClient, which makes it worth resolving deliberately rather + // than deleting as a drive-by in a framework migration. +#pragma warning disable SYSLIB0014 originalServerCertificateValidationCallback = ServicePointManager.ServerCertificateValidationCallback; ServicePointManager.ServerCertificateValidationCallback = WrapperForServerCertificateValidationCallback; @@ -70,6 +78,7 @@ public async Task Execute(RunningDeployment deployment) finally { ServicePointManager.ServerCertificateValidationCallback = originalServerCertificateValidationCallback; +#pragma warning restore SYSLIB0014 } } diff --git a/source/Calamari.AzureWebApp/Calamari.AzureWebApp.csproj b/source/Calamari.AzureWebApp/Calamari.AzureWebApp.csproj index 05304d5f16..6a2510d635 100644 --- a/source/Calamari.AzureWebApp/Calamari.AzureWebApp.csproj +++ b/source/Calamari.AzureWebApp/Calamari.AzureWebApp.csproj @@ -6,7 +6,7 @@ false Exe 8.0 - net8.0 + net10.0 win-x64 true diff --git a/source/Calamari.CloudAccounts/Calamari.CloudAccounts.csproj b/source/Calamari.CloudAccounts/Calamari.CloudAccounts.csproj index 77c55d6a89..7b9474bc6f 100644 --- a/source/Calamari.CloudAccounts/Calamari.CloudAccounts.csproj +++ b/source/Calamari.CloudAccounts/Calamari.CloudAccounts.csproj @@ -2,7 +2,7 @@ Calamari.CloudAccounts - net8.0 + net10.0 Octopus.Calamari.CloudAccounts Calamari.CloudAccounts Octopus Deploy diff --git a/source/Calamari.Common/Calamari.Common.csproj b/source/Calamari.Common/Calamari.Common.csproj index 33957753eb..4794c49f60 100644 --- a/source/Calamari.Common/Calamari.Common.csproj +++ b/source/Calamari.Common/Calamari.Common.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable anycpu false diff --git a/source/Calamari.ConsolidateCalamariPackages.Api/Calamari.ConsolidateCalamariPackages.Api.csproj b/source/Calamari.ConsolidateCalamariPackages.Api/Calamari.ConsolidateCalamariPackages.Api.csproj index 0270115b70..71a8e5c119 100644 --- a/source/Calamari.ConsolidateCalamariPackages.Api/Calamari.ConsolidateCalamariPackages.Api.csproj +++ b/source/Calamari.ConsolidateCalamariPackages.Api/Calamari.ConsolidateCalamariPackages.Api.csproj @@ -3,7 +3,7 @@ Octopus.Calamari.ConsolidatedPackage.Api Octopus.Calamari.ConsolidatedPackage.Api - net8.0 + net10.0 enable enable Octopus Deploy diff --git a/source/Calamari.ConsolidateCalamariPackages.Tests/Calamari.ConsolidateCalamariPackages.Tests.csproj b/source/Calamari.ConsolidateCalamariPackages.Tests/Calamari.ConsolidateCalamariPackages.Tests.csproj index 65dc78396a..3a4af6ae62 100644 --- a/source/Calamari.ConsolidateCalamariPackages.Tests/Calamari.ConsolidateCalamariPackages.Tests.csproj +++ b/source/Calamari.ConsolidateCalamariPackages.Tests/Calamari.ConsolidateCalamariPackages.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 false true diff --git a/source/Calamari.ConsolidateCalamariPackages/Calamari.ConsolidateCalamariPackages.csproj b/source/Calamari.ConsolidateCalamariPackages/Calamari.ConsolidateCalamariPackages.csproj index 84c3745c6c..8a75547337 100644 --- a/source/Calamari.ConsolidateCalamariPackages/Calamari.ConsolidateCalamariPackages.csproj +++ b/source/Calamari.ConsolidateCalamariPackages/Calamari.ConsolidateCalamariPackages.csproj @@ -3,7 +3,7 @@ Octopus.Calamari.ConsolidatedPackage Octopus.Calamari.ConsolidatedPackage - net8.0 + net10.0 true true Octopus Deploy diff --git a/source/Calamari.Contracts/Calamari.Contracts.csproj b/source/Calamari.Contracts/Calamari.Contracts.csproj index 0600ca85cd..165c674019 100644 --- a/source/Calamari.Contracts/Calamari.Contracts.csproj +++ b/source/Calamari.Contracts/Calamari.Contracts.csproj @@ -3,7 +3,7 @@ Octopus.Calamari.Contracts Octopus.Calamari.Contracts - net8.0 + net10.0 enable enable Octopus Deploy diff --git a/source/Calamari.DockerCredentialHelper/Calamari.DockerCredentialHelper.csproj b/source/Calamari.DockerCredentialHelper/Calamari.DockerCredentialHelper.csproj index 347a26eec4..7245c8cf4f 100644 --- a/source/Calamari.DockerCredentialHelper/Calamari.DockerCredentialHelper.csproj +++ b/source/Calamari.DockerCredentialHelper/Calamari.DockerCredentialHelper.csproj @@ -4,7 +4,7 @@ Exe docker-credential-octopus Calamari.DockerCredentialHelper - net8.0 + net10.0 win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 enable true @@ -14,10 +14,4 @@ project reference and produces a non-runnable apphost in the test output. --> - - - - - diff --git a/source/Calamari.GoogleCloudAccounts/Calamari.GoogleCloudAccounts.csproj b/source/Calamari.GoogleCloudAccounts/Calamari.GoogleCloudAccounts.csproj index 012535291a..1250bf088d 100644 --- a/source/Calamari.GoogleCloudAccounts/Calamari.GoogleCloudAccounts.csproj +++ b/source/Calamari.GoogleCloudAccounts/Calamari.GoogleCloudAccounts.csproj @@ -2,7 +2,7 @@ Calamari.GoogleCloudAccounts - net8.0 + net10.0 CS8632 true diff --git a/source/Calamari.GoogleCloudScripting.Tests/Calamari.GoogleCloudScripting.Tests.csproj b/source/Calamari.GoogleCloudScripting.Tests/Calamari.GoogleCloudScripting.Tests.csproj index 7bf01fa582..ba7d2e9537 100644 --- a/source/Calamari.GoogleCloudScripting.Tests/Calamari.GoogleCloudScripting.Tests.csproj +++ b/source/Calamari.GoogleCloudScripting.Tests/Calamari.GoogleCloudScripting.Tests.csproj @@ -5,7 +5,7 @@ win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 false enable - net8.0 + net10.0 true diff --git a/source/Calamari.GoogleCloudScripting/Calamari.GoogleCloudScripting.csproj b/source/Calamari.GoogleCloudScripting/Calamari.GoogleCloudScripting.csproj index 2a2a3858b3..52006b187c 100644 --- a/source/Calamari.GoogleCloudScripting/Calamari.GoogleCloudScripting.csproj +++ b/source/Calamari.GoogleCloudScripting/Calamari.GoogleCloudScripting.csproj @@ -6,7 +6,7 @@ win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 false false - net8.0 + net10.0 CS8632 true diff --git a/source/Calamari.Scripting.Tests/Calamari.Scripting.Tests.csproj b/source/Calamari.Scripting.Tests/Calamari.Scripting.Tests.csproj index 61acd46f81..715f462dcd 100644 --- a/source/Calamari.Scripting.Tests/Calamari.Scripting.Tests.csproj +++ b/source/Calamari.Scripting.Tests/Calamari.Scripting.Tests.csproj @@ -2,7 +2,7 @@ Calamari.Scripting.Tests Calamari.Scripting.Tests - net8.0 + net10.0 win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 false true diff --git a/source/Calamari.Scripting/Calamari.Scripting.csproj b/source/Calamari.Scripting/Calamari.Scripting.csproj index 895a4d21d3..7c72026528 100644 --- a/source/Calamari.Scripting/Calamari.Scripting.csproj +++ b/source/Calamari.Scripting/Calamari.Scripting.csproj @@ -7,7 +7,7 @@ enable win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 true - net8.0 + net10.0 true diff --git a/source/Calamari.Shared/Calamari.Shared.csproj b/source/Calamari.Shared/Calamari.Shared.csproj index 2f4574b9fd..70a63551a5 100644 --- a/source/Calamari.Shared/Calamari.Shared.csproj +++ b/source/Calamari.Shared/Calamari.Shared.csproj @@ -22,7 +22,7 @@ Calamari enable - net8.0 + net10.0 diff --git a/source/Calamari.Terraform.Tests/Calamari.Terraform.Tests.csproj b/source/Calamari.Terraform.Tests/Calamari.Terraform.Tests.csproj index 2fddd8279d..b560613e87 100644 --- a/source/Calamari.Terraform.Tests/Calamari.Terraform.Tests.csproj +++ b/source/Calamari.Terraform.Tests/Calamari.Terraform.Tests.csproj @@ -5,7 +5,7 @@ Calamari.Terraform.Tests win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 false - net8.0 + net10.0 CS8632 true diff --git a/source/Calamari.Terraform/Calamari.Terraform.csproj b/source/Calamari.Terraform/Calamari.Terraform.csproj index f06bb292ec..d348cb526e 100644 --- a/source/Calamari.Terraform/Calamari.Terraform.csproj +++ b/source/Calamari.Terraform/Calamari.Terraform.csproj @@ -6,7 +6,7 @@ false Exe win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 - net8.0 + net10.0 true diff --git a/source/Calamari.Testing/Calamari.Testing.csproj b/source/Calamari.Testing/Calamari.Testing.csproj index 1a774d5dbe..187f0c11df 100644 --- a/source/Calamari.Testing/Calamari.Testing.csproj +++ b/source/Calamari.Testing/Calamari.Testing.csproj @@ -4,7 +4,7 @@ enable https://github.com/OctopusDeploy/Calamari/ Apache-2.0 - net8.0 + net10.0 true Octopus.Calamari.Testing @@ -16,10 +16,11 @@ + - diff --git a/source/Calamari.Tests/Calamari.Tests.csproj b/source/Calamari.Tests/Calamari.Tests.csproj index 908be1896f..bcd6344116 100644 --- a/source/Calamari.Tests/Calamari.Tests.csproj +++ b/source/Calamari.Tests/Calamari.Tests.csproj @@ -10,7 +10,7 @@ win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 - net8.0 + net10.0 true true diff --git a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.JsonFileOutput/Acme.JsonFileOutput.csproj b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.JsonFileOutput/Acme.JsonFileOutput.csproj index a672254824..c3387ff722 100644 --- a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.JsonFileOutput/Acme.JsonFileOutput.csproj +++ b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.JsonFileOutput/Acme.JsonFileOutput.csproj @@ -5,7 +5,7 @@ en-US 1.0.0.0 ACME Corporation - net8.0 + net10.0 Acme.JsonFileOutput Acme.JsonFileOutput https://github.com/OctopusDeploy/Calamari/ diff --git a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Package/Acme.Package.csproj b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Package/Acme.Package.csproj index aa15b8bac5..7f4e210107 100644 --- a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Package/Acme.Package.csproj +++ b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Package/Acme.Package.csproj @@ -5,7 +5,7 @@ en-US 1.0.0.0 ACME Corporation - net8.0 + net10.0 Acme.Package Acme.Package https://github.com/OctopusDeploy/Calamari/ diff --git a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.PackageBilingual/Acme.PackageBilingual.csproj b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.PackageBilingual/Acme.PackageBilingual.csproj index 6da45eedff..7aed0006ba 100644 --- a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.PackageBilingual/Acme.PackageBilingual.csproj +++ b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.PackageBilingual/Acme.PackageBilingual.csproj @@ -5,7 +5,7 @@ en-US 1.0.0.0 ACME Corporation - net8.0 + net10.0 Acme.PackageBilingual Acme.PackageBilingual https://github.com/OctopusDeploy/Calamari/ diff --git a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Service/Acme.Service.csproj b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Service/Acme.Service.csproj index dd5e74eadb..1800276d38 100644 --- a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Service/Acme.Service.csproj +++ b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Service/Acme.Service.csproj @@ -4,7 +4,7 @@ A sample project containing a Windows service. en-US ACME Corporation - net8.0 + net10.0 Acme.Service Acme.Service https://github.com/OctopusDeploy/Calamari/ diff --git a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.StructuredConfigFiles/Acme.StructuredConfigFiles.csproj b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.StructuredConfigFiles/Acme.StructuredConfigFiles.csproj index 782c942328..ead68801b7 100644 --- a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.StructuredConfigFiles/Acme.StructuredConfigFiles.csproj +++ b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.StructuredConfigFiles/Acme.StructuredConfigFiles.csproj @@ -5,7 +5,7 @@ en-US 1.0.0.0 ACME Corporation - net8.0 + net10.0 Acme.StructuredConfigFiles Acme.StructuredConfigFiles https://github.com/OctopusDeploy/Calamari/ diff --git a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web.Tests/Acme.Web.Tests.csproj b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web.Tests/Acme.Web.Tests.csproj index ab1d375751..259f71206e 100644 --- a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web.Tests/Acme.Web.Tests.csproj +++ b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web.Tests/Acme.Web.Tests.csproj @@ -4,7 +4,7 @@ Test project for Calamari. en-US Octopus Deploy - net8.0 + net10.0 Acme.Web.Tests Acme.Web.Tests https://github.com/OctopusDeploy/Calamari/ diff --git a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web/Acme.Web.Nix.csproj b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web/Acme.Web.Nix.csproj index ac21a9a30a..1128cb6a0a 100644 --- a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web/Acme.Web.Nix.csproj +++ b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web/Acme.Web.Nix.csproj @@ -4,7 +4,7 @@ Test project for Calamari. en-US Octopus Deploy - net8.0 + net10.0 Acme.Web Acme.Web https://github.com/OctopusDeploy/Calamari/ diff --git a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web/Acme.Web.csproj b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web/Acme.Web.csproj index 7d4370ee8b..df4b60c5d2 100644 --- a/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web/Acme.Web.csproj +++ b/source/Calamari.Tests/Fixtures/Deployment/Packages/Acme.Web/Acme.Web.csproj @@ -4,7 +4,7 @@ Test project for Calamari. en-US Octopus Deploy - net8.0 + net10.0 Acme.Web Acme.Web https://github.com/OctopusDeploy/Calamari/ diff --git a/source/Calamari.Tests/Fixtures/Deployment/Packages/Octopus.Sample.AzureCloudService/Octopus.Sample.AzureCloudService.csproj b/source/Calamari.Tests/Fixtures/Deployment/Packages/Octopus.Sample.AzureCloudService/Octopus.Sample.AzureCloudService.csproj index b8413caa65..9361d9450d 100644 --- a/source/Calamari.Tests/Fixtures/Deployment/Packages/Octopus.Sample.AzureCloudService/Octopus.Sample.AzureCloudService.csproj +++ b/source/Calamari.Tests/Fixtures/Deployment/Packages/Octopus.Sample.AzureCloudService/Octopus.Sample.AzureCloudService.csproj @@ -5,7 +5,7 @@ en-US 1.0.0.0 Octopus Deploy - net8.0 + net10.0 Octopus.Sample.AzureCloudService Octopus.Sample.AzureCloudService https://github.com/OctopusDeploy/Calamari/ diff --git a/source/Calamari.Tests/Fixtures/Nginx/NginxFixture.cs b/source/Calamari.Tests/Fixtures/Nginx/NginxFixture.cs index e5a4d507d4..08d0cfdb00 100644 --- a/source/Calamari.Tests/Fixtures/Nginx/NginxFixture.cs +++ b/source/Calamari.Tests/Fixtures/Nginx/NginxFixture.cs @@ -306,8 +306,7 @@ public void TestLocationsUnsuitableForFilenames() { var chainCertFilePath = TestEnvironment.GetTestPath("Helpers", "Certificates", "SampleCertificateFiles", "3-cert-chain.pfx"); - var certificateCollection = new X509Certificate2Collection(); - certificateCollection.Import(chainCertFilePath, "hello world", X509KeyStorageFlags.PersistKeySet); + var certificateCollection = X509CertificateLoader.LoadPkcs12CollectionFromFile(chainCertFilePath, "hello world", X509KeyStorageFlags.PersistKeySet); var certificate = certificateCollection.First(); var certificatePem = new string(PemEncoding.Write("CERTIFICATE", certificate.RawData)); diff --git a/source/Calamari.Tests/Helpers/Certificates/SampleCertificate.cs b/source/Calamari.Tests/Helpers/Certificates/SampleCertificate.cs index ccf927406e..8942cb427a 100644 --- a/source/Calamari.Tests/Helpers/Certificates/SampleCertificate.cs +++ b/source/Calamari.Tests/Helpers/Certificates/SampleCertificate.cs @@ -139,7 +139,7 @@ public static void AssertHasPrivateKeyRights(CryptoKeySecurity privateKeySecurit X509Certificate2 LoadAsX509Certificate2() { - return new X509Certificate2(FilePath, Password, + return X509CertificateLoader.LoadPkcs12FromFile(FilePath, Password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet); } diff --git a/source/Calamari/Calamari.csproj b/source/Calamari/Calamari.csproj index 3fa2a5a8db..f192f5d2f7 100644 --- a/source/Calamari/Calamari.csproj +++ b/source/Calamari/Calamari.csproj @@ -18,7 +18,7 @@ Calamari win-x64;linux-x64;osx-x64;linux-arm;linux-arm64 Calamari.exe.manifest - net8.0 + net10.0 CS8632 true diff --git a/source/Directory.Build.props b/source/Directory.Build.props index c078493167..c68904c1f7 100644 --- a/source/Directory.Build.props +++ b/source/Directory.Build.props @@ -1,6 +1,6 @@ - true + true false From fb4e969ccf625f35a4b9d2a4c912fd8b4bb98b29 Mon Sep 17 00:00:00 2001 From: Nick Josevski Date: Fri, 7 Aug 2026 15:42:30 +1000 Subject: [PATCH 5/9] Install the .NET 10 SDK in the Nuke bootstrappers build.sh and build.ps1 hardcode the SDK channel to 8.0 in an Octopus-local modification to the Nuke bootstrapper. The modification deliberately ignores global.json. Builds then roll forward to the latest patch automatically. On an agent without .NET preinstalled the bootstrapper would install an 8.0 SDK and then fail against a global.json requiring 10.0.302. Both bootstrappers now use the 10.0 channel. Verified end to end. ./build.sh --target PublishCalamariProjects --target-runtime linux-x64 succeeds. The published output is genuinely self-contained on the new runtime. Calamari.runtimeconfig.json reports net10.0 with Microsoft.NETCore.App 10.0.10 included. Co-Authored-By: Claude Opus 5 (1M context) --- build.ps1 | 6 +++--- build.sh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.ps1 b/build.ps1 index a7ae09dc9f..178c39ec83 100644 --- a/build.ps1 +++ b/build.ps1 @@ -65,14 +65,14 @@ else { # This means we would need to manually update our global.json file every time there is a new # .NET SDK available, and then all developers would need to immediately install this on their machines. # - # In our builds, we want the same "automatic roll-forward" behaviour that we get when we use the dotnet/sdk:8.0 docker + # In our builds, we want the same "automatic roll-forward" behaviour that we get when we use the dotnet/sdk:10.0 docker # images -- where we always get the latest patch version of the SDK without manual intervention. # # We achieve this with a small tweak to the Nuke bootstrapper to tell it to install the latest version from - # the 8.0 channel, regardless of what's in the global.json. + # the 10.0 channel, regardless of what's in the global.json. Remove-Variable DotNetVersion - $DotNetChannel = "8.0" + $DotNetChannel = "10.0" # ----- End Octopus Deploy Modification ----- # Install by channel or version diff --git a/build.sh b/build.sh index f10b818403..71b62c1189 100755 --- a/build.sh +++ b/build.sh @@ -91,14 +91,14 @@ else # This means we would need to manually update our global.json file every time there is a new # .NET SDK available, and then all developers would need to immediately install this on their machines. # - # In our builds, we want the same "automatic roll-forward" behaviour that we get when we use the dotnet/sdk:8.0 docker + # In our builds, we want the same "automatic roll-forward" behaviour that we get when we use the dotnet/sdk:10.0 docker # images -- where we always get the latest patch version of the SDK without manual intervention. # # We achieve this with a small tweak to the Nuke bootstrapper to tell it to install the latest version from - # the 8.0 channel, regardless of what's in the global.json. + # the 10.0 channel, regardless of what's in the global.json. unset DOTNET_VERSION - DOTNET_CHANNEL="8.0" + DOTNET_CHANNEL="10.0" # ----- End Octopus Deploy Modification ----- # Install by channel or version From f52c7f2cc9cee6c2ab18e213cfd687dafe5dffda Mon Sep 17 00:00:00 2001 From: Nick Josevski Date: Fri, 7 Aug 2026 15:42:30 +1000 Subject: [PATCH 6/9] Let dotnet-script roll forward to a newer .NET runtime Bundled dotnet-script 1.6.0 is a framework-dependent app targeting Microsoft.NETCore.App 8.0.0. Framework-dependent apps do not roll forward across a major version by default. On a machine with only .NET 10 installed dotnet-script fails to launch. Calamari itself is unaffected. Calamari ships self-contained. The failure is in the separate dotnet-script process. Setting DOTNET_ROLL_FORWARD=Major on that invocation lets dotnet-script run on whatever newer runtime is present. An explicit value already set in the environment is respected rather than overwritten. This is a customer-facing fix as much as a CI one. Without the fix a target with .NET 10 and no .NET 8 runtime cannot run C# script steps at all. SilentProcessRunner applies EnvironmentVars additively. Passing a dictionary where one was previously null does not discard the inherited environment. Not done here: upgrading the bundled dotnet-script. The 2.0.1 release zip Calamari vendors is still net8 targeted. Only the NuGet package ships a net10 build. The upgrade also changes the NuGet version dotnet-script bundles. The change invalidates the premise of UsingIsolatedAssemblyLoadContext. Verified: 10 DotnetScriptFixture tests pass. Co-Authored-By: Claude Opus 5 (1M context) --- .../DotnetScript/DotnetScriptExecutor.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/source/Calamari.Common/Features/Scripting/DotnetScript/DotnetScriptExecutor.cs b/source/Calamari.Common/Features/Scripting/DotnetScript/DotnetScriptExecutor.cs index 1df9532fc5..9e783acafd 100644 --- a/source/Calamari.Common/Features/Scripting/DotnetScript/DotnetScriptExecutor.cs +++ b/source/Calamari.Common/Features/Scripting/DotnetScript/DotnetScriptExecutor.cs @@ -11,6 +11,8 @@ namespace Calamari.Common.Features.Scripting.DotnetScript { public class DotnetScriptExecutor : ScriptExecutor { + const string DotnetRollForwardVariableName = "DOTNET_ROLL_FORWARD"; + readonly ICommandLineRunner commandLineRunner; public DotnetScriptExecutor(ICommandLineRunner commandLineRunner, ILog log): base(log) @@ -37,13 +39,37 @@ protected override IEnumerable PrepareExecution(Script script, bool.TryParse(variables.Get("Octopus.Action.Script.CSharp.BypassIsolation", "false"), out var bypassDotnetScriptIsolation); var cli = CreateCommandLineInvocation(executable, arguments, !string.IsNullOrWhiteSpace(localDotnetScriptPath)); - cli.EnvironmentVars = environmentVars; + cli.EnvironmentVars = WithDotnetRollForward(environmentVars); cli.WorkingDirectory = workingDirectory; cli.Isolate = !bypassDotnetScriptIsolation; yield return new ScriptExecution(cli, otherTemporaryFiles.Concat(new[] { bootstrapFile, configurationFile })); } + /// + /// dotnet-script is a framework-dependent application - the bundled copy targets + /// Microsoft.NETCore.App 8.0.0. By default a framework-dependent app will not roll forward + /// across a major version, so on a machine that only has a newer runtime installed it fails + /// to launch with "You must install or update .NET to run this application". + /// + /// Calamari itself is published self-contained and carries no such requirement; this affects + /// only the separate dotnet-script process. Setting DOTNET_ROLL_FORWARD=Major lets it run on + /// whatever newer runtime is present, so C# script steps don't additionally require the exact + /// runtime dotnet-script was built against. + /// + static Dictionary WithDotnetRollForward(Dictionary? environmentVars) + { + var vars = environmentVars == null + ? new Dictionary() + : new Dictionary(environmentVars); + + // Don't override an explicit value - the surrounding environment may have set one deliberately. + if (!vars.ContainsKey(DotnetRollForwardVariableName)) + vars[DotnetRollForwardVariableName] = "Major"; + + return vars; + } + private string GetExecutable(string? localDotnetScriptPath, string bundledExecutable) { return string.IsNullOrWhiteSpace(localDotnetScriptPath) From c976c1ca8a4a1f00c3d6df4ff9e4f6324dcdf38f Mon Sep 17 00:00:00 2001 From: Nick Josevski Date: Fri, 7 Aug 2026 15:42:30 +1000 Subject: [PATCH 7/9] Stop using empty string to unset env vars in proxy test fixtures .NET 10 changed Environment.SetEnvironmentVariable(name, ""). On net8 an empty value deleted the variable. On net10 it stores an empty string. Verified directly against 8.0.27 and 10.0.10. Passing null still deletes on both. The proxy fixtures relied on the old behaviour. Two uses together account for 11 of the 13 test failures in PR #2091's chain 2026.3.533. Reset helpers passed string.Empty to mean unset. On net10 that leaves HTTP_PROXY, HTTPS_PROXY and NO_PROXY set to "". ProxyEnvironmentVariablesGenerator tests for presence rather than value. The generator therefore took the "proxy variables already exist" branch and leaked empty values into every later fixture in the run. ProxySettingsInitializerFixture passed "" for absent credentials and then asserted BeNull(). On net10 the read returns "". Reset helpers now pass null. Credential assertions use BeNullOrEmpty(). The assertion states the actual intent rather than a representation detail. The setter deliberately keeps "" so the empty-string case is now genuinely covered. No product change and no customer-visible behaviour change. All 30 uses of the idiom were in test code. Inherited environment variables behave identically on both runtimes. Calamari inherits its environment in production. Verified on net10.0 locally: ProxySettingsInitializerFixture 5/5, ScriptEngineFixture 1/1, SetupKubectlAuthenticationFixture 3/3. The five Bash and DotnetScript proxy cases need bash, pwsh and the proxy fixture host. Those share the same teardown mechanism and ScriptProxyFixtureBase got the same fix. CI is the confirmation. Co-Authored-By: Claude Opus 5 (1M context) --- ...oxyEnvironmentVariablesGeneratorFixture.cs | 22 +++++++++---------- .../ProxySettingsInitializerFixture.cs | 18 +++++++-------- .../Proxies/ScriptProxyFixtureBase.cs | 16 +++++++------- .../Proxies/WebProxyInitializerFixture.cs | 10 ++++----- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/source/Calamari.Tests/Fixtures/Integration/Proxies/ProxyEnvironmentVariablesGeneratorFixture.cs b/source/Calamari.Tests/Fixtures/Integration/Proxies/ProxyEnvironmentVariablesGeneratorFixture.cs index 4609800cd5..46f287aee5 100644 --- a/source/Calamari.Tests/Fixtures/Integration/Proxies/ProxyEnvironmentVariablesGeneratorFixture.cs +++ b/source/Calamari.Tests/Fixtures/Integration/Proxies/ProxyEnvironmentVariablesGeneratorFixture.cs @@ -179,17 +179,17 @@ IEnumerable RunWith( void ResetProxyEnvironmentVariables() { - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleUseDefaultProxy, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyHost, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPort, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyUsername, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPassword, string.Empty); - Environment.SetEnvironmentVariable("HTTP_PROXY", string.Empty); - Environment.SetEnvironmentVariable("http_proxy", string.Empty); - Environment.SetEnvironmentVariable("HTTPS_PROXY", string.Empty); - Environment.SetEnvironmentVariable("https_proxy", string.Empty); - Environment.SetEnvironmentVariable("NO_PROXY", string.Empty); - Environment.SetEnvironmentVariable("no_proxy", string.Empty); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleUseDefaultProxy, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyHost, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPort, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyUsername, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPassword, null); + Environment.SetEnvironmentVariable("HTTP_PROXY", null); + Environment.SetEnvironmentVariable("http_proxy", null); + Environment.SetEnvironmentVariable("HTTPS_PROXY", null); + Environment.SetEnvironmentVariable("https_proxy", null); + Environment.SetEnvironmentVariable("NO_PROXY", null); + Environment.SetEnvironmentVariable("no_proxy", null); } void AssertAuthenticatedProxyUsed(IEnumerable result) diff --git a/source/Calamari.Tests/Fixtures/Integration/Proxies/ProxySettingsInitializerFixture.cs b/source/Calamari.Tests/Fixtures/Integration/Proxies/ProxySettingsInitializerFixture.cs index 1f82d5eaf5..f234bf916b 100644 --- a/source/Calamari.Tests/Fixtures/Integration/Proxies/ProxySettingsInitializerFixture.cs +++ b/source/Calamari.Tests/Fixtures/Integration/Proxies/ProxySettingsInitializerFixture.cs @@ -79,11 +79,11 @@ void SetEnvironmentVariables( void ResetProxyEnvironmentVariables() { - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleUseDefaultProxy, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyHost, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPort, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyUsername, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPassword, string.Empty); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleUseDefaultProxy, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyHost, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPort, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyUsername, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPassword, null); } void AssertCustomProxy(IProxySettings proxySettings, bool hasCredentials) @@ -101,8 +101,8 @@ void AssertCustomProxy(IProxySettings proxySettings, bool hasCredentials) } else { - proxy.Username.Should().BeNull(); - proxy.Password.Should().BeNull(); + proxy.Username.Should().BeNullOrEmpty(); + proxy.Password.Should().BeNullOrEmpty(); } } @@ -118,8 +118,8 @@ static void AssertSystemProxySettings(IProxySettings proxySettings, bool hasCred } else { - proxy.Username.Should().BeNull(); - proxy.Password.Should().BeNull(); + proxy.Username.Should().BeNullOrEmpty(); + proxy.Password.Should().BeNullOrEmpty(); } } diff --git a/source/Calamari.Tests/Fixtures/Integration/Proxies/ScriptProxyFixtureBase.cs b/source/Calamari.Tests/Fixtures/Integration/Proxies/ScriptProxyFixtureBase.cs index 705f82dc27..2af25d89ed 100644 --- a/source/Calamari.Tests/Fixtures/Integration/Proxies/ScriptProxyFixtureBase.cs +++ b/source/Calamari.Tests/Fixtures/Integration/Proxies/ScriptProxyFixtureBase.cs @@ -93,14 +93,14 @@ protected CalamariResult RunWith( void ResetProxyEnvironmentVariables() { - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleUseDefaultProxy, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyHost, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPort, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyUsername, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPassword, string.Empty); - EnvironmentHelper.SetEnvironmentVariable("HTTP_PROXY", string.Empty); - EnvironmentHelper.SetEnvironmentVariable("HTTPS_PROXY", string.Empty); - EnvironmentHelper.SetEnvironmentVariable("NO_PROXY", string.Empty); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleUseDefaultProxy, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyHost, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPort, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyUsername, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPassword, null); + EnvironmentHelper.SetEnvironmentVariable("HTTP_PROXY", null); + EnvironmentHelper.SetEnvironmentVariable("HTTPS_PROXY", null); + EnvironmentHelper.SetEnvironmentVariable("NO_PROXY", null); } protected virtual void AssertAuthenticatedProxyUsed(CalamariResult output) diff --git a/source/Calamari.Tests/Fixtures/Integration/Proxies/WebProxyInitializerFixture.cs b/source/Calamari.Tests/Fixtures/Integration/Proxies/WebProxyInitializerFixture.cs index ce31e3e832..0a2370f233 100644 --- a/source/Calamari.Tests/Fixtures/Integration/Proxies/WebProxyInitializerFixture.cs +++ b/source/Calamari.Tests/Fixtures/Integration/Proxies/WebProxyInitializerFixture.cs @@ -164,11 +164,11 @@ void RunWith( void ResetProxyEnvironmentVariables() { - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleUseDefaultProxy, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyHost, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPort, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyUsername, string.Empty); - Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPassword, string.Empty); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleUseDefaultProxy, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyHost, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPort, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyUsername, null); + Environment.SetEnvironmentVariable(EnvironmentVariables.TentacleProxyPassword, null); } void AssertAuthenticatedProxyUsed() From b36d6ce3a639e4cd6e8cf64f300fbc2e34708678 Mon Sep 17 00:00:00 2001 From: Nick Josevski Date: Fri, 7 Aug 2026 15:42:30 +1000 Subject: [PATCH 8/9] Generate the manifest test's sample app on net10.0 CodeGenerator.GenerateConsoleApplication hardcoded a net8-era toolchain. It wrote a global.json pinning SDK 8.0.10 and ran dotnet new console -f net8.0. The agents provide only a .NET 10 SDK. SDK 8.0.10 is unsatisfiable because rollForward latestFeature only rolls in the 8.0.x band. The test failed before it could exercise anything. That is the ExecuteManifestCommandFixture.WithInstructions failure on the Linux netcore-testing configs in PR #2091's chain 2026.3.534. It was the last remaining Linux test failure after the proxy fixture fix landed. Bumped to SDK 10.0.302 and net10.0, matching the repository's root global.json. Verification is partial. The SDK resolution error is gone. dotnet new console -f net10.0 succeeds and the project is created. The test still fails on an Apple Silicon dev machine for a pre-existing and unrelated reason. RID selection a few lines below sets osx-x64 for Mac and then overwrites it with linux-arm64 whenever ProcessArchitecture is Arm64. That check is not nested under the IsRunningOnNix branch. The generated app is published for linux-arm64 and exec'd on macOS. CI is unaffected and is the confirmation for this change. Co-Authored-By: Claude Opus 5 (1M context) --- source/Calamari.Tests/Helpers/CodeGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Calamari.Tests/Helpers/CodeGenerator.cs b/source/Calamari.Tests/Helpers/CodeGenerator.cs index ad275b31e5..1cf63f10a1 100644 --- a/source/Calamari.Tests/Helpers/CodeGenerator.cs +++ b/source/Calamari.Tests/Helpers/CodeGenerator.cs @@ -26,11 +26,11 @@ CommandLineInvocation CreateCommandLineInvocation(string executable, string argu File.WriteAllText(Path.Combine(projectPath.FullName, "global.json"), @"{ ""sdk"": { - ""version"": ""8.0.10"", + ""version"": ""10.0.302"", ""rollForward"": ""latestFeature"" } }"); - var result = clr.Execute(CreateCommandLineInvocation("dotnet", "new console -f net8.0")); + var result = clr.Execute(CreateCommandLineInvocation("dotnet", "new console -f net10.0")); result.VerifySuccess(); var programCS = Path.Combine(projectPath.FullName, "Program.cs"); var newProgram = $@"using System; From ef2b9c0a60a659a153055614d47314e1e812907f Mon Sep 17 00:00:00 2001 From: Nick Josevski Date: Fri, 7 Aug 2026 15:42:30 +1000 Subject: [PATCH 9/9] Drop the System.Text.Json reference that main added main added System.Text.Json 9.0.16 to Calamari.csproj after this branch diverged. On net10.0 it is framework-provided, so NuGet prunes it and NU1510 fails the restore under TreatWarningsAsErrors. The net10 shared framework ships a newer System.Text.Json than 9.0.16, so removing the explicit reference keeps the version floor main was after. Same rationale as "Remove framework-provided package references". Co-Authored-By: Claude Opus 5 (1M context) --- source/Calamari.AzureAppService/Calamari.AzureAppService.csproj | 1 - source/Calamari.Tests/Calamari.Tests.csproj | 1 - source/Calamari/Calamari.csproj | 1 - 3 files changed, 3 deletions(-) diff --git a/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj b/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj index a4d87e9d40..5376812cbb 100644 --- a/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj +++ b/source/Calamari.AzureAppService/Calamari.AzureAppService.csproj @@ -17,7 +17,6 @@ - diff --git a/source/Calamari.Tests/Calamari.Tests.csproj b/source/Calamari.Tests/Calamari.Tests.csproj index bcd6344116..e78048f514 100644 --- a/source/Calamari.Tests/Calamari.Tests.csproj +++ b/source/Calamari.Tests/Calamari.Tests.csproj @@ -21,7 +21,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/source/Calamari/Calamari.csproj b/source/Calamari/Calamari.csproj index f192f5d2f7..50cd121eaf 100644 --- a/source/Calamari/Calamari.csproj +++ b/source/Calamari/Calamari.csproj @@ -39,7 +39,6 @@ -