Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does cobra commander for go (golang) work?

Tags:

command

go

I am trying to understand how to create costume commands for go using the cobra (and viper) libraries and be able to use them.

Specifically I was trying to understand and make the example they provide work. The example is the following:

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

func main() {

    var echoTimes int

    var cmdPrint = &cobra.Command{
        Use:   "print [string to print]",
        Short: "Print anything to the screen",
        Long:  `print is for printing anything back to the screen.
        For many years people have printed back to the screen.
        `,
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println("Print: " + strings.Join(args, " "))
        },
    }

    var cmdEcho = &cobra.Command{
        Use:   "echo [string to echo]",
        Short: "Echo anything to the screen",
        Long:  `echo is for echoing anything back.
        Echo works a lot like print, except it has a child command.
        `,
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println("Print: " + strings.Join(args, " "))
        },
    }

    var cmdTimes = &cobra.Command{
        Use:   "times [# times] [string to echo]",
        Short: "Echo anything to the screen more times",
        Long:  `echo things multiple times back to the user by providing
        a count and a string.`,
        Run: func(cmd *cobra.Command, args []string) {
            for i:=0; i < echoTimes; i++ {
                fmt.Println("Echo: " + strings.Join(args, " "))
            }
        },
    }

    cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")

    var rootCmd = &cobra.Command{Use: "app"}
    rootCmd.AddCommand(cmdPrint, cmdEcho)
    cmdEcho.AddCommand(cmdTimes)
    rootCmd.Execute()
}

However, I have made the file for it, in package main and I can't seem to make it work properly. I do understand that the rootCmd is not suppose to be executable. However, it seems to me that the Use field behaves inconsistently (as far as I can tell). I was trying to understand how commands actually run in the command line and compile the file in different ways to experiment this.

For example I compiled this file I named as cobra.go with go build cobra.go and now I have an executable cobra.

If I do ./name it behaves the same way as ./name help and prints:

Usage:
  app [command]

Available Commands:
  print [string to print]   Print anything to the screen
  echo [string to echo]     Echo anything to the screen
  help [command]            Help about any command

 Available Flags:
      --help=false: help for app

Use "app help [command]" for more information about that command.

However the description incorrectly says that to use it you must do "app help [command]".

If I do that ./app help print (with only the cobra executable of course) it prints:

zsh: no such file or directory: ./app

if I do app help print it also doesn't work (since it isn't found in PATH):

zsh: command not found: app

So my understanding is that, unless I build it with the same name as root, cobra will act funny.

So it means that the use field has to match the file name for the output of help to make sense and for us to be able to run its sub commands properly, right?

Also, how is one able to have (compile) multiple different "root" commands in the same project, if only one package main is allowed? If one has multiple "root" commands under main, it shouldn't work, right? I will try some of these ideas soon.

like image 616
Charlie Parker Avatar asked Mar 20 '23 00:03

Charlie Parker


1 Answers

You could check how cobra is used is other projects (go-search).

For instance, the bitballoon-cli project defines multiple commands, each in their own file, each in the main package. Like create.go:

package main

import (
    "github.com/BitBalloon/bitballoon-go"
    "github.com/spf13/cobra"
    "log"
)

var createCmd = &cobra.Command{
    Use:   "create",
    Short: "create a new BitBalloon site",
    Long:  "creates a new BitBalloon site and returns an ID you can deploy to",
}

var siteName, customDomain, password, notificationEmail string

func init() {
    createCmd.Run = create

    createCmd.Flags().StringVarP(&siteName, "name", "n", "", "Name of the site (must be a valid subdomain: <name>.bitballoon.com)")
    createCmd.Flags().StringVarP(&customDomain, "domain", "d", "", "Custom domain for the site (only works for premium sites)")
    createCmd.Flags().StringVarP(&password, "password", "p", "", "Password for the site")
    createCmd.Flags().StringVarP(&notificationEmail, "email", "e", "", "Notification email for form submissions (only works for premium sites)")
}

func create(cmd *cobra.Command, args []string) {
  // your function for this command
}

You could check if that way of defining and adding a command to your project works in your case.


4 years later, see the video "justforfunc #32: CLI tools with Cobra", by Francesc Campoy.

like image 192
VonC Avatar answered Mar 27 '23 23:03

VonC