diff --git a/NuGet.config b/NuGet.config
index a57f4eaac523..7711f990206e 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -24,8 +24,12 @@
+
+
+
+
@@ -35,11 +39,13 @@
+
+
@@ -68,10 +74,13 @@
+
+
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 0dd6a6b1aab6..57a146246309 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -15,42 +15,42 @@
b73682307aa0128c5edbec94c2e6a070d13ae6bb
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
@@ -59,14 +59,14 @@
https://github.com/dotnet/core-setup
7d57652f33493fa022125b7f63aad0d70c52d810
-
+
https://github.com/dotnet/emsdk
- dd379007ea0e38b82e87ca5fd61bb43b432eaf7a
+ b2bf6cec0cb4ce281802ac3951bdd14287aad112
-
+
https://github.com/dotnet/emsdk
- dd379007ea0e38b82e87ca5fd61bb43b432eaf7a
+ b2bf6cec0cb4ce281802ac3951bdd14287aad112
@@ -131,13 +131,13 @@
https://github.com/dotnet/roslyn
450493a9b4ec6337bced0120e97cb76f4ed783db
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
https://dev.azure.com/devdiv/DevDiv/_git/NuGet-NuGet.Client-Trusted
@@ -226,99 +226,99 @@
51441adcd6c424ae7315d66ce7e96baf34d70369
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- 4a2048b77c7de671b08320e346c73da6fd6d9aeb
+ e1078413a088bdc4c4d5b3cd45faeda03d7d1076
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- 4a2048b77c7de671b08320e346c73da6fd6d9aeb
+ e1078413a088bdc4c4d5b3cd45faeda03d7d1076
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- 4a2048b77c7de671b08320e346c73da6fd6d9aeb
+ e1078413a088bdc4c4d5b3cd45faeda03d7d1076
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-windowsdesktop
- 4a2048b77c7de671b08320e346c73da6fd6d9aeb
+ e1078413a088bdc4c4d5b3cd45faeda03d7d1076
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-wpf
- 75bc144dfb125fb821e28adb648dd35618b23693
+ b11f25ba2d9d291ca38621d7ba15ad9563e96d30
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
@@ -339,30 +339,30 @@
41f3afd466695ac2460260431537fe4d779ff446
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-winforms
- 96d636b0e1c0e7b43433f6c5f4920c61e448955a
+ 6502cec45fb6ee15a78a39105e6cd9ccf2e6c364
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-wpf
- 75bc144dfb125fb821e28adb648dd35618b23693
+ b11f25ba2d9d291ca38621d7ba15ad9563e96d30
https://github.com/dotnet/xdt
@@ -469,89 +469,89 @@
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-aspnetcore
- 209bf5cfd6f0b329c31faa125790e1d1d820cdb5
+ 4e6235b8f9af12335ecd58a0a06b8f4105af2de0
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
@@ -585,9 +585,9 @@
216020cfc0f97a6dd5075191faa55c61a7c27609
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 8381bdb01fe4a26e1f61370e779c78bb73ecc95b
+ 3879076d9a06ce098d37c3882fb1845a6627335b
https://github.com/dotnet/arcade-services
diff --git a/eng/Versions.props b/eng/Versions.props
index 833761c8d7ef..60539ff11370 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -81,61 +81,61 @@
- 9.0.19-servicing.26370.1
+ 9.0.20-servicing.26414.2
- 9.0.19
- 9.0.19-servicing.26367.24
- 9.0.19
- 9.0.19
- 9.0.19-servicing.26367.24
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
+ 9.0.20
+ 9.0.20-servicing.26413.15
+ 9.0.20
+ 9.0.20
+ 9.0.20-servicing.26413.15
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
8.0.0-rc.1.23414.4
- 9.0.19-servicing.26367.24
- 9.0.19-servicing.26367.24
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
+ 9.0.20-servicing.26413.15
+ 9.0.20-servicing.26413.15
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
2.1.0
- 9.0.19
+ 9.0.20
8.0.0
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
8.0.0
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
- 9.0.19
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
+ 9.0.20
4.5.1
4.5.5
8.0.5
4.5.4
- 9.0.19
- 9.0.19
+ 9.0.20
+ 9.0.20
- 9.0.19-servicing.26371.1
- 9.0.19-servicing.26371.1
- 9.0.19
- 9.0.19
+ 9.0.20-servicing.26421.5
+ 9.0.20-servicing.26421.5
+ 9.0.20
+ 9.0.20
@@ -217,19 +217,19 @@
- 9.0.19
- 9.0.19-servicing.26370.16
- 9.0.19-servicing.26370.16
- 9.0.19-servicing.26370.16
- 9.0.19-servicing.26370.16
- 9.0.19-servicing.26370.16
- 9.0.19
- 9.0.19
- 9.0.19-servicing.26370.16
- 9.0.19-servicing.26370.16
- 9.0.19-servicing.26370.16
- 9.0.19-servicing.26370.16
- 9.0.19-servicing.26370.16
+ 9.0.20
+ 9.0.20-servicing.26414.7
+ 9.0.20-servicing.26414.7
+ 9.0.20-servicing.26414.7
+ 9.0.20-servicing.26414.7
+ 9.0.20-servicing.26414.7
+ 9.0.20
+ 9.0.20
+ 9.0.20-servicing.26414.7
+ 9.0.20-servicing.26414.7
+ 9.0.20-servicing.26414.7
+ 9.0.20-servicing.26414.7
+ 9.0.20-servicing.26414.7
@@ -239,8 +239,8 @@
- 9.0.19-rtm.26371.2
- 9.0.19-rtm.26371.2
+ 9.0.20-rtm.26421.6
+ 9.0.20-rtm.26421.6
@@ -319,7 +319,7 @@
15.0.9617
18.0.9617
- 9.0.19
+ 9.0.20
$(MicrosoftNETWorkloadEmscriptenCurrentManifest90100PackageVersion)
9.0.100$([System.Text.RegularExpressions.Regex]::Match($(EmscriptenWorkloadManifestVersion), `-(?!rtm)[A-z]*[\.]*\d*`))
diff --git a/src/BuiltInTools/AspireService/Models/RunSessionRequest.cs b/src/BuiltInTools/AspireService/Models/RunSessionRequest.cs
index 32d52181449f..c98a008c44a8 100644
--- a/src/BuiltInTools/AspireService/Models/RunSessionRequest.cs
+++ b/src/BuiltInTools/AspireService/Models/RunSessionRequest.cs
@@ -76,7 +76,7 @@ internal class RunSessionRequest
ProjectPath = projectLaunchConfig.ProjectPath,
Debug = string.Equals(projectLaunchConfig.LaunchMode, DebugLaunchMode, StringComparison.OrdinalIgnoreCase),
Arguments = Arguments,
- Environment = Environment.Select(envVar => new KeyValuePair(envVar.Name, envVar.Value!)),
+ Environment = Environment.Select(envVar => new KeyValuePair(envVar.Name, envVar.Value ?? "")),
LaunchProfile = projectLaunchConfig.LaunchProfile,
DisableLaunchProfile = projectLaunchConfig.DisableLaunchProfile
};
diff --git a/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs b/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs
index 59787ed4f47b..f4080ea5a032 100644
--- a/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs
+++ b/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs
@@ -84,6 +84,10 @@ private static async Task ReceiveAndApplyUpdatesAsync(NamedPipeClientStream pipe
var payloadType = (RequestType)await pipeClient.ReadByteAsync(cancellationToken);
switch (payloadType)
{
+ case RequestType.SetEnvironmentVariables:
+ await SetEnvironmentVariablesAsync(pipeClient, cancellationToken).ConfigureAwait(false);
+ break;
+
case RequestType.ManagedCodeUpdate:
// Shouldn't get initial managed code updates when the debugger is attached.
// The debugger itself applies these updates when launching process with the debugger attached.
@@ -123,6 +127,29 @@ private static async Task ReceiveAndApplyUpdatesAsync(NamedPipeClientStream pipe
}
}
+ private static async Task SetEnvironmentVariablesAsync(NamedPipeClientStream pipeClient, CancellationToken cancellationToken)
+ {
+ var environmentVariableCount = await pipeClient.ReadInt32Async(cancellationToken).ConfigureAwait(false);
+
+ Log($"Setting environment variables ({environmentVariableCount})");
+
+ for (var i = 0; i < environmentVariableCount; i++)
+ {
+ var name = await pipeClient.ReadStringAsync(cancellationToken).ConfigureAwait(false);
+ var value = await pipeClient.ReadStringAsync(cancellationToken).ConfigureAwait(false);
+ try
+ {
+ Environment.SetEnvironmentVariable(name, value);
+ }
+ catch (Exception e)
+ {
+ Log($"Unable to set environment variable '{name}' = '{value}': {e.Message}");
+ }
+ }
+
+ await pipeClient.WriteAsync([(byte)ResponseType.EnvironmentVariablesSet], cancellationToken).ConfigureAwait(false);
+ }
+
private static async ValueTask ReadAndApplyManagedCodeUpdateAsync(
NamedPipeClientStream pipeClient,
HotReloadAgent agent,
diff --git a/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs b/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs
index 62686ddc226c..7feb4e525f42 100644
--- a/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs
+++ b/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs
@@ -22,9 +22,15 @@ internal interface IUpdateRequest : IRequest
internal enum RequestType
{
- ManagedCodeUpdate = 1,
- StaticAssetUpdate = 2,
- InitialUpdatesCompleted = 3,
+ SetEnvironmentVariables = 1,
+ ManagedCodeUpdate = 2,
+ StaticAssetUpdate = 3,
+ InitialUpdatesCompleted = 4,
+}
+
+internal enum ResponseType : byte
+{
+ EnvironmentVariablesSet = 2,
}
internal readonly struct ManagedCodeUpdateRequest(IReadOnlyList deltas, ResponseLoggingLevel responseLoggingLevel) : IUpdateRequest
diff --git a/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs b/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs
index a8333af46546..5751b304481f 100644
--- a/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs
+++ b/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs
@@ -37,6 +37,7 @@ internal sealed class BrowserRefreshServer : IAsyncDisposable, IStaticAssetChang
private readonly TaskCompletionSource _terminateWebSocket;
private readonly TaskCompletionSource _browserConnected;
private readonly string? _environmentHostName;
+ private readonly ImmutableArray _autoReloadWebSocketOrigins;
// initialized by StartAsync
private IHost? _refreshServer;
@@ -52,6 +53,7 @@ public BrowserRefreshServer(EnvironmentOptions options, IReporter reporter)
_terminateWebSocket = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_browserConnected = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_environmentHostName = EnvironmentVariables.AutoReloadWSHostName;
+ _autoReloadWebSocketOrigins = EnvironmentVariables.AutoReloadWSOrigins;
}
public async ValueTask DisposeAsync()
@@ -118,10 +120,21 @@ public async ValueTask StartAsync(CancellationToken cancellationToken)
builder.UseUrls($"http://{hostName}:0");
}
+ var allowedHosts = new List() { "localhost", "127.0.0.1", "[::1]" };
+ if (!_autoReloadWebSocketOrigins.IsDefault)
+ {
+ allowedHosts.AddRange(_autoReloadWebSocketOrigins);
+ }
+
+ if (_environmentHostName != null)
+ {
+ allowedHosts.Add(_environmentHostName);
+ }
+
builder.Configure(app =>
{
app.UseWebSockets();
- app.Run(WebSocketRequestAsync);
+ app.Run(context => WebSocketRequestAsync(context, [.. allowedHosts]));
});
})
.Build();
@@ -159,11 +172,19 @@ private IEnumerable GetServerUrls(IHost server)
];
}
- private async Task WebSocketRequestAsync(HttpContext context)
+ private async Task WebSocketRequestAsync(HttpContext context, ImmutableArray allowedHosts)
{
if (!context.WebSockets.IsWebSocketRequest)
{
- context.Response.StatusCode = 400;
+ context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ return;
+ }
+
+ // check the domain of the Origin header:
+ if (!Uri.TryCreate(context.Request.Headers.Origin.FirstOrDefault(), UriKind.Absolute, out var originUri) ||
+ !allowedHosts.Contains(originUri.Host, StringComparer.OrdinalIgnoreCase))
+ {
+ context.Response.StatusCode = StatusCodes.Status403Forbidden;
return;
}
diff --git a/src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs b/src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs
index 650336813222..9d9e740b44c9 100644
--- a/src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs
+++ b/src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections.Immutable;
+
namespace Microsoft.DotNet.Watch;
internal static class EnvironmentVariables
@@ -46,6 +48,14 @@ public static class Names
public static string TestOutputDir => Environment.GetEnvironmentVariable("__DOTNET_WATCH_TEST_OUTPUT_DIR") ?? "";
public static string? AutoReloadWSHostName => Environment.GetEnvironmentVariable("DOTNET_WATCH_AUTO_RELOAD_WS_HOSTNAME");
+
+ ///
+ /// A list of additional domains, other than localhost and ,
+ /// allowed as origins of connections to the broser refresh web socket.
+ /// Use when the browser opens the app on a custom domain.
+ ///
+ public static ImmutableArray AutoReloadWSOrigins = ReadList("DOTNET_WATCH_AUTO_RELOAD_WS_ORIGINS", separators: [';', ',']);
+
public static string? BrowserPath => Environment.GetEnvironmentVariable("DOTNET_WATCH_BROWSER_PATH");
private static bool ReadBool(string variableName)
@@ -53,4 +63,9 @@ private static bool ReadBool(string variableName)
private static TimeSpan ReadTimeSpan(string variableName, TimeSpan defaultValue)
=> Environment.GetEnvironmentVariable(variableName) is var value && long.TryParse(value, out var intValue) && intValue >= 0 ? TimeSpan.FromMilliseconds(intValue) : defaultValue;
+
+ private static ImmutableArray ReadList(string variableName, char[] separators)
+ => Environment.GetEnvironmentVariable(variableName) is { } value
+ ? [.. value.Split(separators, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)]
+ : [];
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs
index 617d7d733856..a370363d128c 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs
@@ -31,7 +31,7 @@ public override void Dispose()
// Do nothing.
}
- public override void CreateConnection(string namedPipeName, CancellationToken cancellationToken)
+ public override void CreateConnection(string namedPipeName, IReadOnlyCollection<(string name, string value)> environmentVariables, CancellationToken cancellationToken)
{
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs
index 2499e85e13c1..6ca7a21090c2 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs
@@ -19,10 +19,10 @@ public override void Dispose()
_wasmApplier.Dispose();
}
- public override void CreateConnection(string namedPipeName, CancellationToken cancellationToken)
+ public override void CreateConnection(string namedPipeName, IReadOnlyCollection<(string name, string value)> environmentVariables, CancellationToken cancellationToken)
{
- _wasmApplier.CreateConnection(namedPipeName, cancellationToken);
- _hostApplier.CreateConnection(namedPipeName, cancellationToken);
+ _wasmApplier.CreateConnection(namedPipeName, environmentVariables, cancellationToken);
+ _hostApplier.CreateConnection(namedPipeName, environmentVariables, cancellationToken);
}
public override Task WaitForProcessRunningAsync(CancellationToken cancellationToken)
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
index 15c4ea831d9b..cc13196a5532 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
@@ -132,7 +132,7 @@ private static DeltaApplier CreateDeltaApplier(HotReloadProfile profile, Project
// It is important to first create the named pipe connection (delta applier is the server)
// and then start the process (named pipe client). Otherwise, the connection would fail.
- deltaApplier.CreateConnection(namedPipeName, processCommunicationCancellationSource.Token);
+ deltaApplier.CreateConnection(namedPipeName, projectOptions.LaunchEnvironmentVariables, processCommunicationCancellationSource.Token);
processSpec.OnExit += (_, _) =>
{
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs
index 5d7f507984cc..02e678789179 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs
@@ -17,7 +17,7 @@ internal sealed class DefaultDeltaApplier(IReporter reporter) : SingleProcessDel
private NamedPipeServerStream? _pipe;
private bool _managedCodeUpdateFailedOrCancelled;
- public override void CreateConnection(string namedPipeName, CancellationToken cancellationToken)
+ public override void CreateConnection(string namedPipeName, IReadOnlyCollection<(string name, string value)> environmentVariables, CancellationToken cancellationToken)
{
_pipe = new NamedPipeServerStream(namedPipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly);
@@ -38,6 +38,10 @@ async Task> ConnectAsync()
var capabilities = (await ClientInitializationResponse.ReadAsync(_pipe, cancellationToken)).Capabilities;
Reporter.Verbose($"Capabilities: '{capabilities}'");
+
+ // Initialize process:
+ await SetEnvironmentVariablesAsync(environmentVariables, cancellationToken);
+
return [.. capabilities.Split(' ')];
}
catch (EndOfStreamException)
@@ -50,7 +54,7 @@ async Task> ConnectAsync()
// pipe might throw another exception when forcibly closed on process termination:
if (!cancellationToken.IsCancellationRequested)
{
- Reporter.Error($"Failed to read capabilities: {e.Message}");
+ Reporter.Error($"Failed to read capabilities: {e}");
}
return [];
@@ -58,6 +62,32 @@ async Task> ConnectAsync()
}
}
+ public async ValueTask SetEnvironmentVariablesAsync(IReadOnlyCollection<(string name, string value)> environmentVariables, CancellationToken cancellationToken)
+ {
+ if (environmentVariables.Count == 0)
+ {
+ return;
+ }
+
+ Debug.Assert(_pipe != null);
+
+ // Send a request to set environment variables:
+ await _pipe.WriteAsync((byte)RequestType.SetEnvironmentVariables, cancellationToken);
+ await _pipe.WriteAsync(environmentVariables.Count, cancellationToken);
+ foreach (var (name, value) in environmentVariables)
+ {
+ await _pipe.WriteAsync(name, cancellationToken);
+ await _pipe.WriteAsync(value, cancellationToken);
+ }
+
+ // Wait until the environment variables are set in the target process:
+ var responseType = (ResponseType)await _pipe.ReadByteAsync(cancellationToken);
+ if (responseType != ResponseType.EnvironmentVariablesSet)
+ {
+ throw new InvalidOperationException($"Unexpected response received from the agent: {responseType}");
+ }
+ }
+
public override Task WaitForProcessRunningAsync(CancellationToken cancellationToken)
// Should only be called after CreateConnection
=> _capabilitiesTask ?? throw new InvalidOperationException();
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/DeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/DeltaApplier.cs
index 852c9295e949..154bb04cfc4c 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/DeltaApplier.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/DeltaApplier.cs
@@ -14,7 +14,7 @@ internal abstract class DeltaApplier(IReporter reporter) : IDisposable
public static readonly string StartupHookPath = Path.Combine(AppContext.BaseDirectory, "hotreload", "Microsoft.Extensions.DotNetDeltaApplier.dll");
- public abstract void CreateConnection(string namedPipeName, CancellationToken cancellationToken);
+ public abstract void CreateConnection(string namedPipeName, IReadOnlyCollection<(string name, string value)> environmentVariables, CancellationToken cancellationToken);
///
/// Waits for the application process to start.
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs b/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
index c68073001705..6d0e82c2740c 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
@@ -61,18 +61,6 @@ public EnvironmentOptions EnvironmentOptions
var environmentBuilder = EnvironmentVariablesBuilder.FromCurrentEnvironment();
var namedPipeName = Guid.NewGuid().ToString();
- foreach (var (name, value) in projectOptions.LaunchEnvironmentVariables)
- {
- // ignore dotnet-watch reserved variables -- these shouldn't be set by the project
- if (name.Equals(EnvironmentVariables.Names.AspNetCoreHostingStartupAssemblies, StringComparison.OrdinalIgnoreCase) ||
- name.Equals(EnvironmentVariables.Names.DotNetStartupHooks, StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- environmentBuilder.SetVariable(name, value);
- }
-
// override any project settings:
environmentBuilder.SetVariable(EnvironmentVariables.Names.DotnetWatch, "1");
environmentBuilder.SetVariable(EnvironmentVariables.Names.DotnetWatchIteration, (Iteration + 1).ToString(CultureInfo.InvariantCulture));
diff --git a/test/Microsoft.NET.TestFramework/TestCommandLine.cs b/test/Microsoft.NET.TestFramework/TestCommandLine.cs
index 2519a063e819..7d71ff429037 100644
--- a/test/Microsoft.NET.TestFramework/TestCommandLine.cs
+++ b/test/Microsoft.NET.TestFramework/TestCommandLine.cs
@@ -43,7 +43,7 @@ public static TestCommandLine Parse(string[] args)
{
RemainingArgs = new List()
};
- Stack argStack = new(args.Reverse());
+ Stack argStack = new(((IEnumerable)args).Reverse());
while (argStack.Any())
{
diff --git a/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs b/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
index 0914da6c407f..e5ff04117461 100644
--- a/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
+++ b/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
@@ -650,6 +650,9 @@ public async Task Aspire()
App.Start(testAsset, ["-lp", "http"], relativeProjectDirectory: "WatchAspire.AppHost", testFlags: TestFlags.ReadKeyFromStdin);
+ // DEBUG_* environment variables should be set for app host process:
+ await App.WaitUntilOutputContains("Setting environment variables (3)");
+
await App.AssertWaitingForChanges();
// check that Aspire server output is logged via dotnet-watch reporter: