Skip to content

Prepare dependencies for .NET 10 (net8-safe subset) - #2095

Draft
NickJosevski wants to merge 3 commits into
mainfrom
nj/net10-prep-deps-v2
Draft

Prepare dependencies for .NET 10 (net8-safe subset)#2095
NickJosevski wants to merge 3 commits into
mainfrom
nj/net10-prep-deps-v2

Conversation

@NickJosevski

@NickJosevski NickJosevski commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Background

Calamari has to reach .NET 10 before .NET 8 support ends on 10 November 2026.

Every risky dependency bump is done while Calamari still targets net8. A green build on net8 proves each bump in isolation. The target framework flip (#2091) then happens on a dependency graph that is already ready.

Results

The net8-safe subset extracted from #2091. Now targets main directly — #2094 and #2116 have landed, so this is three commits.

Commit What
Remove the dead .NET Framework reference assemblies Microsoft.NETFramework.ReferenceAssemblies.net462, unused
Upgrade Autofac 4.8.0 to 9.3.1 Five majors in one step. No code changes needed
Let dotnet-script roll forward to a newer .NET runtime Sets DOTNET_ROLL_FORWARD=Major on the dotnet-script invocation

The version-alignment commits are gone. System.Linq.Async was removed outright by #2120, and the Serilog and Microsoft.Extensions.* alignment went with it.

Autofac: five majors from 2018 to current, no code changes. ContainerBuilder.Update() is the headline Autofac 5 removal and was never used here. It does add one new warning — CS8714 at Calamari.Common/Plumbing/Pipeline/Resolver.cs:17, because Autofac 9's Resolve<TService> carries a notnull constraint that TBehaviour doesn't satisfy. Calamari.Common doesn't treat warnings as errors, so it builds; worth a look if that changes.

The dotnet-script commit fixes a live customer bug

Worth calling out because the fix is not really about .NET 10.

Bundled dotnet-script 1.6.0 targets Microsoft.NETCore.App 8.0.0. Framework-dependent apps do not roll forward across a major version by default. A customer whose target has only .NET 10 installed cannot run C# script steps at all. That failure exists today on net8, and worker-tools:ubuntu.24.04 is a shipping image in exactly that state.

Calamari itself is unaffected. Calamari ships self-contained. The failure is in the separate dotnet-script process.

Scope: this fixes launch. It does not make dotnet-script net10-ready. A script using #r "nuget: <framework-provided package>" still fails on net10 — dotnet-script 1.6.0 bundles NuGet.ProjectModel 6.10.0, which cannot parse the logs array that NU1510 adds to project.assets.json. That needs the dotnet-script 2.0.x bump, tracked separately.

Reducing risk

  • Clean rebuild of source/Calamari.sln on net8.0: 165 warnings, 0 errors. main is 164; the extra one is the CS8714 above
  • DOTNET_ROLL_FORWARD=Major only engages when the requested major is absent, and picks the lowest higher major. Where an 8.x runtime exists the resolved runtime is unchanged. Every configuration this alters is one where dotnet-script fails to launch today — which also means CI agents carrying both SDK 8 and SDK 10 do not exercise it
  • Rebased onto current main; no duplicate PackageReference across the 42 project files

How to review this PR

General quality.

⚠️ Does this change require a corresponding Server Change?
⚠️ If so - please add a "Requires Server Change" label to this PR!

@APErebus

APErebus commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

This is the opposite of the net8 migration (#1528), which landed dependency, build-system and TFM churn together and produced ~40 commits of un-attributable fixes. The payoff was immediate: Autofac 4.8.0 → 9.3.1 — five majors, 2018 to current — was a non-event. Zero new warnings, all DI tests green. ContainerBuilder.Update(), the headline Autofac 5 removal, was never used here.

Appreciate the shade 🤖

/// whatever newer runtime is present, so C# script steps don't additionally require the exact
/// runtime dotnet-script was built against.
/// </summary>
static Dictionary<string, string> WithDotnetRollForward(Dictionary<string, string>? environmentVars)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DotNet script requires the dotnet sdk, not just the dotnet runtime, does this work for that?

<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Autofac" Version="4.8.0" />
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
<PackageReference Include="Autofac" Version="9.3.1" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like a huge bump... Probably need some integration testing to make sure it's all on the up and up

@NickJosevski
NickJosevski marked this pull request as draft August 4, 2026 06:55

@zentron zentron left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to be careful with this upgrade and confirm any breaking changes that will impact users utilizing dotnet-script.

Unfortunately in this new world the customer C# scripts will now run under whatever framework we are running Calamari itself under.

Not marked as Request Changes since this is just more a comment for caution.

NickJosevski and others added 3 commits August 10, 2026 14:17
Calamari.csproj still referenced Microsoft.NETFramework.ReferenceAssemblies.net462.
That reference has done nothing since net462 was dropped as a target framework in
#1669.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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<T> extension builds on Meta<T> 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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
@NickJosevski
NickJosevski force-pushed the nj/net10-prep-deps-v2 branch from 8702b70 to b5274ac Compare August 10, 2026 04:18
NickJosevski added a commit that referenced this pull request Aug 10, 2026
Script wrappers form an execution chain and file-format replacers are tried in
sequence, so a reordering changes deployment behaviour. Nothing currently
detects that: every wrapper still constructs and every command still resolves,
so the existing suite stays green.

Two guards, both PlatformAgnostic:

ContainerOrderingFixture asserts the orderings directly. The load-bearing one is
ScriptWrapperPrioritiesAreUnique — ScriptEngine sorts with OrderByDescending,
which is stable, so a shared priority silently hands the tie-break to the
container's collection ordering. Priorities are distinct today; the test keeps
them that way. The rest pin the wrapper chain for a Kubernetes/AWS/PowerShell
step, the file-format replacer order, and discoverer key uniqueness.

ContainerSnapshotFixture compares a rendering of the whole container against an
approved file: registrations, lifetimes, collection order, and the concrete
types injected into every command. Targeted assertions only catch what someone
thought of; this catches anything observable that moves, which is what makes it
useful across a dependency upgrade. Re-approve with
CALAMARI_APPROVE_CONTAINER_SNAPSHOT=1.

The snapshot is normalised so one approved file serves every platform: the
OS-chosen filesystem and certificate-store implementations collapse to
placeholders, Autofac's __RegistrationOrder tick counter is dropped, and
collection fields render distinct element types rather than counts.

Verified against the Autofac 4.8.0 -> 9.3.1 upgrade in #2095: the snapshot fails
with exactly the version line plus five registrations gaining AutoActivate (the
RegisterInstance ones), and all five ordering tests still pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants