diff --git a/source/Calamari.Common/Features/Scripting/DotnetScript/DotnetScriptBootstrapper.cs b/source/Calamari.Common/Features/Scripting/DotnetScript/DotnetScriptBootstrapper.cs index 0e92786e7b..b59b802ee8 100644 --- a/source/Calamari.Common/Features/Scripting/DotnetScript/DotnetScriptBootstrapper.cs +++ b/source/Calamari.Common/Features/Scripting/DotnetScript/DotnetScriptBootstrapper.cs @@ -102,11 +102,31 @@ public static string FormatCommandArguments(string bootstrapFile, string? script var source = string.IsNullOrWhiteSpace(nugetSource) ? "https://api.nuget.org/v3/index.json" : nugetSource; var commandArguments = new StringBuilder(); commandArguments.Append($"-s {source} "); + // dotnet-script 2.0 turned the isolated assembly load context on by default, which + // changes which assembly a script gets when it references a package that dotnet-script + // also carries: a script asking for Newtonsoft.Json 9.0.1 used to bind to the 13.0.3 + // dotnet-script bundles, and under isolation it gets 9.0.1. That is arguably more + // correct, but it is a silent change to what customer scripts run against, so hold the + // existing behaviour here and treat adopting isolation as its own decision. + // + // Unless the customer asked for isolation themselves. When both flags are passed, + // --disable-isolated-load-context wins, so adding it unconditionally would override + // the one group of customers who made a deliberate choice here. + if (!HasIsolatedLoadContextFlag(scriptCommandArguments)) + commandArguments.Append("--disable-isolated-load-context "); if (!string.IsNullOrWhiteSpace(scriptCommandArguments)) commandArguments.Append($"{scriptCommandArguments} "); commandArguments.AppendFormat("\"{0}\" -- {1} \"{2}\"", bootstrapFile, scriptArguments, encryptionKey); return commandArguments.ToString(); } + /// + /// True when the caller already passed --isolated-load-context. Note that + /// --disable-isolated-load-context does not match: "--isolated-load-context" is not a + /// substring of it, because the character before "isolated" there is a single dash. + /// + static bool HasIsolatedLoadContextFlag(string? scriptCommandArguments) + => scriptCommandArguments?.IndexOf("--isolated-load-context", StringComparison.OrdinalIgnoreCase) >= 0; + [return: NotNullIfNotNull("scriptParameters")] static (string? scriptCommandArguments, string? scriptArguments) RetrieveParameterValues(string? scriptParameters) { diff --git a/source/Calamari.Scripting/DotnetScript/dotnet-script.1.6.0.zip b/source/Calamari.Scripting/DotnetScript/dotnet-script.1.6.0.zip deleted file mode 100644 index 88cda61773..0000000000 Binary files a/source/Calamari.Scripting/DotnetScript/dotnet-script.1.6.0.zip and /dev/null differ diff --git a/source/Calamari.Scripting/DotnetScript/dotnet-script.2.0.1.zip b/source/Calamari.Scripting/DotnetScript/dotnet-script.2.0.1.zip new file mode 100644 index 0000000000..65024309a4 Binary files /dev/null and b/source/Calamari.Scripting/DotnetScript/dotnet-script.2.0.1.zip differ diff --git a/source/Calamari.Scripting/DotnetScript/dotnet-script.runtimeconfig.json b/source/Calamari.Scripting/DotnetScript/dotnet-script.runtimeconfig.json new file mode 100644 index 0000000000..5b614377bb --- /dev/null +++ b/source/Calamari.Scripting/DotnetScript/dotnet-script.runtimeconfig.json @@ -0,0 +1,14 @@ +{ + "runtimeOptions": { + "tfm": "net8.0", + "rollForward": "LatestMajor", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "8.0.0" + }, + "configProperties": { + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false + } + } +} diff --git a/source/Calamari.Tests/Fixtures/DotnetScript/DotnetScriptBootstrapperFixture.cs b/source/Calamari.Tests/Fixtures/DotnetScript/DotnetScriptBootstrapperFixture.cs index ba81b3c9a9..fe4a59b45a 100644 --- a/source/Calamari.Tests/Fixtures/DotnetScript/DotnetScriptBootstrapperFixture.cs +++ b/source/Calamari.Tests/Fixtures/DotnetScript/DotnetScriptBootstrapperFixture.cs @@ -22,6 +22,23 @@ public void FormatCommandArgumentsTest([CanBeNull] string scriptParameters, [Can formattedCommandArgument.Should().Contain($"{commandArguments}\"{bootstrapFile}\" -- {scriptArguments}"); } + [Test] + public void FormatCommandArguments_DisablesIsolatedLoadContext_ByDefault() + { + var result = DotnetScriptBootstrapper.FormatCommandArguments("Bootstrap.csx", null); + result.Should().Contain("--disable-isolated-load-context "); + } + + [TestCase("--isolated-load-context -- \"Parameter 1\"")] + [TestCase("--isolated-load-context -d -- \"Parameter 1\"")] + public void FormatCommandArguments_LeavesIsolationOn_WhenTheCallerAskedForIt(string scriptParameters) + { + // Both flags together resolve to disabled, so we must not add ours on top of theirs. + var result = DotnetScriptBootstrapper.FormatCommandArguments("Bootstrap.csx", scriptParameters); + result.Should().NotContain("--disable-isolated-load-context"); + result.Should().Contain("--isolated-load-context "); + } + [Test] public void FormatCommandArguments_UsesCustomNuGetSource_WhenProvided() { diff --git a/source/IncludeDotNetScript.targets b/source/IncludeDotNetScript.targets index 1f6ed73182..dfa02786b5 100644 --- a/source/IncludeDotNetScript.targets +++ b/source/IncludeDotNetScript.targets @@ -3,6 +3,15 @@ + + @@ -10,6 +19,7 @@ + @@ -18,6 +28,7 @@ + \ No newline at end of file