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

Cobra Command Fails to remove flag when runs in a Interactive mode #1419

Open
debankur1 opened this issue Jun 15, 2021 · 10 comments
Open

Cobra Command Fails to remove flag when runs in a Interactive mode #1419

debankur1 opened this issue Jun 15, 2021 · 10 comments
Labels
area/flags-args Changes to functionality around command line flags and args area/lib Methods and functions that exist in the cobra library and consumed by users

Comments

@debankur1
Copy link

debankur1 commented Jun 15, 2021

When cobra command is executed via API e.g. rootCmd.Execute() in an interactive mode, it keeps the flags that were persisted from the previous command here are the code snippet and test results.

package main

import (
"bufio"
"fmt"
"github.com/spf13/cobra"
"os"
"strings"
)

func main() {
for {
reader := bufio.NewReader(os.Stdin)
input, _, _ := reader.ReadLine()
args := strings.Split(string(input), " ")
rootCmd.SetArgs(args)
rootCmd.Execute()
fmt.Println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&")

}

}

var rootCmd = &cobra.Command{
Use: "cli",
Short: "test app to test iShell like usage in cobra",

}
var testCmd = &cobra.Command{
Use: "tcmd",
Short: "test cmd for cobra command",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello World")
},
}
func init() {
rootCmd.AddCommand(testCmd)
}

tcmd
Hello World
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
tcmd -h
test cmd for cobra command

Usage:
cli tcmd [flags]

Flags:
-h, --help help for tcmd
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
tcmd
test cmd for cobra command

Usage:
cli tcmd [flags]

Flags:
-h, --help help for tcmd
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

As it can be clearly seen that after running the command with a flag -h or --help next time it uses the same flag , instead running the entire command.

Same thing I have testes with iShell

func main(){
shell := ishell.New()
shell.Println("Sample Interactive Shell")
shell.AddCmd(&ishell.Cmd{
Name: "test",
Help: "Test Command Help",
Func: func(c *ishell.Context) {
rootCmd.SetArgs(c.RawArgs)
rootCmd.Execute()
},
})
shell.Run()
}
Have also tried the cmd.RestFlags() it didn't work.
Any help is really appreciated.

@debankur1 debankur1 changed the title Cobra Command Fails to remove flag when run in a Interactive mode Cobra Command Fails to remove flag when runs in a Interactive mode Jun 15, 2021
@marckhouzam
Copy link
Collaborator

You have to use the flag.Changed field to reset the flag.
Here is an example:

nonPersistentFlag.Changed = false

I hope this helps.

@debankur1
Copy link
Author

debankur1 commented Jun 16, 2021

You have to use the flag.Changed field to reset the flag.
Here is an example:

nonPersistentFlag.Changed = false

I hope this helps.

thanks for the quick response, I believe when we execute rootCmd.Execute() then cobra should do it automatically because if I need to set nonPersistentFlag.Changed = false, then I have to do for all the commands and their subcommands

@debankur1
Copy link
Author

@marckhouzam I tried

var testCmd = &cobra.Command{
Use: "tcmd",
Short: "test cmd for cobra command",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello World")
},
PostRun: func(cmd *cobra.Command, args []string) {
helpFlag := cmd.Flags().Lookup("help")
helpFlag.Changed = false
},
}

@marckhouzam
Copy link
Collaborator

I think we need to take a step back and look at why you would want to do this @debankur1.

The uses of Cobra that I've seen do a single rootCmd.Execute and then exit, so at each time all the flags start fresh.

What is your scenario that would require you to reset the flags manually?

@debankur1
Copy link
Author

debankur1 commented Jun 16, 2021

@marckhouzam The scenario is simple, let us consider user want to use Interactive Shell (https://github.com/abiosoft/ishell)

func main(){
shell := ishell.New()
shell.Println("Sample Interactive Shell")
shell.AddCmd(&ishell.Cmd{
Name: "test",
Help: "Test Command Help",
Func: func(c *ishell.Context) {
rootCmd.SetArgs(c.RawArgs)
rootCmd.Execute()
},
})
shell.Run()
}

and user wants to run the cobra command from the interactive shell i.e. rootCmd.Execute() , but the problem is rootCmd.Execute() holds a reference to the memory once it is executed that is not correct. This is a pretty valid case where users may like to use the power of Cobra in an interactive way. Maybe post rootCmd.Execute() it should initialize once again so that all the flags are reset or it should behave as if a user is running a new command.

@debankur1
Copy link
Author

debankur1 commented Jun 17, 2021

@marckhouzam I have found a workaround to reset the flag (in my case it is help)
cmd.Flags().Lookup("help").Value.Set("false"), but the bug remains still valid because flag reset must happen automatically in

func (c *Command) ExecuteC() (cmd *Command, err error){
}

@github-actions
Copy link

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

@brunomiranda-hotmart
Copy link

@marckhouzam I stumbled on the same problem, in my case is in testing. I created a small example to show the problem.

main.go:

package main

import (
	"github.com/spf13/cobra"
)

var (
	cmd            *cobra.Command
	shouldContinue bool
)

func init() {
	cmd = &cobra.Command{
		Use: "debug",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Print("Step 1")
			if shouldContinue {
				cmd.Print("Step 2")
				return
			}
		},
	}
	cmd.Flags().BoolVar(&shouldContinue, "continue", false, "test")
}

func main() {
	cmd.Execute()
}

main_test.go:

package main

import (
	"strings"
	"testing"

	"github.com/g14a/metana/pkg"
	"github.com/stretchr/testify/assert"
)

func TestFull(t *testing.T) {
	test := []struct {
		args   []string
		output string
	}{
		{
			args:   []string{"--continue"},
			output: strings.Join([]string{"Step 1", "Step 2"}, ""),
		},
		{
			args:   []string{},
			output: strings.Join([]string{"Step 1"}, ""),
		},
	}

	for i, tt := range test {
		_, out, _ := pkg.ExecuteCommandC(cmd, tt.args...)
		t.Log(i, out, tt.args)

		assert.NotEmpty(t, out)
		assert.Equal(t, tt.output, out)
	}
}

PS.: I'm aware that changing the order in this test case would fix the problem, however in complex commands that might no be possible.

@johnSchnake johnSchnake added area/flags-args Changes to functionality around command line flags and args area/lib Methods and functions that exist in the cobra library and consumed by users labels Mar 16, 2022
Anaminus added a commit to Anaminus/rbxmk that referenced this issue Apr 26, 2022
If the executable is run from Windows Explorer, then the program will
enter a mode that allows commands to be used interactively.

NOTE: Cobra commands are currently not designed to be run more than
once. Because of this, the behavior is locked behind
the "interactive_commands" build tag. Progress on a solution can be
tracked here: spf13/cobra#1419
@HaRo87
Copy link

HaRo87 commented Mar 5, 2023

I'am facing the same issue in my tests. 😢

@brombach
Copy link

brombach commented Jun 22, 2023

I've run into the same thing in my tests. I believe the issue is caused by the way Cobra uses global variables. I've tried rootCmd.ResetCommands() and rootCmd.ResetFlags(), but the issue is that flags are persisted in each subcommand, so each subcommand must be reset between. I found a similar issue in Github's CLI package:

cli/cli#759

which has the workaround:

https://github.com/cli/cli/blob/c0c28622bd62b273b32838dfdfa7d5ffc739eeeb/command/pr_test.go#L55-L67

The alternative is to restructure each command into a factory and don't use the magic init() and global variables, as the associated PR does.

https://github.com/cli/cli/pull/1500/files#diff-f043ea6e74e4ab1e778461b05a9f79245ba31ca1ce58def813d0e919ed2f35b4R40-R82

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/flags-args Changes to functionality around command line flags and args area/lib Methods and functions that exist in the cobra library and consumed by users
Projects
None yet
Development

No branches or pull requests

6 participants