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

NDesk.Options - spike for better command line argument handling #572

Closed
wants to merge 23 commits into from

Conversation

serra
Copy link
Contributor

@serra serra commented Aug 15, 2015

Work in Progress

This is a spike for #428 to use NDesk.Options to parse command line arguments.
This is the same command line argument parser as used in the Mono project.

First step is to see if we can incorporate this while being completely backwards compatible. This will give us the benefit of a cleaner argument-parsing code base immediately. And it will als allow to add shortcuts immediately and without any pain.

So far seems NDesk.Options can be used while being completely backwards compatible. I think it will take me a couple of hours more to get to a completely backwards compatible implementation.

issues to address

  • -updateAssemblyInfo takes optional parameter. This is a bit hard to fix while maintaining full backwards compatibility, because for optional values, the notation -updateassemlyinfo filetochange.cs is not allowed; only -updateassemlyinfo:filetochange.cs or -updateassemlyinfo=filetochange.cs. We can only support this if we include it as another special case. (that means excluding it from the option set and handle it as an additional argument)
  • -execArgs is case sensitive; tested with both execargs and execArgs; probably should create case insensitive option set for backwards compatbility
  • write tests for untested arguments
  • update helpwriter
  • See if we're missing some arguments; there are fields in Arguments.cs that are not tested

Although only -o --option arguments are listed, /o and /option are supported too.

Use convention to derive a SemVer product version from a GitFlow or GitHub based repository.

      --path, --targetpath=VALUE
                             The path to inspect
                               Defaults to the current directory.
  -i, --init                 Start the configuration utility for gitversion
  -h, -?, --help             Show this message and exit
  -l, --log=VALUE            Path to logfile
      --exec=VALUE           Executes target executable making GitVersion 
                               variables available as environmental variables
      --execargs=VALUE       Arguments for the executable specified by --exec
      --proj=VALUE           Build an msbuild file making GitVersion 
                               variables available as msbuild properties
      --projargs=VALUE       Additional arguments to pass to msbuild
  -u, --username=VALUE       Username in case authentication is required
  -p, --password=VALUE       Password in case authentication is required
  -o, --output=VALUE         Determines the output to the console
                               Can be either 'json' or 'buildserver', will 
                               default to 'json'.
      --url=VALUE            Url to remote git repository
  -b, --remotebranch=VALUE   Name of the branch to use on the remote 
                               repository, must be used in combination with --
                               url
      --updateassemblyinfo[=VALUE]
                             * Will recursively search for all 'AssemblyInf-
                               o.cs' files in the git repo and update them
                               To use another filename use --
                               updateassemblyinfo:[another-assemblyinfo-
                               filename.cs]
      --updateassemblyinfoname
                             * Deprecated: use --
                               updateassemblyinfo:[assemblyinfofilename.cs] 
                               instead.
      --dynamicrepolocation=VALUE
                             Override locations dynamic repositories are 
                               clonden to
                               Defaults to %tmp%.
  -c, --commit=VALUE         The commit id to inspect
                               Defaults to the latest available commit on the 
                               specified branch.
  -v, --showvariable=VALUE   Used in conjuntion with /output json, will 
                               output just a particular variable
      --nofetch              
      --showconfig           Outputs the effective GitVersion config
                               Outputs the defaults and custom from GitVersio-
                               n.yaml in yaml format.
      --assemblyversionformat
                             Deprecated: use AssemblyVersioningScheme 
                               configuration value instead.

@serra
Copy link
Contributor Author

serra commented Aug 15, 2015

image

@serra serra changed the title 428 n desk spike NDesk.Options - spike for better command line argument handling Aug 15, 2015
@serra
Copy link
Contributor Author

serra commented Aug 16, 2015

Note that 6e1db37 is NOT backwards compatible.

@serra
Copy link
Contributor Author

serra commented Aug 16, 2015

@JakeGinnivan I like the state of this PR at 6e1db37.

However, this is a breaking change. It would break any client doing:

gitversion /updateassemlyinfo filetochange.cs

That's currently supported.

What does work is:

gitversion /updateassemlyinfo
gitversion /updateassemlyinfo:filetochange.cs
gitversion /updateassemlyinfo=filetochange.cs
gitversion --updateassemlyinfo:filetochange.cs
gitversion --updateassemlyinfo:filetochange.cs

More specific, -updateAssemblyInfo takes optional parameter. This is a bit hard to fix while maintaining full backwards compatibility, because for optional values, the notation -updateassemlyinfo filetochange.cs is not allowed; only -updateassemlyinfo:filetochange.cs or -updateassemlyinfo=filetochange.cs.

A complete list of accepted arguments can be reviewed here.

We can only support this if we include it as another special case, which would result in some not-so-pretty-yet-easily-identifiable code.

Suggestions? Do we need to be fully backwards compatible?

@orjan
Copy link
Contributor

orjan commented Aug 16, 2015

Just a thought, I'm not sure if it's possible or good way, but here's my idea. What about patching the args so they'll work with the new parser? If this is possible it's easy to display that some arguments are deprecated and will be removed in a future version?

On 16 aug 2015, at 16:46, Marijn van der Zee notifications@github.com wrote:

@JakeGinnivan I like the state of this PR at 6e1db37.

However, this is a breaking change. It would break any client doing:

gitversion /updateassemlyinfo filetochange.cs
That's currently supported.

What does work is:

gitversion /updateassemlyinfo
gitversion /updateassemlyinfo:filetochange.cs
gitversion /updateassemlyinfo=filetochange.cs
gitversion --updateassemlyinfo:filetochange.cs
gitversion --updateassemlyinfo:filetochange.cs
More specific, -updateAssemblyInfo takes optional parameter. This is a bit hard to fix while maintaining full backwards compatibility, because for optional values, the notation -updateassemlyinfo filetochange.cs is not allowed; only -updateassemlyinfo:filetochange.cs or -updateassemlyinfo=filetochange.cs.

A complete list of accepted arguments can be reviewed here.

We can only support this if we include it as another special case, which would result in some not-so-pretty-yet-easily-identifiable code.

Suggestions? Do we need to be fully backwards compatible?


Reply to this email directly or view it on GitHub.

@serra
Copy link
Contributor Author

serra commented Aug 16, 2015

@orjan yeah, I did that for other args such as the positional args "init" and "path". I can do that for updateassemlyinfo but it gets kind of messy hence my question @JakeGinnivan if it's worth it or if we can accept a breaking change.

@JakeGinnivan
Copy link
Contributor

Before we make this decision, I think we need to look at the command line args again. If we were to go back to the drawing board and come up with new command line arguments, how much better would they be than what we have now. If we can make a drastic improvement to the command line usage by completely breaking it and bumping to v4, then I am leaning towards doing that.

We could always create a GitVersionCompatShim.exe which provides backwards compatibility on a command line level.

@JakeGinnivan
Copy link
Contributor

If one of you could come up with what a rethought out command line structure looked like, that would be a great step towards the compat decision

@serra
Copy link
Contributor Author

serra commented Aug 17, 2015

OK, I'll cook up an overview of proposed command line interface, indicating breaking changes.

@SimonCropp
Copy link
Contributor

FWIW I have never liked NDesk.options. it is abandonware and the syntax is ugly and convoluted.

Eg the git repo link here http://www.ndesk.org/Options is dead, even the forks, and the forks of the forks, are abandonware https://github.com/mwpowellhtx/NDesk.Options.Extensions https://github.com/gibbed/NDesk.Options https://github.com/nano-byte/NDesk.Options https://github.com/hach-que/NDesk.Options

I would much prefer something like this https://github.com/gsscoder/commandline

So what other choices apart from NDesk.options were considered?

@gep13
Copy link
Member

gep13 commented Aug 17, 2015

I know the guys who run Cake are thinking about using the Configuration options that are being cooked up as part of ASP.NET 5 for their tool:

https://www.nuget.org/packages/Microsoft.Framework.Configuration/1.0.0-beta6

https://github.com/aspnet/Configuration

https://gitter.im/cake-build/cake?at=55b919a622f1cbba636fb1a9

Was it considered?

@serra
Copy link
Contributor Author

serra commented Aug 17, 2015

@SimonCropp afaik NDesk.Options was adopted by the Mono project, see here, history and adoption post from the Mono lead.

I was triggered by this remark requesting for a code dependency instead of nuget/binary dependency. I have not thoroughly considered all command line argument parsers out there, but just went with one I was familiar with. I considered:

  • [NDesk/Mono].Options (dependency on single codefile, decent commandline interface)
  • CLAP (impressive, but not really simple IMO)
  • CommandLineParser (attribute API, would work nicely with current Arguments.cs but would result in strong coupling to the lib)
  • https://github.com/gsscoder/commandline (just a quick look, similar API as CommandLineParser above)
  • I did not consider the Cake stuff. Should I?

FWIW I don't intend to do a thorough comparison of all command line argument parsers out there. If anyone has a good reference of such a comparison or a strong preference just let me know and I'll be happy to implement it.

IMO if we use a lib with a proper command line interface (like Mono.Options has) and keep it decoupled well (which the current codebase allows us to do very well BTW, good job there), this can't really hurt us. In fact, this decoupling is wat kept me from picking the CommandLineParser-like API, because the use of attributes IMO give us stronger coupling than we need to the argument parsing library.

Looking forward to your reactions!

@dazinator
Copy link
Member

I think you could probably implement all of those parsers in a loosely
coupled way - it would just be a matter of defining an 'IParser' contract
with a method that accepts a string array (the args) and returns an
instance of an options class which is a Poco (no attributes etc). You could
have an NDesk implementation of that contract or a CommandLineParser
implementation of that contract etc etc.

I havent used ndesk but CommandLineParser is awesome.

On Mon, 17 Aug 2015 16:10 Marijn van der Zee notifications@github.com
wrote:

@SimonCropp https://github.com/SimonCropp afaik NDesk.Options was
adopted by the Mono project, see here
https://github.com/mono/mono/blob/master/mcs/class/Mono.Options/Mono.Options/Options.cs,
[history(
https://github.com/mono/mono/commits/master/mcs/class/Mono.Options/Mono.Options/Options.cs)
and adoption post from the Mono lead
http://tirania.org/blog/archive/2008/Oct-14.html.

I was triggered by this remark
#428 (comment)
requesting for a code dependency instead of nuget/binary dependency. I have
not thoroughly considered all command line argument parsers out there, but
just went with one I was familiar with. I considered:

FWIW I don't intend to do a thorough comparison of all command line
argument parsers out there
https://www.nuget.org/packages?q=command-line-parser. If anyone has a
good reference of such a comparison or a strong preference just let me know
and I'll be happy to implement it.

IMO if we use a lib with a proper command line interface (like
Mono.Options has) and keep it decoupled well (which the current
codebase allows us to do very well BTW, good job there), this can't really
hurt us. In fact, this decoupling is wat kept me from picking the
CommandLineParser-like API, because the use of attributes IMO give us
stronger coupling than we need to the argument parsing library.

Looking forward to you reactions!


Reply to this email directly or view it on GitHub
#572 (comment).

@JakeGinnivan
Copy link
Contributor

For me I want to see what the ideal command line args looks like. We should be aligned with git in terms of --longVersion, -l and rethinking the structure so it's easy to work with. Once we know the target command line we want, lets look at which library allows us to achieve that the easiest.

With a rethought cli then we can make the decision if we want to make a breaking change to switch, or if the gains are not worth it?

@serra
Copy link
Contributor Author

serra commented Aug 18, 2015

@JakeGinnivan I'll write up a proposal today.

@serra
Copy link
Contributor Author

serra commented Aug 19, 2015

@JakeGinnivan this proposal assumes the same functionality. It is not revolutionary, but more consistent across the cli and allows for extension and renaming.

Main breaking change is that we drop the positional path and init arguments. Breaking changes are marked with an asterix in the description. I can imagine implementing this without breaking changes, which would have the downside of some ugly code and an inconsistent cli wrt the updateassemblyinfo option.

I'd consider it an improvement if option names were better readable, but this can easily be achieved later by specifying aliases for the options and later phasing out bad names along with other breaking changes.

I lack the foresight on what's coming for gitversion. But given the current feature set I'd stick with the current options-only CLI over a verb+options cli (eg git commit -m "intial commit"). If you do foresee the need for this, we should reconsider this proposal.

Use convention to derive a SemVer product version from a GitFlow or GitHub based repository.

      --path, --targetpath=VALUE
                             The path to inspect
                               Defaults to the current directory.
  -i, --init                 Start the configuration utility for gitversion
  -h, -?, --help             Show this message and exit
  -l, --log=VALUE            Path to logfile
      --exec=VALUE           Executes target executable making GitVersion 
                               variables available as environmental variables
      --execargs=VALUE       Arguments for the executable specified by --exec
      --proj=VALUE           Build an msbuild file making GitVersion 
                               variables available as msbuild properties
      --projargs=VALUE       Additional arguments to pass to msbuild
  -u, --username=VALUE       Username in case authentication is required
  -p, --password=VALUE       Password in case authentication is required
  -o, --output=VALUE         Determines the output to the console
                               Can be either 'json' or 'buildserver', will 
                               default to 'json'.
      --url=VALUE            Url to remote git repository
  -b, --remotebranch=VALUE   Name of the branch to use on the remote 
                               repository, must be used in combination with --
                               url
      --updateassemblyinfo[=VALUE]
                             * Will recursively search for all 'AssemblyInf-
                               o.cs' files in the git repo and update them
                               To use another filename use --
                               updateassemblyinfo:[another-assemblyinfo-
                               filename.cs]
      --updateassemblyinfoname
                             * Deprecated: use --
                               updateassemblyinfo:[assemblyinfofilename.cs] 
                               instead.
      --dynamicrepolocation=VALUE
                             Override locations dynamic repositories are 
                               cloned to
                               Defaults to %tmp%.
  -c, --commit=VALUE         The commit id to inspect
                               Defaults to the latest available commit on the 
                               specified branch.
  -v, --showvariable=VALUE   Used in conjunction with /output json, will 
                               output just a particular variable
      --nofetch              
      --showconfig           Outputs the effective GitVersion config
                               Outputs the defaults and custom from GitVersion.yaml in yaml format.
      --assemblyversionformat
                             Deprecated: use AssemblyVersioningScheme 
                               configuration value instead.

@serra
Copy link
Contributor Author

serra commented Aug 19, 2015

some examples

gitversion
gitversion -i
gitversion /i
gitversion --init
gitversion /?
gitversion --help
gitversion --log=log.txt
gitversion -l log.txt
gitversion --log:log.txt
gitversion -v SemVer
gitversion -v:SemVer

@JakeGinnivan
Copy link
Contributor

What about having a more command focused cli, this would align better with git, nuget, npm etc

gitversion init
gitversion help
gitversion version
gitversion debug
gitversion run

If no command is given then run is the default. Then we can show much less when you say gitversion help and then to get more info about running you go gitversion help run?

Thoughts?

@JakeGinnivan
Copy link
Contributor

Then run becomes interesting.

I don't like the /output flag, it is confusing and mixed with /log its a mess. We need to think through how to make that better..

@serra
Copy link
Contributor Author

serra commented Aug 20, 2015

OK, so anything is open?

Agree wrt output and log.

I like the command style cli, but those get messed up IMO when not sticking to verbs (add, commit, log). And they shine for clis to application with much functionality, whereas gitversion is pretty focused.

But it's definitely worth a try. I'll give it some thought and let go of the current cli.

@JakeGinnivan
Copy link
Contributor

Small improvements -> Lets try and keep compatible
Large improvements -> Complete rethink/breaking changes are fine

Before trying to get small gains I want to see if we can rethink it to make it a bit more intuitive

@pascalberger
Copy link
Member

Fyi, there's also PR pending for a possible command line tool in the BCL (dotnet/corefxlab#280).

@JakeGinnivan
Copy link
Contributor

I saw that! Looks quite good actually. Need to get back onto this, will try later this week once I sort out Shouldly and TestStack.BDDfy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants