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

Support validation of positional arguments #284

Merged
merged 2 commits into from
Jul 23, 2017
Merged

Conversation

dnephin
Copy link
Contributor

@dnephin dnephin commented May 27, 2016

Fixes #42
Branched from #120

Adds a new field Args that allows a user to define the expected positional arguments that a command should receive. If a command doesn't receive the expected args it returns an error.

By accepting a validation function users can create their own custom validation for positional args. This PR includes validators for the most common cases.

}

// NoArgs returns an error if any args are included
func NoArgs(cmd *Command, args []string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can a noarg command have subcommands? This seems like the trigger I wanted in #285, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, subcommand lookup happens before this is called in Find, so these arg handling functions only take care of non-subcommand args. I believe this NoArgs option is what you want in #285.

@dnephin
Copy link
Contributor Author

dnephin commented May 27, 2016

There's a problem with this PR. Arg validation happens before the --help flag is checked, so they can report errors incorrectly when --help is used.

I think the order of these operations needs to be adjusted.

@dnephin
Copy link
Contributor Author

dnephin commented May 31, 2016

Ok, that issue should be fixed. Now legacy arg validation happens at the same time (before any flags are parsed), but any new arg validation will happen after flag parsing, so that flags like --help will behave correctly.

I updated the tests to catch this case.

cobra_test.go Outdated
@@ -513,6 +523,11 @@ func TestSubCmdTakesArgs(t *testing.T) {
func TestCmdOnlyValidArgs(t *testing.T) {
result := noRRSetupTest("echo times one two five")

if result.Error == nil {
t.Errorf("Expected an error")
t.FailNow()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The testing package provides:

t.Fatal("Expected an error")

as a convenience method for wrapping the two calls you're making here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I've made that change. It did seem strange that I had to call two functions, but somehow I missed this.

@dnephin
Copy link
Contributor Author

dnephin commented Jun 20, 2016

This PR is in use by https://github.com/docker/docker to validation positional args.

By using a function instead of an enum for the Args field, it allows us to do things like validate the format of args before they are passed to any Run function.

@rileytg
Copy link

rileytg commented Aug 7, 2016

Am I understanding correctly?

In addition to the documented features, you can now validate arguments in a custom PositionalArgs type func passed to Args:.

contrived example (imagine if instead of :

func validColorArgs(cmd *cobra.Command, args []string) error {
  if err := cli.RequiresMinArgs(1)(cmd, args); err != nil {
    return err
  }
  if myapp.IsValidColor(args[0]) {
     return nil
  }
  return fmt.Errorf("Invalid color specified: %s", args[0])
}

@dnephin
Copy link
Contributor Author

dnephin commented Aug 7, 2016

@rileytg yup, that's correct. You can write custom validation functions for the positional args.

@rileytg
Copy link

rileytg commented Aug 10, 2016

what do you think about changing the language to express something along the lines of: (im thinking maybe keep it like urs and add a custom to the end of the list with my example.)

Positional and Custom Arguments

Validation of positional arguments can be specified using the Args field. A custom validator can be provided like this:

Args: func validColorArgs(cmd *cobra.Command, args []string) error {
  if err := cli.RequiresMinArgs(1)(cmd, args); err != nil {
    return err
  }
  if myapp.IsValidColor(args[0]) {
     return nil
  }
  return fmt.Errorf("Invalid color specified: %s", args[0])
}

The follow validators are built in:

  • NoArgs - the command will report an error if there are any positional args.
  • ArbitraryArgs - the command will accept any args.
  • OnlyValidArgs - the command will report an error if there are any positiona
    args that are not in the ValidArgs list.
  • MinimumNArgs(int) - the command will report an error if there are not at
    least N positional args.
  • MaximumNArgs(int) - the command will report an error if there are more than
    N positional args.
  • ExactArgs(int) - the command will report an error if there are not
    exactly N positional args.
  • RangeArgs(min, max) - the command will report an error if the number of args
    is not between the minimum and maximum number of expected args.

@dnephin
Copy link
Contributor Author

dnephin commented Aug 10, 2016

That sounds good. I'll update the PR with that change

@Julio-Guerra
Copy link

What about the help message of such commands? It would be great to automatically generate command [flags] arg1 arg2 arg3, etc. according to Args or whatever. Mostly because of [flags] which is already automatically concatenated to the Use field. So we can't simply put arg1 arg2 arg3 in the Use field, otherwise we end up with command arg1 arg2 arg3 [flags] instead of command [flags] arg1 arg2 arg3.

@dnephin
Copy link
Contributor Author

dnephin commented Sep 16, 2016

I find the [flags] placement to be unreliable in general, and I disable it by setting a custom template. The problem you describe already exists today with a use line. You can fix it by setting the use line to command [flags] arg1 arg2 arg3 explicitly.

@dnephin
Copy link
Contributor Author

dnephin commented Sep 16, 2016

Rebased and updated the README text as suggested

@jwhitcraft
Copy link

I would really love this feature to be merged, so I don't have to use the fork and be able to get all other fixes.

@dnephin @eparis what is the status of this?

@dnephin
Copy link
Contributor Author

dnephin commented Jan 25, 2017

Still waiting for feedback from the project maintainers.

This code is being used in docker/docker (as of 1.13).

I pulled the latest from spf13/master into my fork, so you could use that for now if you want.

@jwhitcraft
Copy link

@dnephin, i just pulled it down and it seems your master branch is missing the commits from this branch. Could you rebase this branch with master so i can just use this branch from your fork?

Thanks!

@dnephin
Copy link
Contributor Author

dnephin commented Jan 26, 2017

The master branch has these changes, it just doesn't have the same commits. I think they were rebased from a different branch, so the commit ids are different.

@@ -109,6 +113,16 @@ var cmdRootSameName = &Command{
Long: "The root description for help",
}

var cmdRootTakesArgs = &Command{
Use: "root-with-args [random args]",
Short: "The root can run it's own function and takes args!",
Copy link

@kofalt kofalt Feb 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/it's/its

@harsimranmaan
Copy link

@spf13 @dnephin Is this still waiting on something?

@vadim-su
Copy link

vadim-su commented Apr 9, 2017

@dnephin what is the status of the PRs now?

@dnephin
Copy link
Contributor Author

dnephin commented Apr 10, 2017

Status hasn't changed. Waiting on someone to accept or reject, or provide more feedback about how to get it accepted.

@n10v
Copy link
Collaborator

n10v commented Apr 22, 2017

@dnephin what do you think of it:

But maybe Args field will be called as CheckArgs or somehow else so it will be clearer to understand the purpose of function.
And I think built-in validators should be above custom.
Also IMHO it's more beautiful to conclude name of built in functions in backticks.

@dnephin dnephin force-pushed the define_args branch 2 times, most recently from 85279b9 to fb31500 Compare April 22, 2017 21:23
@dnephin
Copy link
Contributor Author

dnephin commented Apr 22, 2017

I've made those changes to the README.

I'm not really a fan of CheckArgs. Check them for what? ValidateArgs maybe, but there is already a ValidArgs which is a bit too similar.

@n10v
Copy link
Collaborator

n10v commented Apr 22, 2017

Ok, 'Args' is good too
Actually I like this PR. It adds really needed this feature and fix bug, to that there are many complains (#42).
We should only wait for @spf13 agreement

@harsimranmaan
Copy link

Thanks @BoGeM and @dnephin for driving this forward. Hopefully, @spf13 would agree for this merge now. Much appreciated.

@n10v n10v mentioned this pull request May 9, 2017
@n10v
Copy link
Collaborator

n10v commented May 12, 2017

Please solve merge conflicts.
We should merge it when @spf13 will be free.

@n10v
Copy link
Collaborator

n10v commented May 16, 2017

@dnephin I solved the merge conflicts by myself. Please copy all code from my branch (git pull https://github.com/bogem/cobra.git dnephin-define_args) and paste to yours. After you push conflict solutions, I will write directly to @spf13 so he will say his (final) decision about this PR.
I will not commit to master (except of bug fixes) till we merge it.

eparis and others added 2 commits June 5, 2017 14:22
…ry arguments

Check that arguments are in ValidArgs

If a command defined cmd.ValidArgs check that the argument is actually
in ValidArgs and fail if it is not.
Fix some typos in README and comments.
Move arg validation to after flag validation so that the help flag is run first.
Pass the same args to ValidateArgs as the Run methods receive.
Update README.

Signed-off-by: Daniel Nephin <[email protected]>
@dnephin
Copy link
Contributor Author

dnephin commented Jun 5, 2017

rebased

@harsimranmaan
Copy link

harsimranmaan commented Jun 7, 2017

@BoGeM @spf13 The ball is in your court now. Thanks @dnephin .

@n10v n10v merged commit f20b4e9 into spf13:master Jul 23, 2017
@kofalt
Copy link

kofalt commented Jul 24, 2017

Thanks @BoGeM and @dnephin! 🎉

@notjames
Copy link

notjames commented Jul 26, 2017

I know that this is coming from left field a little, however, I'm very new to Go and I'm trying to port an internal application and am currently very stuck primarily because the documentation simply does not make sense with regard to custom argument validation. I've been through this entire issue and the README.md, which briefly mentions it, but gives NO context about where it should go, how it should properly be used, etc, and everything I try ends up being an error. Would someone for the love of all that is good PLEASE explain how to use Args: and friends IE custom argument validation? @rileytg ?

For instance, the docs say we can use this example (and I'm only supposing this belongs in the &cobra.command() call:

func validColorArgs(cmd *cobra.Command, args []string) error {
  if err := cli.RequiresMinArgs(1)(cmd, args); err != nil {
    return err
  }
  if myapp.IsValidColor(args[0]) {
     return nil
  }
  return fmt.Errorf("Invalid color specified: %s", args[0])
}

So, I used that. I get an error. syntax error: unexpected validArgs, expecting ( and apparently use of Args takes Args PositionalArgs so I will need to look that up. However, none of this is documented very well...I think.

Tangentially, the documentation for this project tries to be good, but it is seriously lacking some important details methinks. It almost seems like nobody is keeping it updated or at the very least is keeping it updated only minimally.

For instance, I thought someone hijacked the documentation when mentioning even the ability for custom arg validation because when I tried to use it, my IDE screamed that it wasn't even available in the struct. Turns out, after reading this issue and seeing the change was just merged a couple of days ago that the problem was simply an outdated library on my system. Kudos for adding something about it at all in the documentation, but the details about how to properly use it are completely wrong or just missing.

How are developers supposed to rely on this library if we can't track the changes or use the library reliably? Sorry for the rant. It's almost 4 AM so...it's just that I've lost a lot of time trying to figure this out and it seems like I shouldn't have had to.

@n10v
Copy link
Collaborator

n10v commented Jul 26, 2017

@notjames Sorry for inconvenience and this feature is documented really bad. I will make a PR now to fix this issue and we can discuss it there.
Thank you for your notice!

nvanbenschoten added a commit to nvanbenschoten/cockroach that referenced this pull request Apr 2, 2018
Positional argument validation was added to spf13/cobra in
spf13/cobra#284, but our cli still
relies on custom validation in each of the "run" functions.
This validation is unstandardized and overly permissive in
certain cases. This change fixes this by replacing our custom
logic with validation expressed using Cobra's `PositionalArgs`
validators.

Release note: None
nvanbenschoten added a commit to nvanbenschoten/cockroach that referenced this pull request Apr 2, 2018
Positional argument validation was added to spf13/cobra in
spf13/cobra#284, but our cli still
relies on custom validation in each of the "run" functions.
This validation is unstandardized and overly permissive in
certain cases. This change fixes this by replacing our custom
logic with validation expressed using Cobra's `PositionalArgs`
validators.

Release note: None
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.