Hi everyone!
Since June I've been working on version 2 of one of my .NET libraries, CliInvoke - a library focussed on making running external processes from .NET safer and easier. It started out as an alternative to CliWrap but has diverged in strategies and direction, with CliInvoke focussing more on separation of concerns and Dependency Injection support.
From my perspective it's fair to say that, although well intentioned, CliInvoke version 1 hasn't withstood the test of time from an API surface and ergonomics perspective.
I've tried to learn from version 1's failings and am now looking for feedback before I release version 2. The goal of CliInvoke remains the same as before but with improved ergonomics and a cleaner API surface.
Here's a quick overview of what can be done with CliInvoke v2 pre-release versions, though the Factory Interface approach isn't yet in a published version.
Command Execution
Factory Interface Approach
using AlastairLundy.CliInvoke.Core.Factories;
using AlastairLundy.CliInvoke.Core;
using AlastairLundy.CliIinvoke;
using Microsoft.Extensions.DependencyInjection;
// Dependency Injection setup code ommitted for clarity
IProcessConfigurationFactory processConfigFactory = serviceProvider.GetRequiredService<IProcessConfigurationFactory>();
IProcessConfigurationInvoker invoker = serviceProvider.GetRequiredService<IProcessConfigurationInvoker>();
ProcessConfiguration configuration = processConfigFactory.Create("path/to/exe", "arguments");
ProcessResult result = await invoker.ExecuteAsync(configuration, CancellationToken.None);
Builder Configuration
This approach is more complex but allows for greater configuration of ProcessConfiguration
.
using AlastairLundy.CliInvoke;
using AlastairLundy.CliInvoke.Builders;
using AlastairLundy.CliInvoke.Core;
using AlastairLundy.CliInvoke.Core.Builders;
using Microsoft.Extensions.DependencyInjection;
//Namespace and class code ommitted for clarity
// Dependency Injection setup code ommitted for clarity
IProcessConfigurationInvoker _processConfigInvoker = serviceProvider.GetRequiredService<IProcessConfigurationInvoker>();
// Fluently configure your Command.
IProcessConfigurationBuilder builder = new ProcessConfigurationBuilder("Path/To/Executable")
.WithArguments(["arg1", "arg2"])
.WithWorkingDirectory("/Path/To/Directory");
// Build it as a ProcessConfiguration object when you're ready to use it.
ProcessConfiguration config = builder.Build();
// Execute the process through ProcessInvoker and get the results.
ProcessResult result = await _processConfigInvoker.ExecuteAsync(config);
Buffered Command Execution
This is similar to Command Execution except with the Standard Output and Error redirected and saved to strings in the BufferedProcessResult
class, which inherits from the base ProcessResult
class.
Factory Interface Approach
using AlastairLundy.CliInvoke.Core.Factories;
using AlastairLundy.CliInvoke.Core;
using AlastairLundy.CliIinvoke;
using Microsoft.Extensions.DependencyInjection;
// Dependency Injection setup code ommitted for clarity
IProcessConfigurationFactory processConfigFactory = serviceProvider.GetRequiredService<IProcessConfigurationFactory>();
IProcessConfigurationInvoker invoker = serviceProvider.GetRequiredService<IProcessConfigurationInvoker>();
ProcessConfiguration configuration = processConfigFactory.Create("path/to/exe", "arguments");
BufferedProcessResult result = await invoker.ExecuteBufferedAsync(configuration, CancellationToken.None);
Builder Configuration
using AlastairLundy.CliInvoke;
using AlastairLundy.CliInvoke.Builders;
using AlastairLundy.CliInvoke.Core;
using AlastairLundy.CliInvoke.Core.Builders;
using Microsoft.Extensions.DependencyInjection;
//Namespace and class code ommitted for clarity
// Dependency Injection setup code ommitted for clarity
IProcessConfigurationInvoker _processConfigInvoker = serviceProvider.GetRequiredService<IProcessConfigurationInvoker>();
// Fluently configure your Command.
IProcessConfigurationBuilder builder = new ProcessConfigurationBuilder("Path/To/Executable")
.WithArguments(["arg1", "arg2"])
.WithWorkingDirectory("/Path/To/Directory")
.RedirectStandardOutput(true)
.RedirectStandardError(true);
// Build it as a ProcessConfiguration object when you're ready to use it.
ProcessConfiguration config = builder.Build();
// Execute the process through ProcessInvoker and get the results.
BufferedProcessResult result = await _processConfigInvoker.ExecuteBufferedAsync(config);
Closing Thoughts
I'd like to get your thoughts and opinions/any feedback you have.
Is there anything you think I should change in the library or anything that you think needs adding?
Thanks in advance!
Top comments (0)