Skip to content

Let dotnet-script roll forward to a newer .NET runtime - #2095

Draft
NickJosevski wants to merge 1 commit into
mainfrom
nj/net10-prep-deps-v2
Draft

NickJosevski wants to merge 1 commit into
mainfrom
nj/net10-prep-deps-v2

Conversation

@NickJosevski

@NickJosevski NickJosevski commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Sets DOTNET_ROLL_FORWARD=Major on the dotnet-script child process.

Why

The bundled dotnet-script is framework-dependent, targets Microsoft.NETCore.App 8.0.0. Framework-dependent apps don't cross major version boundary by default, machine with .NET 10 and no .NET 8 already gets error:

You must install or update .NET to run this application.

Because worker-tools:ubuntu.24.04 ships SDK 10 with no .NET 8 runtime, so C# script steps cannot run there today.

Change

if (!vars.ContainsKey("DOTNET_ROLL_FORWARD"))
    vars["DOTNET_ROLL_FORWARD"] = "Major";

Question

When my Octopus instance gets the dotnet10 Calamari build. But the workers/targets I run only have dotnet8 SDK, no net10. What happens?

Answer

Nothing changes. They keep working, C# script steps included.

  • Calamari doesn't need .NET 10 on worker. It ships self-contained with a runtime.
  • dotnet-script asks for Microsoft.NETCore.App 8.0.0, finds your .NET 8, and runs on it.
  • Major only engages when the requested major is absent. Where .NET 8 exists, is as-is.

The configuration that breaks is the opposite one, so this PR fixes:

Worker has Before After
.NET 8 only works works — unchanged
.NET 8 and .NET 10 works works — unchanged, still resolves 8
.NET 10 only will not launch launches

Risk

Nothing can go from working to broken. Every configuration this alters is one where dotnet-script fails to launch today; every configuration that works today has .NET 8, where the variable is inert.

  • An explicit DOTNET_ROLL_FORWARD already in the environment is respected, not overwritten.
  • Set on the child process only, not exported globally.
  • Calamari.Common: 79 warnings, 0 errors — identical to main.
  • 10/10 DotnetScriptFixture tests pass.
  • CI agents still have SDK 8 and SDK 10,

Server will need to reference this build.

@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
NickJosevski force-pushed the nj/deps-cve-test-tooling branch from 1b88535 to e47255c Compare August 4, 2026 07:51
@NickJosevski
NickJosevski force-pushed the nj/net10-prep-deps-v2 branch from 1af3547 to 1d13556 Compare August 4, 2026 07:51
@NickJosevski
NickJosevski force-pushed the nj/deps-cve-test-tooling branch from e47255c to af660e7 Compare August 6, 2026 09:05
@NickJosevski
NickJosevski force-pushed the nj/net10-prep-deps-v2 branch from 5a197ad to a9c3d46 Compare August 7, 2026 04:32
@NickJosevski
NickJosevski changed the base branch from nj/deps-cve-test-tooling to main August 7, 2026 04:33
@NickJosevski
NickJosevski force-pushed the nj/net10-prep-deps-v2 branch from a9c3d46 to 4c549d7 Compare August 7, 2026 05:43
@NickJosevski
NickJosevski force-pushed the nj/net10-prep-deps-v2 branch 2 times, most recently 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.
@NickJosevski
NickJosevski force-pushed the nj/net10-prep-deps-v2 branch 2 times, most recently from b86bd05 to 8f5bedf Compare August 11, 2026 05:41
@NickJosevski NickJosevski changed the title Prepare dependencies for .NET 10 (net8-safe subset) Let dotnet-script roll forward to a newer .NET runtime Aug 11, 2026
@NickJosevski
NickJosevski marked this pull request as ready for review August 11, 2026 05:58
@NickJosevski
NickJosevski force-pushed the nj/net10-prep-deps-v2 branch from 8f5bedf to 40355b0 Compare August 12, 2026 02:28
The bundled dotnet-script is framework-dependent and targets
Microsoft.NETCore.App 8.0.0. Framework-dependent apps do not cross a major
version boundary by default, so on a machine that has .NET 10 but no .NET 8 it
fails to launch with "You must install or update .NET to run this application".

Setting DOTNET_ROLL_FORWARD=Major on the dotnet-script invocation lets it run on
whatever newer runtime is present.

The variable only engages when the requested major is absent, so where a .NET 8
runtime exists the resolved runtime is unchanged. An explicit value already set
in the environment is respected rather than overwritten. Every configuration this
alters is one where dotnet-script fails to launch today, so nothing can go from
working to broken.

Calamari itself is unaffected either way - it ships self-contained and carries
its own runtime. This is only about the separate dotnet-script process.

Fixes a live bug rather than only preparing for .NET 10: worker-tools:ubuntu.24.04
ships SDK 10 with no .NET 8 runtime, so C# script steps cannot run there today.

Scope is launch only. A script using #r "nuget:" on a framework-provided package
still fails under the .NET 10 SDK; that needs the bundled dotnet-script upgraded
to 2.0.x and is tracked separately.

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 40355b0 to 161112d Compare August 18, 2026 07:54
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