Skip to content

Bundle dotnet-script 2.0.1 and give it a roll-forward policy - #2134

Draft
NickJosevski wants to merge 1 commit into
mainfrom
nj/dotnet-script-2.0.1-rollforward
Draft

Bundle dotnet-script 2.0.1 and give it a roll-forward policy#2134
NickJosevski wants to merge 1 commit into
mainfrom
nj/dotnet-script-2.0.1-rollforward

Conversation

@NickJosevski

Copy link
Copy Markdown
Contributor

Supersedes the WithDotnetRollForward approach in #2095, and folds in the 2.0.1 bundle change.

Problem

dotnet-script is framework-dependent and requests Microsoft.NETCore.App 8.0.0 with no rollForward. Under the default Minor policy that binds to any 8.x and nothing higher, so on a target with no 8.x runtime it fails to launch (exit 150) before any script runs. Calamari itself is published self-contained and is unaffected — this is only the separate dotnet-script process.

Upgrading to 2.0.1 does not fix this on its own. The GitHub release zip — the asset IncludeDotNetScript.targets extracts — has a runtimeconfig byte-identical to 1.6.0's. 2.0.1's net10 build exists only in the NuGet package (tools/net10.0), which requests exactly 10.0.0 with no rollForward and would take C# script steps away from every net8-only target. So both changes are here, because they're orthogonal.

Approach

The zip is vendored byte-for-byte from upstream (sha256 667535727b0660223cfb9dcfc240831d8ca2a2af8f2e98214ee556334e0e1e13, verifiable against the release asset). The policy is applied by overwriting the extracted runtimeconfig with dotnet-script.runtimeconfig.json from source control.

That's deliberate over the two alternatives:

  • vs. pre-patching the zip — the policy stays reviewable as text, and can't be lost silently the next time someone re-vendors.
  • vs. DOTNET_ROLL_FORWARD (Let dotnet-script roll forward to a newer .NET runtime #2095) — no C# change; works on every launch path including Windows' dotnet-script.cmd; doesn't leak into every process a customer's script starts; and config sits below env var and command line in precedence, so a per-step override still works. It also avoids a bug in Let dotnet-script roll forward to a newer .NET runtime #2095: WithDotnetRollForward intends "don't override an explicit value" but checks only the passed dictionary, never Environment.GetEnvironmentVariable, and SilentProcessRunner applies that dictionary over the inherited environment — so a machine-level DOTNET_ROLL_FORWARD is silently overridden.

Why Major and not LatestMajor

The decision doc recommends LatestMajor. I've shipped Major here, because measuring it turned up a cost the doc doesn't account for. Measured on a box with 6/8/9/10 installed:

policy resolved runtime
none (upstream) 8.0.27
Major 8.0.27 — no-op, 8.x is present
LatestMajor 10.0.10

LatestMajor genuinely does what the doc wants. But dotnet-script derives its generated csproj TFM from the loaded corelib, so rolling the host to 10 makes it emit net10.0 — and the restore then needs an SDK that can target net10.0. Under dotnet test that reds 20 dotnet-script tests with NETSDK1045: The current .NET SDK does not support targeting .NET 10.0.

I could not fully isolate whether that's only MSBuild environment leaking from the test host, or whether it also bites a real target with SDK 8 + runtime 10 installed — a plausible shape, and one where scripts work today. Until that's settled, LatestMajor can't claim "cannot break a target that works today". Major fixes the actual reported failure and moves nobody else; switching later is a one-word change plus the test constant.

Two failing tests, left failing on purpose

UsingIsolatedAssemblyLoadContext(True) and (False). Baseline on main already fails four DotnetScriptProxyFixture proxy tests; this branch fails those four plus these two.

Both are 2.0.1 behaviour changes around --isolated-load-context, not roll-forward (they fail with Major, where the resolved runtime is unchanged from main):

  • (False) asserts a failureCould not load file or assembly 'NuGet.Protocol, Version=6.10.1.5' — that no longer happens. It now exits 0. 2.0.1 bundles NuGet 6.14.3 where 1.6.0 bundled 6.10.0.
  • (True) still prints the expected NuGet.Commands version: 6.10.1.5 but no longer produces Parameters Parameter0Parameter1.

I didn't paper over these. (False) encodes that isolation is necessary, and if 2.0.1 no longer needs it that's a real signal about assembly isolation that wants a decision from someone who owns this area — not an assertion update from me.

Verification

  • Vendored zip hash matches the upstream release asset exactly.
  • Build and publish both emit the policy — confirmed in bin/Release/net8.0/dotnet-script/.
  • A real script executed through the bundled 2.0.1 and reported the rolled-forward runtime.
  • New DotnetScriptRuntimeConfigFixture (2 tests, PlatformAgnostic) guards both failure modes: the copy silently not happening, and a future re-vendor bringing upstream settings the override would clobber.

Not addressed

The SDK requirement is unchanged. dotnet-script shells out to dotnet restore on every run, even for a script with zero NuGet references, so a runtime-only target has never worked and still doesn't. No roll-forward setting or version bump touches that.

🤖 Generated with Claude Code

dotnet-script is framework-dependent and requests Microsoft.NETCore.App 8.0.0
with no rollForward, so on a target with no 8.x runtime it fails to launch with
exit 150 before any script runs. Calamari is published self-contained and is
unaffected; this is only the separate dotnet-script process.

Upgrading does not fix it on its own. 2.0.1's GitHub release zip - the asset
IncludeDotNetScript.targets extracts - has a runtimeconfig byte-identical to
1.6.0's. The net10 build exists only in the NuGet package (tools/net10.0), which
requests exactly 10.0.0 with no rollForward and would take C# scripts away from
every net8-only target. The two changes are orthogonal, so both are here.

The zip is vendored byte-for-byte from upstream (sha256 6675357...0e1e13) and the
policy is applied by overwriting the extracted runtimeconfig with a file in
source control. That keeps the policy reviewable as text and stops it being lost
silently the next time the zip is re-vendored, which pre-patching the binary
would not.

Chose Major over the LatestMajor the decision doc recommends. Major engages only
when the requested major is absent, so it fixes the launch failure and moves
nobody else. LatestMajor also moves targets that work today, and measurement
turned up a cost the decision doc did not account for - see the PR description.

Two dotnet-script tests fail on this branch and are left failing deliberately;
both are 2.0.1 behaviour changes around --isolated-load-context, not roll-forward,
and both want a decision from someone who owns that area. Baseline on main fails
four proxy tests already.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant