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

Default command #823

Open
rmasci opened this issue Feb 11, 2019 · 16 comments
Open

Default command #823

rmasci opened this issue Feb 11, 2019 · 16 comments
Assignees
Labels
area/cobra-command Core `cobra.Command` implementations kind/bug A bug in cobra; unintended behavior lifecycle/needs-pr Ready for a PR from the community

Comments

@rmasci
Copy link

rmasci commented Feb 11, 2019

Why is it so difficult to have a default command? So if I am making a program that does 4 things:

MyCommand Item1 filename
MyCommand Item2 -a 1 -b 2 filename
MyCommand Item3 -c 1 -d 2 -e 3 filename
MyCommand Item4 -a1 -e 3 filename

So if a user just types, 'MyCommand filename', it runs as if they typed 'MyCommand item1 filename' by default? If command item1 errors out then throw an error, but try item1 if no command specified.

@GustavoKatel
Copy link

I solved adding a find operation and changing the default args before executing the root command.
Something like this:

cmd, _, err := rootCmd.Find(os.Args[1:])
if err != nil || cmd == nil {
    // Not found
    args := append([]string{"Item1"}, os.Args[1:]...)
    rootCmd.SetArgs(args)
}

@eine
Copy link

eine commented Mar 18, 2019

@rmasci , @GustavoKatel , you might find #725 useful.

@rmasci
Copy link
Author

rmasci commented Mar 19, 2019 via email

@jharshman
Copy link
Collaborator

@rmasci it seems like your question has been answered by #725 . If so, please close out this issue.

@eine
Copy link

eine commented Jul 15, 2019

@jharshman, since there is no intuitive one-liner solution to this question, I think it might be worth keeping this issue open. Although the solution in #725 works, it is not clean and it is not properly integrated in the codebase. Therefore, I think this can be kept open until some equivalent to checkRootAlias is added to the codebase.

@innovia
Copy link

innovia commented Mar 27, 2020

I solved adding a find operation and changing the default args before executing the root command.
Something like this:

cmd, _, err := rootCmd.Find(os.Args[1:])
if err != nil || cmd == nil {
    // Not found
    args := append([]string{"Item1"}, os.Args[1:]...)
    rootCmd.SetArgs(args)
}

just a quick bug fix (should be checking if cmd.args == nil b/c cmd is struct flags, help and more...) as well as context for where to place this

func Execute() {
	cmd, _, err := rootCmd.Find(os.Args[1:])

  // default cmd if no cmd is given
	if err != nil || cmd.Args == nil {
		args := append([]string{"<your-default-command>"}, os.Args[1:]...)
		rootCmd.SetArgs(args)
	}

	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

@innovia
Copy link

innovia commented Mar 27, 2020

I still get an error - now its always the default

@connorlbark
Copy link

connorlbark commented Apr 22, 2020

Ideally, this would be more tightly integrated into the API so that help text will still give you information about e.g. the other commands that are possible instead of dumping the help text for the subcommand (the default behaviour when using one of these fixes without supplying a subcommand) and make it more clear that it's running a de-facto subcommand. Perhaps

example-app description

Usage:
  example-app (command)

Available Commands:
  command1 (default)         desc
  command2                   desc
  command2                   desc

Flags:
      --debug   print debug while running
  -h, --help    help for example-app

Use "example-app [command] --help" for more information about a command.

Not sure this is optimal, but some way to make it less confusing would be nice.

Edit:
You can get close to the desired behaviour with a modification of the example code from the issue linked above ( #725 ), with some shortfalls

func subCommands() (commandNames []string) {
	for _, command := range cmd.Commands() {
		commandNames = append(commandNames, append(command.Aliases, command.Name())...)
	}
	return
}

func setDefaultCommandIfNonePresent() {
	if len(os.Args) > 1 { 
		potentialCommand := os.Args[1]
		for _, command := range subCommands() {
			if command == potentialCommand {
				return
			}
		}
		os.Args = append([]string{os.Args[0], "<default subcommand>"}, os.Args[1:]...)
	}

}

func main() {
	setDefaultCommandIfNonePresent()
	if err := cmd.Execute(); err != nil {
		zap.S().Error(err)
		os.Exit(1)
	}
}

The difference here is that it checks if len(os.Args) > 1 before changing the default subcommand. This means that, if ran without any arguments, it will print the default help command (with all of the subcommands). Otherwise, if supplied any arguments, it will use the subcommand. So, it will display the main 'help' without arguments, and the subcommand's help if supplied '-h'/'--help'. Close enough for me.

@github-actions
Copy link

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

@Ehco1996
Copy link

try this

var rootCmd = &cobra.Command{
	Use: "root",
}

func Execute() {

	cmd, _, err := rootCmd.Find(os.Args[1:])
	// default cmd if no cmd is given
	if err == nil && cmd.Use == rootCmd.Use {
		args := append([]string{defaultCmd.Use}, os.Args[1:]...)
		rootCmd.SetArgs(args)
	}

	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

@IKukhta
Copy link

IKukhta commented Jun 28, 2021

better to keep default help behaviour with this code:

	cmd, _, err := rootCmd.Find(os.Args[1:])
	// default cmd if no cmd is given
	if err == nil && cmd.Use == rootCmd.Use && cmd.Flags().Parse(os.Args[1:]) != pflag.ErrHelp {
		args := append([]string{defaultCmd.Use}, os.Args[1:]...)
		rootCmd.SetArgs(args)
	}

	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

@rmasci
Copy link
Author

rmasci commented Oct 22, 2021

Latest solve for this is the following:

main.go

func main() {
  // Define the default sub command 'defCmd' here. If user doesn't submit
  // using a default command, we'll use what is here.
  defCmd:="mydefaultcmd"
  cmd.Execute(defCmd)
}

root.go

func Execute(defCmd string) {
  var cmdFound bool
  cmd :=rootCmd.Commands()

  for _,a:=range cmd{
    for _,b:=range os.Args[1:] {
      if a.Name()==b {
       cmdFound=true
        break
      }
    }
  }
  if cmdFound == false {
    args:=append([]string{defCmd}, os.Args[1:]...)
    rootCmd.SetArgs(args)
  }
  if err := rootCmd.Execute(); err != nil {
    fmt.Println(err)
    os.Exit(1)
  }
}

@johnSchnake
Copy link
Collaborator

I'm trying to clean up the backlog and wanted to check on a proposed solution. Walking through the code it just seems like this is a bug unique to the root command due to how it tries to Execute only from that node. It tries to find a subcommand and if it doesn't find one it errors.

It seems like the solution would be just to treat that as a bug. Would it be an acceptable solution to simply fix the code to allow setting the same Run in both cases?


var (
	rootCmd = &cobra.Command{
		Use:   "main",
		Short: "Main app",
		RunE: TestFunc,
	}

	subCmd = &cobra.Command{
		Use:   "sub",
		Short: "sub command",
		RunE: TestFunc,
	}
)

func TestFunc(c *cobra.Command, args []string) error {
	fmt.Println("hello world")
	return nil
}

func Execute(){
	rootCmd.AddCommand(subCmd)
	rootCmd.Execute()
}

In this case the root command behaves the same way as the child command.

@johnSchnake johnSchnake self-assigned this Feb 25, 2022
@johnSchnake johnSchnake added area/cobra-command Core `cobra.Command` implementations kind/bug A bug in cobra; unintended behavior and removed kind/stale labels Feb 25, 2022
@johnSchnake
Copy link
Collaborator

I think the change is really narrowly scoped if we approach it this way. This section of code finds the command being invoked, returns a 'not found' error causing the execution to stop. Since the rootCmd has a RunE though it should have been identified via Find

@johnSchnake johnSchnake added the lifecycle/needs-pr Ready for a PR from the community label Feb 25, 2022
@rkgarcia
Copy link

Hi,

I fixed this using

var rootCmd = &cobra.Command{
	Use:   "myapp",
	Short: "MyApp",
	// Uncomment the following line if your bare application
	// has an action associated with it:
	Run: func(cmd *cobra.Command, args []string) {
		childCmd.Run(cmd, args) // <-- Here I'm invoking the default command
	},
}

This is the better approach or what can I do? this works, but I don't know side effects (if exists)

@thediveo
Copy link

Hi,

I fixed this using

var rootCmd = &cobra.Command{
	Use:   "myapp",
	Short: "MyApp",
	// Uncomment the following line if your bare application
	// has an action associated with it:
	Run: func(cmd *cobra.Command, args []string) {
		childCmd.Run(cmd, args) // <-- Here I'm invoking the default command
	},
}

This is the better approach or what can I do? this works, but I don't know side effects (if exists)

If I understand cobra correctly, then this won't run any of the pre and post hooks of your subcommand. If you don't use them, then your example should be fine.

ondrejbudai added a commit to ondrejbudai/osbuild-deploy-container that referenced this issue Aug 14, 2024
Prior this commit, the bootc-image-builder container image had
a custom entrypoint that hardcoded the use of the build subcommand. This
meant that if a user wanted to use a different subcommand, they had to
overwrite the entrypoint.

This commit changes the cobra code in bib to fallback to build if no
subcommand was given. This is slighly ugly, but it allows us to remove
the custom entrypoint, streamlining the use of subcommands. Let's see
an example of calling the version subcommand:

Before:
podman run --rm -it --entrypoint=/usr/bin/bootc-image-builder \
    quay.io/centos-bootc/bootc-image-builder:latest version

After:
sudo podman run --rm -it \
    quay.io/centos-bootc/bootc-image-builder:latest version

Kudos to https://github.com/IKukhta for his code from
spf13/cobra#823 (comment)
mvo5 pushed a commit to ondrejbudai/osbuild-deploy-container that referenced this issue Aug 16, 2024
Prior this commit, the bootc-image-builder container image had
a custom entrypoint that hardcoded the use of the build subcommand. This
meant that if a user wanted to use a different subcommand, they had to
overwrite the entrypoint.

This commit changes the cobra code in bib to fallback to build if no
subcommand was given. This is slighly ugly, but it allows us to remove
the custom entrypoint, streamlining the use of subcommands. Let's see
an example of calling the version subcommand:

Before:
podman run --rm -it --entrypoint=/usr/bin/bootc-image-builder \
    quay.io/centos-bootc/bootc-image-builder:latest version

After:
sudo podman run --rm -it \
    quay.io/centos-bootc/bootc-image-builder:latest version

Kudos to https://github.com/IKukhta for his code from
spf13/cobra#823 (comment)
mvo5 added a commit to mvo5/cobra that referenced this issue Aug 16, 2024
This commit makes unknown commands a proper error type so that
the caller can check for this specific error type without having
to do string comparisons.

This is useful in the context of spf13#823
mvo5 added a commit to mvo5/cobra that referenced this issue Aug 19, 2024
This commit makes unknown commands a proper error type so that
the caller can check for this specific error type without having
to do string comparisons.

This is useful in the context of spf13#823

Signed-off-by: Michael Vogt <mvogt@redhat.com>
Signed-off-by: Michael Vogt <michael.vogt@gmail.com>
mvo5 pushed a commit to ondrejbudai/osbuild-deploy-container that referenced this issue Aug 19, 2024
Prior this commit, the bootc-image-builder container image had
a custom entrypoint that hardcoded the use of the build subcommand. This
meant that if a user wanted to use a different subcommand, they had to
overwrite the entrypoint.

This commit changes the cobra code in bib to fallback to build if no
subcommand was given. This is slighly ugly, but it allows us to remove
the custom entrypoint, streamlining the use of subcommands. Let's see
an example of calling the version subcommand:

Before:
podman run --rm -it --entrypoint=/usr/bin/bootc-image-builder \
    quay.io/centos-bootc/bootc-image-builder:latest version

After:
sudo podman run --rm -it \
    quay.io/centos-bootc/bootc-image-builder:latest version

Kudos to https://github.com/IKukhta for his code from
spf13/cobra#823 (comment)
mvo5 pushed a commit to ondrejbudai/osbuild-deploy-container that referenced this issue Aug 19, 2024
Prior this commit, the bootc-image-builder container image had
a custom entrypoint that hardcoded the use of the build subcommand. This
meant that if a user wanted to use a different subcommand, they had to
overwrite the entrypoint.

This commit changes the cobra code in bib to fallback to build if no
subcommand was given. This is slighly ugly, but it allows us to remove
the custom entrypoint, streamlining the use of subcommands. Let's see
an example of calling the version subcommand:

Before:
podman run --rm -it --entrypoint=/usr/bin/bootc-image-builder \
    quay.io/centos-bootc/bootc-image-builder:latest version

After:
sudo podman run --rm -it \
    quay.io/centos-bootc/bootc-image-builder:latest version

Kudos to https://github.com/IKukhta for his code from
spf13/cobra#823 (comment)
github-merge-queue bot pushed a commit to osbuild/bootc-image-builder that referenced this issue Aug 19, 2024
Prior this commit, the bootc-image-builder container image had
a custom entrypoint that hardcoded the use of the build subcommand. This
meant that if a user wanted to use a different subcommand, they had to
overwrite the entrypoint.

This commit changes the cobra code in bib to fallback to build if no
subcommand was given. This is slighly ugly, but it allows us to remove
the custom entrypoint, streamlining the use of subcommands. Let's see
an example of calling the version subcommand:

Before:
podman run --rm -it --entrypoint=/usr/bin/bootc-image-builder \
    quay.io/centos-bootc/bootc-image-builder:latest version

After:
sudo podman run --rm -it \
    quay.io/centos-bootc/bootc-image-builder:latest version

Kudos to https://github.com/IKukhta for his code from
spf13/cobra#823 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/cobra-command Core `cobra.Command` implementations kind/bug A bug in cobra; unintended behavior lifecycle/needs-pr Ready for a PR from the community
Projects
None yet
Development

No branches or pull requests