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

fix(args): Correct legacyArgs logical errors #1157

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

donggangcj
Copy link

Close #1156
Signed-off-by: Dong Gang dong.gang@daocloud.io

Close spf13#1156
Signed-off-by: Dong Gang <dong.gang@daocloud.io>
@CLAassistant
Copy link

CLAassistant commented Jul 11, 2020

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Dong Gang seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@donggangcj
Copy link
Author

donggangcj commented Jul 11, 2020

@marckhouzam I know you've done a lot of work on helm and could you please review this pr if you are free.😀

@umarcor
Copy link
Contributor

umarcor commented Jul 11, 2020

Ref #842.

@marckhouzam
Copy link
Collaborator

Thanks @donggangcj. When I tried this fix with helm, it didn't address the problem described in #1156.

Could you provide a test program that shows the problem before the PR, and that the problem is resolved after the PR?

As for helm, this PR touches the legacyArgs function, however, notice that legacyArgs is only called when command.Args == nil:

cobra/command.go

Lines 627 to 630 in 675ae5f

if commandFound.Args == nil {
return commandFound, a, legacyArgs(commandFound, stripFlags(a, commandFound))
}
return commandFound, a, nil

However, helm always uses command.Args, except for its root command, so I'm under the impression a different approach will be required to fix this problem.

Signed-off-by: Dong Gang <dong.gang@daocloud.io>
@donggangcj
Copy link
Author

donggangcj commented Jul 13, 2020

@marckhouzam My bad! I missed important part in previous commit and I‘ve commit again. I ran a simple test locally and the result is here:
Before the PR:

# NOTE: It prints the help message
➜  cobra git:(fix-exit-code-error) helm repo sadd foo https://foo/bar 

This command consists of multiple subcommands to interact with chart repositories.

It can be used to add, remove, list, and index chart repositories.

Usage:
  helm repo [command]

Available Commands:
....

➜  cobra git:(fix-exit-code-error) echo $?
0

After the PR:

# NOTE: It prints the Command.Args error string instead of help message
➜  bin git:(temp/validate) ✗ ./helm repo sadd foo https://foo/bar
Error: "helm repo" accepts no arguments

Usage:  helm repo add|remove|list|index|update [ARGS] [flags]

➜  bin git:(temp/validate) ✗ echo $?
1

The function legacyArgs is only called when Comand.Args == nil ,so the previous commit doesn't work if Command.Args != nil. helm approach is making commands that contains subcommands (like get,repo...)Command.Args= require.NoArgs,so it skips validation legacyArgs .

I continue to view the code logic and find the root cause of problem.

cobra/command.go

Lines 804 to 817 in 675ae5f

if !c.Runnable() {
return flag.ErrHelp
}
c.preRun()
argWoFlags := c.Flags().Args()
if c.DisableFlagParsing {
argWoFlags = a
}
if err := c.ValidateArgs(argWoFlags); err != nil {
return err
}

If switch the order of part1 and part2 , everythings will be ok!

        // part1
	if !c.Runnable() {
		return flag.ErrHelp
	}

	c.preRun()

       // part2
        argWoFlags := c.Flags().Args()
	if c.DisableFlagParsing {
		argWoFlags = a
	}

	if err := c.ValidateArgs(argWoFlags); err != nil {
		return err
	}

I read the source code of cli which is using cobra but it doesn't appear this problem.The related code is here. Although cli and kubectl (by add Run function ) avoids this problem successfully, but I think the root cause is from cobra.

I think a command shouldn't accept any args if it has subcommand,or it's easy to confuse args and subcommand string😀. Finally,Please forgive me for my bad English!

@donggangcj
Copy link
Author

Ref #842.

Thanks!

@marckhouzam
Copy link
Collaborator

I read the source code of cli which is using cobra but it doesn't appear this problem.

Actually, it looks like they hack their way around this problem:
https://github.com/cli/cli/blob/1ddb4d76a750748012c91204740d49991ad2f968/cmd/gh/main.go#L59
and
https://github.com/cli/cli/blob/1ddb4d76a750748012c91204740d49991ad2f968/command/help.go#L69-L73

Copy link
Collaborator

@marckhouzam marckhouzam left a comment

Choose a reason for hiding this comment

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

This works, but I have concerns about backwards compatibility.

If those concerns are valid, here is another idea.
There was another suggestion on how to fix this here:
#1056 (comment)

But in the end if was not done:
#1062 (comment)
and
#1062 (comment)

However, you may be able to revive that approach, as long not specifying a sub-command continued to not return an error. So something like
helm repo badcommand
would return an error
but
helm repo
would not.

This is just a suggestion, but I am not sure it would get accepted either.

// root command with subcommands, do subcommand checking.
if !cmd.HasParent() && len(args) > 0 {
// root command with subcommands, the args should be empty.
if cmd.HasSubCommands() && len(args) > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We cannot make this change as it will break backwards-compatibility.
Cobra does support commands that take args and sub-commands.
For example helm v2 has the helm get command:

Usage:
  helm get [flags] RELEASE_NAME
  helm get [command]

As you can see you can either pass it a sub-command, or you can pass it an argument.
As you mention, this can cause confusion so helm v3 no longer does that.
However it shows that some programs do use this feature and therefore we cannot change it.

@@ -816,6 +810,12 @@ func (c *Command) execute(a []string) (err error) {
return err
}

if !c.Runnable() {
return flag.ErrHelp
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Moving this after the parsing of the arguments seems right to me. The change will only impact commands that are not runnable which have the Args field defined. The change means that the Args field will be respected when it used to not be. I believe this can be justified as a change.

return flag.ErrHelp
}

c.preRun()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is more worrisome. I don't understand enough about preRun() and initializers to know if a program would expect the preRun() to be executed even if the call to Args fails with an error, which used to happen before but will not with this change.

This could be a backwards-compatibility issue.
@jharshman do you think this could break backwards-compatibility?

@github-actions
Copy link

This PR is being marked as stale due to a long period of inactivity

@github-actions
Copy link

This PR is being marked as stale due to a long period of inactivity

@jpmcb jpmcb added triage/needs-info Needs more investigation from maintainers or more info from the issue provider and removed needs investigation labels Feb 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triage/needs-info Needs more investigation from maintainers or more info from the issue provider
Projects
None yet
Development

Successfully merging this pull request may close these issues.

return non-zero exit code upon non-runnable subcommand
6 participants