File: Context\EnvironmentVariables.cs
Web Access
Project: src\src\sdk\src\Dotnet.Watch\Watch\Microsoft.DotNet.HotReload.Watch.csproj (Microsoft.DotNet.HotReload.Watch)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Extensions.Logging;

namespace Microsoft.DotNet.Watch;

internal static class EnvironmentVariables
{
    public static class Names
    {
        public const string DotnetWatch = "DOTNET_WATCH";
        public const string DotnetWatchIteration = "DOTNET_WATCH_ITERATION";

        public const string DotnetLaunchProfile = "DOTNET_LAUNCH_PROFILE";
        public const string DotnetHostPath = "DOTNET_HOST_PATH";

        public const string DotNetWatchHotReloadNamedPipeName = HotReload.AgentEnvironmentVariables.DotNetWatchHotReloadNamedPipeName;
        public const string DotNetWatchHotReloadWebSocketEndpoint = HotReload.AgentEnvironmentVariables.DotNetWatchHotReloadWebSocketEndpoint;
        public const string DotNetWatchAgentWebSocketPort = "DOTNET_WATCH_AGENT_WEBSOCKET_PORT";
        public const string DotNetWatchAgentWebSocketSecurePort = "DOTNET_WATCH_AGENT_WEBSOCKET_SECURE_PORT";
        public const string DotNetStartupHooks = HotReload.AgentEnvironmentVariables.DotNetStartupHooks;
        public const string DotNetModifiableAssemblies = HotReload.AgentEnvironmentVariables.DotNetModifiableAssemblies;
        public const string HotReloadDeltaClientLogMessages = HotReload.AgentEnvironmentVariables.HotReloadDeltaClientLogMessages;

        public const string SuppressBrowserRefresh = "DOTNET_WATCH_SUPPRESS_BROWSER_REFRESH";
    }

    public static LogLevel? CliLogLevel
    {
        get
        {
            var value = Environment.GetEnvironmentVariable("DOTNET_CLI_CONTEXT_VERBOSE");
            return string.Equals(value, "trace", StringComparison.OrdinalIgnoreCase)
                ? LogLevel.Trace
                : ParseBool(value)
                ? LogLevel.Debug
                : null;
        }
    }

    public static bool IsPollingEnabled => ReadBool("DOTNET_USE_POLLING_FILE_WATCHER");
    public static bool SuppressEmojis => ReadBool("DOTNET_WATCH_SUPPRESS_EMOJIS");
    public static bool RestartOnRudeEdit => ReadBool("DOTNET_WATCH_RESTART_ON_RUDE_EDIT");
    public static TimeSpan? ProcessCleanupTimeout => ReadTimeSpanMilliseconds("DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS");

    public static string SdkRootDirectory =>
#if DEBUG
        Environment.GetEnvironmentVariable("DOTNET_WATCH_DEBUG_SDK_DIRECTORY") ?? "";
#else
        "";
#endif

    public static bool SuppressHandlingStaticWebAssets => ReadBool("DOTNET_WATCH_SUPPRESS_STATIC_FILE_HANDLING");
    public static bool SuppressMSBuildIncrementalism => ReadBool("DOTNET_WATCH_SUPPRESS_MSBUILD_INCREMENTALISM");
    public static bool SuppressLaunchBrowser => ReadBool("DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER");
    public static bool SuppressBrowserRefresh => ReadBool(Names.SuppressBrowserRefresh);

    public static TestFlags TestFlags => Environment.GetEnvironmentVariable("__DOTNET_WATCH_TEST_FLAGS") is { } value ? Enum.Parse<TestFlags>(value) : TestFlags.None;
    public static string TestOutputDir => Environment.GetEnvironmentVariable("__DOTNET_WATCH_TEST_OUTPUT_DIR") ?? "";

    public static string? BrowserWebSocketHostName => Environment.GetEnvironmentVariable("DOTNET_WATCH_AUTO_RELOAD_WS_HOSTNAME");

    /// <summary>
    /// Port used for browser WebSocket communication. Defaults to 0 (auto-assign) if not specified.
    /// </summary>
    public static int BrowserWebSocketPort => ReadInt("DOTNET_WATCH_AUTO_RELOAD_WS_PORT") ?? 0;

    /// <summary>
    /// Secure (HTTPS/WSS) port used for browser WebSocket communication. Defaults to 0 (auto-assign) if not specified.
    /// Only used if TLS is supported and enabled.
    /// </summary>
    public static int BrowserWebSocketSecurePort => ReadInt("DOTNET_WATCH_AUTO_RELOAD_WSS_PORT") ?? 0;

    public static string? BrowserPath => Environment.GetEnvironmentVariable("DOTNET_WATCH_BROWSER_PATH");

    /// <summary>
    /// Port for WebSocket hot reload communication. Used for projects with the HotReloadWebSockets capability.
    /// Mobile workloads (Android, iOS) add this capability. Defaults to 0 (auto-assign) if not specified.
    /// </summary>
    public static int AgentWebSocketPort => ReadInt(Names.DotNetWatchAgentWebSocketPort) ?? 0;

    /// <summary>
    /// Secure (HTTPS/WSS) port for WebSocket hot reload communication.
    /// If not specified, HTTPS is not enabled for the agent WebSocket server.
    /// </summary>
    public static int? AgentWebSocketSecurePort => ReadInt(Names.DotNetWatchAgentWebSocketSecurePort);

    private static bool ReadBool(string variableName)
        => ParseBool(Environment.GetEnvironmentVariable(variableName));

    internal static TimeSpan? ReadTimeSpanMilliseconds(string variableName)
        => Environment.GetEnvironmentVariable(variableName) is var value && long.TryParse(value, out var intValue) && intValue >= 0 ? TimeSpan.FromMilliseconds(intValue) : null;

    internal static TimeSpan? ReadTimeSpanSeconds(string variableName)
        => Environment.GetEnvironmentVariable(variableName) is var value && long.TryParse(value, out var intValue) && intValue >= 0 ? TimeSpan.FromSeconds(intValue) : null;

    private static int? ReadInt(string variableName)
        => Environment.GetEnvironmentVariable(variableName) is var value && int.TryParse(value, out var intValue) ? intValue : null;

    private static bool ParseBool(string? value)
        => value == "1" || bool.TryParse(value, out var boolValue) && boolValue;
}