red-gate/Tech-Radar

[Library] Commandline parsing

nyctef opened this issue ยท 13 comments

What change would you like to make to the tech radar?

Add CommandLineParser to Explore
Add NDesk.Options to Adopt
Add RedGate.Legacy.CommandLine to Endure

Why do you believe this is valuable to Redgate?

Some discussion in this thread: https://redgate.slack.com/archives/CEXRY1B0B/p1550056281025600 (thanks @chrisgeorge0911 for pointing out this should be on the radar :)

Most of our new commandline tools actually use Powershell instead of parsing their own commandline arguments, so we haven't needed a commandline parsing library in a while.

Older tools (such as the SQL Compare commandline) use the RedGate.Legacy.CommandLine package that's been pulled out of SHU - it's not ideal, but it's working well enough for the moment. I think NDesk.Options is a pretty good drop-in for these projects - it's currently being used by the existing Oracle tools and we've used for some of the pre-powershell DLM Automation tools in the past. While the project isn't under active maintenance (there's a single official nuget package from 2011), it still works, and the library itself is pretty tiny if we ever needed to take control of it for ourselves.

As an alternative, we'd like to have a go with CommandLineParser. The advantage here is that the library nicely supports having multiple 'verbs' (similar to git commit / git add / git status / etc). The reason we want to build a new commandline (instead of going for powershell) is that we believe Oracle users are much more linux-based and are less likely to adopt a solution that requires powershell. There are a few existing usages in RG, but nothing extensive: https://codesearch.red-gate.com/?q=%5CbCommandLineParser%5Cb&i=nope&files=&repos=

Where should this be on the tech radar?

Probably in radar_libraries.csv under Redgate/Public NuGet?

If this should be in the Explore ring, who is committed to exploring it?

Team Orca

I've used ManyConsole in production in the past and it is similar to NDesk.Options, but I found it much easier to work with.

fffej commented

NDesk.Options is in Adopt, but as far as I can tell doesn't have any maintainers? Should it be the successor Mono.Options? The package @adrianbanks mentions looks like it builds on top of Mono.Options.

Probably, yeah - I don't know how different Mono.Options is from NDesk.Options, but the docs look pretty similar: https://github.com/xamarin/XamarinComponents/tree/master/XPlat/Mono.Options#getting-started

I think ManyConsole was possibly originally built on NDesk.Options and has been switched over (I was surprised to see it based on Mono.Options when I looked at it the other day as I'm sure it relied on NDesk.Options when I last used it).

EDIT: It looks like NDesk.Options was renamed to Mono.Options.

We've been using Microsoft.Extensions.CommandLineUtils which has been working fine for us in Foundry.

Unfortunately it's not actively maintained by Microsoft anymore, but there's a community fork with active maintenance. We haven't found the original NuGet package lacking, though.

Only thing I'd say is that our command lines are very simple, so this package might not be enough for what's being discussed here.

fffej commented

Looking at the libraries, I'm finding it difficult to distinguish between what we should be using.

Does anyone have any strong opinions?

I haven't used any of the other libraries mentioned in this issue, but my preference would be anything that fits the following criteria:

  • Actively maintained
  • Supports .NET Core/Standard
  • Implementing it doesn't cause its code to scatter all over the codebase with horrible annotations and whatnot
  • Ideally, auto-generation of the help/usage

After a quick browse, my vote goes to CommandLineParser to go in Explore since it seems to tick all those boxes.

fffej commented

@nyctef Sounds like we're about ready for a PR - do you want to make one that summarizes this discussion and we can resolve this?

I forgot to mention - I did some spiking a few weeks back of the command line libraries mentioned here. The code is here for anyone wanting to compare them. All of the implementations do the same thing.

From working with them all, ManyConsole gives the cleanest API as it works on a command based structure. Each command defines its options and the action to take when executed. CommandLineParser was probably the simplest to set up for a simple command line, but I couldn't find a way of doing nested commands (similar to git's command line, for example).

It's probably also worth considering this library, as it's just been released by the .Net team and is part of the .Net Foundation, so is likely to gain some traction as it looks like it might be their new way of doing command line parsing.

I've found that CommandLineParser doesn't have any native support for subcommands, which makes it difficult to use for the sort of semantics (inspired by e.g. kubectl) that are used in Foundry's command lines, e.g.

estatectl get access-token a56bc4f`
estatectl get serverinfo --type mssql --connectionString "..."`

commandlineparser/commandline#69
commandlineparser/commandline#353

It's possible to work around it by handling multi-level parsing yourself in some way, but that's not ideal.

I previously evaluated System.CommandLine, mentioned by @adrianbanks, but while it's powerful I don't think it supports an attribute-based API at the moment, which makes it a bit less readable and more of a change if you're used to CommandLineParser. (They have a reflection-based prototype but it doesn't yet look viable for anything that's not very simple.)

I've found CommandLineUtils to probably be the best balance right now, supporting similar semantics to CommandLineParser but with support for subcommands, but it doesn't support option sets.

(I'm not sure where I should be recording this other than here.)

Also, CommandLineParser seems to be abandoned, really. :(
The last commit to master was on Jul 31, and my PRs (this and this) are dying there slowly since 15th Apr 2019.

I'd propose to move CommandLineParser from Explore to Endure/Retire. :(
Any thoughts from the team that uses it? @nyctef?

FWIW I'd probably prefer to use System.CommandLine over handrolling something else

The main problem I have with System.CommandLine is that it still feels pretty early. The builder API is pretty flexible although verbose, but the reflection-based one (Dragonfruit) is probably too basic, and the documentation is sparse. Most significantly, its NuGet package is called System.CommandLine.Experimental and is in prerelease (which doesn't really give "use this in production" feelings). If it felt more mature then I'd probably favour it.