Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow environment variables to be set to null to remove them #110

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions CliWrap.Tests/ConfigurationSpecs.cs
Original file line number Diff line number Diff line change
@@ -133,7 +133,7 @@ public void Environment_variables_can_be_set()
var cmd = Cli.Wrap("foo").WithEnvironmentVariables(e => e.Set("xxx", "xxx"));

// Act
var cmdOther = cmd.WithEnvironmentVariables(new Dictionary<string, string>
var cmdOther = cmd.WithEnvironmentVariables(new Dictionary<string, string?>
{
["name"] = "value",
["key"] = "door"
@@ -142,7 +142,7 @@ public void Environment_variables_can_be_set()
// Assert
cmd.Should().BeEquivalentTo(cmdOther, o => o.Excluding(c => c.EnvironmentVariables));
cmd.EnvironmentVariables.Should().NotBeEquivalentTo(cmdOther.EnvironmentVariables);
cmdOther.EnvironmentVariables.Should().BeEquivalentTo(new Dictionary<string, string>
cmdOther.EnvironmentVariables.Should().BeEquivalentTo(new Dictionary<string, string?>
{
["name"] = "value",
["key"] = "door"
@@ -164,7 +164,7 @@ public void Environment_variables_can_be_set_with_a_builder()
// Assert
cmd.Should().BeEquivalentTo(cmdOther, o => o.Excluding(c => c.EnvironmentVariables));
cmd.EnvironmentVariables.Should().NotBeEquivalentTo(cmdOther.EnvironmentVariables);
cmdOther.EnvironmentVariables.Should().BeEquivalentTo(new Dictionary<string, string>
cmdOther.EnvironmentVariables.Should().BeEquivalentTo(new Dictionary<string, string?>
{
["name"] = "value",
["key"] = "door"
@@ -228,4 +228,4 @@ public void Stderr_pipe_can_be_set()
cmd.StandardErrorPipe.Should().NotBeSameAs(cmdOther.StandardErrorPipe);
}
}
}
}
61 changes: 59 additions & 2 deletions CliWrap.Tests/EnvironmentSpecs.cs
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ public async Task Command_can_be_executed_with_a_custom_working_directory()
public async Task Command_can_be_executed_with_custom_environment_variables()
{
// Arrange
var env = new Dictionary<string, string>
var env = new Dictionary<string, string?>
{
["foo"] = "bar",
["hello"] = "world",
@@ -59,5 +59,62 @@ public async Task Command_can_be_executed_with_custom_environment_variables()
"[Path] = there"
});
}

[Fact(Timeout = 15000)]
public async Task Command_can_be_executed_with_null_environment_variables_to_unset_them()
{
// Arrange

// Generate unique environment variable names to be used during the test
var keyToDelete = "CLIWRAP_TEST_DELETE_" + string.Concat(Guid.NewGuid().ToString().Take(5));
var keyToSet = "CLIWRAP_TEST_KEEP_" + string.Concat(Guid.NewGuid().ToString().Take(5));

var env = new Dictionary<string, string?>
{
// Stting the variable to null, should remove
// the environment variable
[keyToDelete] = null,

[keyToSet] = "value-is-set",
};

// Set the variable in the current environment so that we can verify it is removed
Environment.SetEnvironmentVariable(keyToDelete, "value-is-set");

// Ensure the variable is not set in the current environment
Environment.SetEnvironmentVariable(keyToSet, null);

try
{
var cmd = Cli.Wrap("dotnet")
.WithArguments(a => a
.Add(Dummy.Program.FilePath)
.Add("print-environment-variables"))
.WithEnvironmentVariables(env);

// Act
var result = await cmd.ExecuteBufferedAsync();
var stdOutLines = result.StandardOutput.Split(Environment.NewLine);
Array.Sort(stdOutLines);

// Assert
stdOutLines.Should()
.Contain(new[]
{
$"[{keyToSet}] = value-is-set",
})
.And
.NotContain(new[]
{
$"[{keyToDelete}] = value-is-set",
});
}
finally
{
// Remove both environment variables by setting them to null
Environment.SetEnvironmentVariable(keyToDelete, null);
Environment.SetEnvironmentVariable(keyToSet, null);
}
}
}
}
}
8 changes: 4 additions & 4 deletions CliWrap/Builders/EnvironmentVariablesBuilder.cs
Original file line number Diff line number Diff line change
@@ -8,12 +8,12 @@ namespace CliWrap.Builders
/// </summary>
public class EnvironmentVariablesBuilder
{
private readonly IDictionary<string, string> _envVars = new Dictionary<string, string>(StringComparer.Ordinal);
private readonly IDictionary<string, string?> _envVars = new Dictionary<string, string?>(StringComparer.Ordinal);

/// <summary>
/// Sets an environment variable with the specified name to the specified value.
/// </summary>
public EnvironmentVariablesBuilder Set(string name, string value)
public EnvironmentVariablesBuilder Set(string name, string? value)
{
_envVars[name] = value;
return this;
@@ -22,6 +22,6 @@ public EnvironmentVariablesBuilder Set(string name, string value)
/// <summary>
/// Builds the resulting environment variables.
/// </summary>
public IReadOnlyDictionary<string, string> Build() => new Dictionary<string, string>(_envVars);
public IReadOnlyDictionary<string, string?> Build() => new Dictionary<string, string?>(_envVars);
}
}
}
22 changes: 16 additions & 6 deletions CliWrap/Command.cs
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ public partial class Command : ICommandConfiguration
public Credentials Credentials { get; }

/// <inheritdoc />
public IReadOnlyDictionary<string, string> EnvironmentVariables { get; }
public IReadOnlyDictionary<string, string?> EnvironmentVariables { get; }

/// <inheritdoc />
public CommandResultValidation Validation { get; }
@@ -54,7 +54,7 @@ public Command(
string arguments,
string workingDirPath,
Credentials credentials,
IReadOnlyDictionary<string, string> environmentVariables,
IReadOnlyDictionary<string, string?> environmentVariables,
CommandResultValidation validation,
PipeSource standardInputPipe,
PipeTarget standardOutputPipe,
@@ -79,7 +79,7 @@ public Command(string targetFilePath) : this(
string.Empty,
Directory.GetCurrentDirectory(),
Credentials.Default,
new Dictionary<string, string>(),
new Dictionary<string, string?>(),
CommandResultValidation.ZeroExitCode,
PipeSource.Null,
PipeTarget.Null,
@@ -170,7 +170,7 @@ public Command WithCredentials(Action<CredentialsBuilder> configure)
/// <summary>
/// Creates a copy of this command, setting the environment variables to the specified value.
/// </summary>
public Command WithEnvironmentVariables(IReadOnlyDictionary<string, string> environmentVariables) => new(
public Command WithEnvironmentVariables(IReadOnlyDictionary<string, string?> environmentVariables) => new(
TargetFilePath,
Arguments,
WorkingDirPath,
@@ -285,7 +285,17 @@ private ProcessStartInfo GetStartInfo()
}

foreach (var (key, value) in EnvironmentVariables)
result.Environment[key] = value;
{
// Workaround for https://github.com/dotnet/runtime/issues/34446
if (value is null)
{
result.Environment.Remove(key);
}
else
{
result.Environment[key] = value;
}
}

return result;
}
@@ -514,4 +524,4 @@ public partial class Command
public static Command operator |(Command source, Command target) =>
PipeSource.FromCommand(source) | target;
}
}
}
2 changes: 1 addition & 1 deletion CliWrap/ICommandConfiguration.cs
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ public interface ICommandConfiguration
/// <summary>
/// Environment variables set for the underlying process.
/// </summary>
IReadOnlyDictionary<string, string> EnvironmentVariables { get; }
IReadOnlyDictionary<string, string?> EnvironmentVariables { get; }

/// <summary>
/// Configured result validation strategy.