Building a CLI with Cobra

Command-Line Interfaces (CLIs) are powerful tools for developers, offering a direct and efficient way to interact with applications, automate tasks, and manage systems. In the Go ecosystem, the Cobra framework stands out as the de facto standard for building robust and modern CLIs. It provides a simple, command-based structure that is both easy to develop and intuitive for end-users. This post will guide you through the fundamentals of building your own CLI using Cobra, from setting up your project to creating commands and handling user input with flags.

Why Cobra for Your Go CLI?

While you could parse command-line arguments manually using Go's built-in flag package, Cobra offers a higher-level abstraction that streamlines the entire process. It is the framework behind many popular tools you might already use, such as kubectl, hugo, and the gh (GitHub CLI).

Here’s why Cobra is a great choice:

  • Easy Command-Based Structure: Cobra is built around a structure of commands, subcommands, and flags. This makes organizing complex CLI logic straightforward.
  • Automatic Help Generation: It automatically generates help text for your commands and flags, saving you significant time and ensuring your CLI is user-friendly. When a user provides an invalid flag or subcommand, Cobra's generated help text is a lifesaver.
  • Flag Parsing: It provides a robust system for defining and parsing flags, including support for both persistent flags (available to a command and all its children) and local flags (only available to a specific command).
  • Extensibility: The framework is highly extensible, with features for generating shell autocompletion (Bash, Zsh, etc.), managing configuration, and more.

Getting Started: Project Setup

Let's dive into building a simple CLI called greet. First, ensure you have a recent version of Go installed.

  1. Initialize Your Go Module: Create a new directory for your project and initialize a Go module.
    mkdir greet-cli
    cd greet-cli
    go mod init github.com/your-username/greet-cli
    
  2. Install the Cobra Generator: Cobra comes with a generator tool, cobra-cli, that scaffolds a new application for you. Install it with the following command:
    go install github.com/spf13/cobra-cli@latest
    

    This makes the cobra-cli executable available in your Go bin directory.

Scaffolding the Application

Now, you can use the generator to initialize your Cobra application.

cobra-cli init

This command creates a few files and directories, establishing the basic structure of your application:

  • main.go: The entry point of your application. It simply calls the Execute function from the cmd package.
  • cmd/root.go: This file defines the rootCmd, which is the base command of your application. All other commands will be added as children of the root command.
  • go.mod and go.sum: Your Go module files, now updated with Cobra as a dependency.

At this point, you already have a working application! Run it:

go run main.go

You'll see the default help message generated by Cobra.

Creating Your First Command

Let's create a new command to greet a user. The cobra-cli tool makes this easy.

cobra-cli add hello

This creates a new file, cmd/hello.go, with a boilerplate command structure. Now, let's customize it to print a greeting.

Open cmd/hello.go and find the helloCmd variable. The most important part is the Run field, which is a function that executes when your command is called.

// cmd/hello.go

package cmd

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

// helloCmd represents the hello command
var helloCmd = &cobra.Command{
    Use:   "hello",
    Short: "Prints a friendly greeting",
    Long: `Prints a friendly greeting to the console. 
You can customize the greeting with a flag.`,
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("Hello, World!")
    },
}

func init() {
    rootCmd.AddCommand(helloCmd)
}

The init() function at the bottom adds our new helloCmd to the rootCmd. Now, you can run your new command:

go run main.go hello
# Output: Hello, World!

Adding Flags for User Input

Static commands are useful, but most CLIs need to handle user input. Flags are the primary way to do this in Cobra. Let's add a --name flag to our hello command to customize the greeting.

In cmd/hello.go, we'll modify the init() function to define the flag and the Run function to use it.

// cmd/hello.go

package cmd

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

var name string // Variable to store the value of the flag

// helloCmd represents the hello command
var helloCmd = &cobra.Command{
    Use:   "hello",
    Short: "Prints a friendly greeting",
    Long: `Prints a friendly greeting to the console. 
You can customize the greeting with a flag.`,
    Run: func(cmd *cobra.Command, args []string) {
        if name != "" {
            fmt.Printf("Hello, %s!\n", name)
        } else {
            fmt.Println("Hello, World!")
        }
    },
}

func init() {
    rootCmd.AddCommand(helloCmd)

    // Here we define our flag and bind it to the `name` variable.
    helloCmd.Flags().StringVarP(&name, "name", "n", "", "Name to greet")
}

Here’s what we changed:

  1. Declared a package-level variable name to hold the flag's value.
  2. In the init() function, we called helloCmd.Flags().StringVarP(). This method defines a string flag.
    • The first argument (&name) is a pointer to the variable where the flag's value will be stored.
    • The second ("name") is the long name of the flag (--name).
    • The third ("n") is the shorthand version (-n).
    • The fourth ("") is the default value.
    • The fifth ("Name to greet") is the usage description shown in the help text.
  3. In the Run function, we check if the name variable is not empty and use it to print a personalized greeting.

Now, test your updated command:

# Test with the long flag
go run main.go hello --name="Alice"
# Output: Hello, Alice!

# Test with the short flag
go run main.go hello -n="Bob"
# Output: Hello, Bob!

# Test the help text
go run main.go hello --help

When you run the --help command, you'll see your new flag listed automatically in the output.

Conclusion

Cobra provides a powerful and declarative framework for building CLIs in Go, allowing you to focus on your application's core logic instead of boilerplate argument parsing and help text generation. By leveraging its scaffolding tool and simple API for commands and flags, you can quickly build professional-grade command-line tools. We've only scratched the surface of what Cobra can do, but this foundation is all you need to start creating your own powerful CLIs.

Resources

← Back to golang tutorials

Author

Efe Omoregie

Efe Omoregie

Software engineer with a passion for computer science, programming and cloud computing