diff --git a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs new file mode 100644 index 0000000000..1464ab3511 --- /dev/null +++ b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs @@ -0,0 +1,185 @@ +using FluentAssertions; + +using System.CommandLine.Builder; +using System.CommandLine.Invocation; +using System.CommandLine.Parsing; +using System.Linq; +using System.Threading.Tasks; + +using Xunit; + +namespace System.CommandLine.Tests +{ + public class EnvironmentVariableDirectiveTests + { + private static readonly Random randomizer = new Random(Seed: 456476756); + private readonly string test_variable = $"TEST_ENVIRONMENT_VARIABLE{randomizer.Next()}"; + + [Fact] + public async Task Sets_environment_variable_to_value() + { + bool asserted = false; + string variable = test_variable; + const string value = "This is a test"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + Environment.GetEnvironmentVariable(variable).Should().Be(value); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env:{variable}={value}]" }); + + asserted.Should().BeTrue(); + } + + [Fact] + public async Task Trims_environment_variable_name() + { + bool asserted = false; + string variable = test_variable; + const string value = "This is a test"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + Environment.GetEnvironmentVariable(variable).Should().Be(value); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env: {variable} ={value}]" }); + + asserted.Should().BeTrue(); + } + + [Fact] + public async Task Trims_environment_variable_value() + { + bool asserted = false; + string variable = test_variable; + const string value = "This is a test"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + Environment.GetEnvironmentVariable(variable).Should().Be(value); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env:{variable}= {value} ]" }); + + asserted.Should().BeTrue(); + } + + [Fact] + public async Task Sets_environment_variable_value_containing_equals_sign() + { + bool asserted = false; + string variable = test_variable; + const string value = "This is = a test containing equals"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + Environment.GetEnvironmentVariable(variable).Should().Be(value); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env:{variable}={value}]" }); + + asserted.Should().BeTrue(); + } + + [Fact] + public async Task Ignores_environment_directive_without_equals_sign() + { + bool asserted = false; + string variable = test_variable; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + Environment.GetEnvironmentVariable(variable).Should().BeNull(); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env:{variable}]" }); + + asserted.Should().BeTrue(); + } + + [Fact] + public static async Task Ignores_environment_directive_with_empty_variable_name() + { + bool asserted = false; + string value = $"This is a test, random: {randomizer.Next()}"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + var env = Environment.GetEnvironmentVariables(); + env.Values.Cast().Should().NotContain(value); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env:={value}]" }); + + asserted.Should().BeTrue(); + } + + [Fact] + public static async Task Ignores_environment_directive_with_whitespace_variable_name() + { + bool asserted = false; + string value = $"This is a test, random: {randomizer.Next()}"; + var rootCommand = new RootCommand + { + Handler = CommandHandler.Create(() => + { + asserted = true; + var env = Environment.GetEnvironmentVariables(); + env.Values.Cast().Should().NotContain(value); + }) + }; + + var parser = new CommandLineBuilder(rootCommand) + .UseEnvironmentVariableDirective() + .Build(); + + await parser.InvokeAsync(new[] { $"[env: ={value}]" }); + + asserted.Should().BeTrue(); + } + } +} diff --git a/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs b/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs index 60a10ae445..a6946c8703 100644 --- a/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs +++ b/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs @@ -228,11 +228,38 @@ public static CommandLineBuilder UseDebugDirective( return builder; } + public static CommandLineBuilder UseEnvironmentVariableDirective( + this CommandLineBuilder builder) + { + builder.AddMiddleware((context, next) => + { + if (context.ParseResult.Directives.TryGetValues("env", out var directives)) + { + foreach (var envDirective in directives) + { + var components = envDirective.Split(new[] { '=' }, count: 2); + var variable = components.Length > 0 ? components[0].Trim() : string.Empty; + if (string.IsNullOrEmpty(variable) || components.Length < 2) + { + continue; + } + var value = components[1].Trim(); + SetEnvironmentVariable(variable, value); + } + } + + return next(context); + }, MiddlewareOrderInternal.EnvironmentVariableDirective); + + return builder; + } + public static CommandLineBuilder UseDefaults(this CommandLineBuilder builder) { return builder .UseVersionOption() .UseHelp() + .UseEnvironmentVariableDirective() .UseParseDirective() .UseDebugDirective() .UseSuggestDirective() diff --git a/src/System.CommandLine/Invocation/MiddlewareOrder.cs b/src/System.CommandLine/Invocation/MiddlewareOrder.cs index b233e8042e..da921a6fad 100644 --- a/src/System.CommandLine/Invocation/MiddlewareOrder.cs +++ b/src/System.CommandLine/Invocation/MiddlewareOrder.cs @@ -15,6 +15,7 @@ internal enum MiddlewareOrderInternal { Startup = -4000, ExceptionHandler = -3000, + EnvironmentVariableDirective = -2600, ConfigureConsole = -2500, RegisterWithDotnetSuggest = -2400, DebugDirective = -2300,