How to Use the Cobra Package in Go
A Command-Line Interface (CLI) is a type of application that runs exclusively in the command-line terminal.
Typically, such programs are used to manage various tools related to developing and maintaining network infrastructure.
The interaction process is simple:
-
The user types the name of the CLI application, the command name, parameters, and sometimes additional flags into the terminal.
-
The CLI application performs the requested action and sends a text response to the terminal.
CLI applications may seem outdated due to the lack of a graphical user interface (GUI), but they are still considered the most versatile, fast, and convenient way for system administration.
Creating CLIs with Go and Cobra Copy link
To create a CLI in Go, you can use a special package called Cobra, which is developed by third-party developers. It is built on top of the flag package (a command-line flag parser) from Go's standard library and provides a higher level of abstraction.
Cobra is a complete CLI platform for the Go language, consisting of two main components:
- A library for creating modern CLI applications.
- A CLI tool for quickly building applications based on standard (for Cobra) command handler files.
Cobra was originally developed by one of the Go team members, Steve Francia (spf13), for the Hugo project — a special framework for building websites. Over time, Cobra became one of the most popular packages in the Go community.
Features of Cobra Copy link
Cobra offers several simple features for building modern command-line interfaces. Additionally, Cobra includes a high-level controller to help organize the code for the CLI application being developed.
Cobra implements:
- A command hierarchy
- Powerful argument and flag parsing
- Flag hierarchy (global and local)
- Subcommand checking
- POSIX compliance
- Automatic help generation for commands and flags
In fact, large projects such as Kubernetes, Hugo, and CockroachDB are built on Go and use the Cobra package to handle commands.
CLI commands follow a fairly standard pattern:
{application} {command code} [arguments] [--flags and their parameters]For example, commands in real projects might look like this:
kubectl get all -n kube-systemOr like this:
etcdctl put first secondCobra Architecture Copy link
The working entities in Cobra can be divided into three types — each represents the structure of commands in the console terminal:
- Commands: These specify specific actions that need to be performed, much like in any classic CLI application.
- Arguments (Args): These are items or entities passed to a command, which the command works with and returns the result.
- Flags: Short modifiers for commands (i.e., specific actions) that make certain adjustments to the execution and affect the final result of the CLI application's operation.
A Little About POSIX Compatibility Copy link
The POSIX standard defines a pattern (scheme) for organizing arguments and flags that CLI applications should follow. This is the classic format that most developers are familiar with — numerous Linux utility programs (such as ls, cp, useradd) and third-party applications follow this convention.
It is important to remember that the command scheme is strictly formalized in the standard and looks as follows:
application_name [-a] [-b] [-c argument] [-d|-e]Each application may have multiple versions of the same option — long and short forms. There is a clear rule that the short version must consist of only one character.
Step 1. Environment Setup Copy link
Checking Go
First, check whether the Go compiler is installed on your system. You can do this by running the version query command:
go versionIf Go is installed, the console will display the Go version along with the operating system’s short name.
Creating the Project Directory
Next, create a separate directory for our Cobra project:
mkdir CobraProjectAfter that, navigate into it:
cd CobraProjectGolang has some peculiarities in its module system, which is necessary for connecting packages. Therefore, you need to initialize the project directory with a special command:
go mod init CobraProjectThis will turn the directory into a full-fledged Go module, and the console will display a message about the creation of the module named CobraProject.
Step 2. Installing the Cobra Package Copy link
Downloading the Package from the Official Repository
Starting from Go 1.18, Go includes a special command go install, which automatically installs remote modules. Therefore, we will use it to download the Cobra package from the official GitHub repository:
go install github.com/spf13/cobra-cli@latestNote that with the @latest tag we are installing the latest release.
Initializing the CLI
After installation, the executable file cobra-cli will be available in the terminal. We will use this tool to initialize the Cobra project in our working directory — at this point, you should already be in that directory:
cobra-cli initOnce executed, this will create several files in your working directory, containing the standard Cobra package code along with the project name CobraProject.
The file structure will look like this:
CobraProject/
cmd/
root.go
main.go
go.mod
go.sum
The main.go file is the entry point for the CLI application. Its default content will look something like this:
package main
import (
"CobraProject/cmd" // the path may vary depending on the location of the working directory
)
func main() {
cmd.Execute()
}
All commands are placed as separate files in the /cmd directory.The root.go file is the root command handler — essentially the base command for any command-line interface.
For example, consider the following command:
go get URLHere, go is the root command, which is handled by root.go, and get is a subcommand, whose handler is placed in a file different from root.go.
Building the CLI
To build the CLI application, you use the same command as for building any regular Go binary project:
go buildBy default, the executable file will appear in the project’s working directory.
To make the built CLI application usable, you also need to install it:
go installAfter this, the CLI application will be available for execution directly from the terminal. To use it, simply type the project name in the console:
CobraProjectIf everything is set up correctly, the standard output for the command without any parameters will appear in the console. Of course, you can modify this standard output later in the root.go file.
Step 3. Creating a Function for the Command Copy link
Each command entered in the terminal calls a corresponding Go function, which executes the logic for that command. Any parameters and flags specified in the terminal are passed into the function.
As a simple example, we will implement a small function that displays the time in the current time zone. To do this, we will use the time package.
After initializing the CLI, the cmd directory should have been created in your working directory. Let's go to it:
cd cmdNow, let's create a file that will contain our function:
touch timefunc.goThe code inside the file will look like this:
package cmd // specify the name of our package
import "time" // import the standard Go time package
func getTimeFromZone(zone string) (string, error) {
loc, err := time.LoadLocation(zone) // get the current location
// check for error
if err != nil {
return "", err // return an empty result with error details
}
timeNow := time.Now().In(loc) // get the current time based on the location
return timeNow.Format(time.RFC1123), nil // return the formatted result without error details
}
As you can see, the function returns two values: the result and any error data. You can use it in the CLI to retrieve the time for a specified time zone.
Step 4. Adding a Command to the CLI Copy link
Now that the functional part of our application is ready, we can "register" the command in the CLI application for external access.
There is a separate add command for this:
cobra-cli add timefromzoneAfter running this command, a timefromzone.go file will appear in the cmd folder with the standard code inside.
In this same folder, you will also find the root.go file, responsible for the "root" command processing, i.e., the command without any parameters.
It’s easy to guess that the handlers for console commands are formed in the file system as separate Go source files.
Let’s open the newly created file and populate it with the following code:
package cmd
import (
"fmt"
"log"
"github.com/spf13/cobra"
)
var timefromzoneCmd = &cobra.Command{
Use: "timefromzone",
Short: "Returns the time from a given geographical zone",
Long: `This command returns the time from a specified geographical zone. It accepts only one argument — the zone for which the time is required. The result is returned in the RFC1123 format.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
timefromzone := args[0]
timeNow, err := getTimeFromZone(timefromzone)
if err != nil {
log.Fatalln("Invalid time zone")
}
fmt.Println(timeNow)
},
}
func init() {
rootCmd.AddCommand(timefromzoneCmd) // add the new command to the root command
}
Let’s break down what each field means in the command definition:
- Use: The name under which the command will be available in the terminal.
- Short: A brief description of the command, which will be displayed to the user in the console.
- Long: A full description of the command, which will be shown to the user in the console.
- Args: The exact number of arguments required for the command to function.
- Run: The handler function where we call and process the previously created
getTimeFromZonefunction.
In some cases, you could simplify the code by writing the logic directly inside the command handler function, like this:
import "time"
var timefromzoneCmd = &cobra.Command{
Use: "timefromzone",
Short: "Returns the time from a given geographical zone",
Long: `This command returns the time from a specified geographical zone. It accepts only one argument — the zone for which the time is required. The result is returned in RFC1123 format.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
zone := args[0]
loc, err := time.LoadLocation(zone)
if err != nil {
log.Fatalln("Invalid time zone")
}
fmt.Println(time.Now().In(loc).Format(time.RFC1123))
},
}
In this case, we directly implemented the logic for retrieving the time inside the Run function. If the time zone is invalid, an error message is logged.
Once the command is added, we just need to reinstall our CLI application:
go installNow, we can use the application from the terminal by specifying the command name and passing the time zone code as an argument:
CobraProject timefromzone Europe/NicosiaThe console output will look something like this:
Sun, 10 Nov 2024 12:41:06 Europe/NicosiaYou can find a complete list of time zones and their codes in Wikipedia.
Step 5. Adding Flags to CLI Copy link
Typically, when running command-line applications, you can specify flags in addition to parameters. Flags are options that modify the behavior of a specific command. They are easily recognized by the preceding hyphen (or double hyphen).
The inclusion of flags in a CLI application adds variability and flexibility to the command behavior. Without flags, you would have to create many complex functions with a lot of redundant code. In this sense, flags help standardize the application.
Cobra has two types of flags:
-
Local flags: These only apply to the specific command.
-
Persistent flags: These can apply to all commands and subcommands.
Let’s return to the timefromzone.go file and modify the initialization function to add a flag. The flag will specify the desired time format.
Here’s how you can add the flag to your command:
func init() {
rootCmd.AddCommand(timefromzoneCmd) // Add the defined command to the root command
timefromzoneCmd.Flags().String("format", "", "Outputs the time in the yyyy-mm-dd format") // Add a flag to the command
}
This adds a flag named --format, which specifies the time format.
Here is the complete updated file with flag handling:
package cmd
import (
"fmt"
"time"
"github.com/spf13/cobra"
)
var timefromzoneCmd = &cobra.Command{
Use: "timefromzone",
Short: "Returns the time from a given geographical zone",
Long: `This command returns the time from a specified geographical zone. It accepts only one argument — the zone for which the time is required. The result is returned in RFC1123 format.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var date string
zone := args[0]
loc, _ := time.LoadLocation(zone) // Load the location from the zone argument
fla, _ := cmd.Flags().GetString("format") // Get the value of the 'format' flag
if fla != "" {
date = time.Now().In(loc).Format(fla) // If flag is provided, use custom format
} else {
date = time.Now().In(loc).Format(time.RFC1123) // Default format
}
fmt.Printf("Current time in timezone %v: %v\n", loc, date)
},
}
func init() {
rootCmd.AddCommand(timefromzoneCmd) // Add the timefromzone command to the root command
timefromzoneCmd.Flags().String("format", "", "Outputs the time in the yyyy-mm-dd format") // Add the 'format' flag
}
Now, let's reinstall the updated CLI application:
go installTo use the new flag, run the command with the --format flag, like this:
CobraProject timefromzone Europe/Nicosia --format 2006-01-02The output will be formatted according to the flag, like this:
Current time in timezone Europe/Nicosia: 2024-11-10Here, the --format flag explicitly tells the command to display the time in the yyyy-mm-dd format, and the result will reflect this format.
Conclusion Copy link
The Cobra package for the Go programming language is an excellent solution that helps developers abstract away the complexities of low-level command-line parsing functions provided by the standard library.
Cobra is a kind of framework for CLI applications that alleviates the "headache" developers face when working with the command-line terminal, allowing them to focus more on business logic.
Each command is represented as a separate file in the /cmd directory, and you can modify it using flags. This is convenient because you can explicitly build a hierarchy of commands and control the process of handling them by editing hook functions like init or run.
This feature gives Cobra CLI applications a more structured layout and less clutter, forming a solid framework.
It’s important to note that third-party developers created Cobra, so it is hosted in a separate GitHub repository and is not part of the Go standard library.
Additionally, on the official Cobra website, you can find installation instructions and details about using the command-line parser.
On our cloud app platform you can deploy Golang apps, such as Beego and Gin.