Skip to content

Migration guide (from v2.5 to v3.0)

Alexey Golub edited this page Feb 23, 2020 · 12 revisions

CliWrap v3.0 introduced a lot of breaking changes. This guide should cover most of the common scenarios with suggestions on how to migrate your code to the latest version of CliWrap. If you still have problems or questions, create a new issue.

Basic execution scenarios

CliWrap v2.5

var result = await Cli.Wrap("cli.exe")
    .SetArguments("Hello world!")
    .ExecuteAsync();

var exitCode = result.ExitCode;
var stdOut = result.StandardOutput;
var stdErr = result.StandardError;
var startTime = result.StartTime;
var exitTime = result.ExitTime;
var runTime = result.RunTime;

CliWrap v3.0

var result = await Cli.Wrap("cli.exe")
    .WithArguments("Hello world!")
    .ExecuteBufferedAsync();

var exitCode = result.ExitCode;
var stdOut = result.StandardOutput;
var stdErr = result.StandardError;
var startTime = result.StartTime;
var exitTime = result.ExitTime;
var runTime = result.RunTime;

If you're not interested in buffering stdout and stderr, you can now also do the following:

var result = await Cli.Wrap("cli.exe")
    .WithArguments("Hello world!")
    .ExecuteAsync();

* Note, synchronous Execute() is now gone. Use ExecuteAsync() or, if you really can't, resort to ExecuteAsync().GetAwaiter().GetResult().

** Note, ExecuteAndForget() is now gone. If you really need to, use ExecuteAsync() without awaiting it.

Command configuration

Setting arguments

CliWrap v2.5

Cli.Wrap("foo").SetArguments("bar baz");

CliWrap v3.0

Cli.Wrap("foo").WithArguments("bar baz");

Or

Cli.Wrap("foo").WithArguments(a => a.Add("bar").Add("baz"));

Setting working directory

CliWrap v2.5

Cli.Wrap("foo").SetWorkingDirectory("path")

CliWrap v3.0

Cli.Wrap("foo").WithWorkingDirectory("path")

Setting environment variables

CliWrap v2.5

Cli.Wrap("foo")
    .SetEnvironmentVariable("var1", "value1")
    .SetEnvironmentVariable("var2", "value2")

CliWrap v3.0

Cli.Wrap("foo").WithEnvironmentVariables(e => e
    .Set("var1", "value1")
    .Set("var2", "value2"));

Configuring validation

CliWrap v2.5

Cli.Wrap("foo")
    .EnableExitCodeValidation(true)
    .EnableStandardErrorValidation(true)

CliWrap v3.0

Cli.Wrap("foo")
    .WithValidation(CommandResultValidation.ZeroExitCode);

Or

Cli.Wrap("foo")
    .WithValidation(CommandResultValidation.None);

* Note, standard error validation has been removed entirely.

** Note, other configuration options (encoding, callbacks, etc) no longer exist and have been replaced with different execution models.

*** Note, in addition to the changes listed above, WithXyz() methods now return new immutable objects, instead of modifying the original instance.

Setting encoding

CliWrap v2.5

await result = Cli.Wrap("foo")
    .SetStandardOutputEncoding(Encoding.UTF8)
    .SetStandardErrorEncoding(Encoding.UTF8)
    .ExecuteAsync();

CliWrap v3.0

await result = Cli.Wrap("foo")
    .ExecuteBufferedAsync(Encoding.UTF8, Encoding.UTF8);

Or

await result = Cli.Wrap("foo")
    .ExecuteBufferedAsync(Encoding.UTF8);

Callbacks

Callbacks have been removed, but you can achieve the same result through piping or event streams.

CliWrap v2.5

var result = await Cli.Wrap("cli.exe")
    .SetStandardOutputCallback(l => Console.WriteLine($"StdOut> {l}"))
    .SetStandardErrorCallback(l => Console.WriteLine($"StdErr> {l}"))
    .ExecuteAsync();

CliWrap v3.0

With piping:

var result = await Cli.Wrap("cli.exe")
    .WithStandardOutputPipe(PipeTarget.ToDelegate(l => Console.WriteLine($"StdOut> {l}")))
    .WithStandardErrorPipe(PipeTarget.ToDelegate(l => Console.WriteLine($"StdErr> {l}")))
    .ExecuteAsync(); // await cmd.ExecuteBufferedAsync();

Or

var stdOutHandler = new Action<string>(l => Console.WriteLine($"StdOut> {l}"));
var stdErrHandler = new Action<string>(l => Console.WriteLine($"StdErr> {l}"));

var cmd = Cli.Wrap("cli.exe") | (stdOutHandler, stdErrHandler);
var result = await cmd.ExecuteAsync(); // await cmd.ExecuteBufferedAsync();

With async event streams:

await foreach (var cmdEvent in Cli.Wrap("cli.exe").ListenAsync())
{
    switch (cmdEvent)
    {
        case StandardOutputCommandEvent stdOut:
            Console.WriteLine($"StdOut> {stdOut.Text}");
            break;
        case StandardErrorCommandEvent stdErr:
            Console.WriteLine($"StdErr> {stdErr.Text}");
            break;
    }
}

With observable event streams:

await Cli.Wrap("cli.exe").Observe().ForEachAsync(cmdEvent =>
{
    switch (cmdEvent)
    {
        case StandardOutputCommandEvent stdOut:
            Console.WriteLine($"StdOut> {stdOut.Text}");
            break;
        case StandardErrorCommandEvent stdErr:
            Console.WriteLine($"StdErr> {stdErr.Text}");
            break;
    }
});

Cancellation

CliWrap v2.5

using var cts = new CancellationTokenSource();

cts.CancelAfter(TimeSpan.FromSeconds(5));

var result = await Cli.Wrap("cli.exe")
    .SetCancellationToken(cts.Token)
    .ExecuteAsync();

CliWrap v3.0

using var cts = new CancellationTokenSource();

cts.CancelAfter(TimeSpan.FromSeconds(5));

var result = await Cli.Wrap("cli.exe")
    .ExecuteAsync(cts.Token);

Piping stdin

CliWrap v3.0 has support for a very wide range of piping scenarios while older versions of CliWrap only supported piping stdin from a regular stream or a string.

CliWrap v2.5

var result = await Cli.Wrap("cli.exe")
    .SetStandardInput("Hello world from stdin!")
    .ExecuteAsync();

CliWrap v3.0

var result = await Cli.Wrap("cli.exe")
    .WithStandardInputPipe(PipeSource.FromString("Hello world from stdin!"))
    .ExecuteAsync();

Or

var result = await ("Hello world from stdin!" | Cli.Wrap("cli.exe")).ExecuteAsync();

More info on what you can do with piping you can find in the readme.

Clone this wiki locally