Building a CLI Tool Using Go and Cobra-CLI
Developing fast applications these days has never been easier with the help of all the tools we have access to. Go is a programming language built by some of the greatest Software Engineers of our time. It helps build anything back-end related, from CLI tools to APIs to even Blockchains and tools like Docker.
In this tutorial, I will go through how to build a simple CLI tool using Go, with the help of Cobra, and how you can use it on the most famous operating system for software development, Linux.
Prerequisites
The things you need in order to complete this tutorial include:
- Go - version 1.20
- Cobra CLI
- A Linux system - I am using Ubuntu 22.04
Installation
In this section, we will be installing the two tools we will be using, which are Go and Cobra CLI.
Go Installation
To download Go, you first have to download the tar.gz
file for your OS from this page:
https://go.dev/dl/
Once downloaded, run the below command to remove any order versions and install this version:
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.4.linux-amd64.tar.gz
Once that is done, you should export the PATH for the binaries file of Go. You can do that by running the below line in your terminal, or adding it at the bottom of your ~/.bashrc
file:
export PATH=$PATH:/usr/local/go/bin
You should now have Go installed on your machine, and you should be able to use it globally, to ensure it has been downloaded, run the below command:
go version
If the output shows the version you attempted to install, it means that the installation was successful!
Cobra CLI Installation
Since you have Go downloaded, it should now be easy to install Cobra CLI. You can run the below command to install the binary:
go install github.com/spf13/cobra-cli@latest
Once that is complete, you can test the installation by running:
cobra-cli
If that displays a description of the application, it means the installation was a success.
Project Setup
In this section, we create the main files of the application.
Creating The Main Directory & Initializing The Go Modules
We will be building a “Quotes” app that contains a few quotes, along with an ID for each one. The tool will be able to:
- List all quotes, including their IDs
- List a specific quote using its ID
We will first create a directory called quotes
and access it:
mkdir quotes
cd quotes
Then we need to initialize a Go Modules file, you can do it by running the command below:
go mod init quotes
This is the file that includes the dependencies of the tool, as well as the Go version you have. The quotes
in the command refers to the name of the tool or module.
You should now see a file called go.mod
in your directory.
Setting Up The Cobra CLI Tool
The Cobra CLI tool makes it very easy to get started with building CLI-based tools. You can run the following command to create the necessary files to start using the tool:
cobra-cli init
The above command will create a bunch of files, including:
main.go
- This is the main file that will allow you start and run the tool. They even added the necessary command there to run the Cobra tool.
cmd
directory- This directory includes all the commands, each one being its own file. You should see one file there called
root.go
. This is first part of the application. It describes how the tool will run and it shows a short and long description of the tool.
- This directory includes all the commands, each one being its own file. You should see one file there called
We can build and run the tool by running the commands below:
go build .
./quotes
The first command builds the tool, and the second command runs it. You should see the long description of the application which was in the root.go
file.
Understanding The Cobra Command Code
If we remove all the comments in the file, it should look like this:
package cmd
import (
"os"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "quotes",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
}
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
The first part of the file includes the package name and the imports.
The second part that starts with var rootCmd = &cobra.Command{
is an initialization of a struct type in Go. The struct is taken from the cobra
package and currently includes three things:
Use
- which describes the keyword that will be used in the command-line to run that specific fileShort
- which is a short and brief description of the toolLong
- which is a longer description of the tool
Note: there are more values that we will be adding later to other files
The third part consists of two functions Execute
and init
. The Execute
simply runs the tool. The init
sets up any necessary values, as well as accept flags
, just like the toggle
flag we see in the file.
Creating The Show
Command
We will start by creating a command show
which will be the main part of the tool. It will:
- List all the quotes
- List a specific quote
While in the root directory of the tool, we can run the following command to create the show.go
file and command:
cobra-cli add show
Once that is done, you should see a new file show.go
in the cmd
directory.
This file will include a new value inside the cobra.Command
which is the Run
value. This is the function that basically runs when the command is triggered.
If you are interested, you can change the descriptions of both the root.go
and show.go
to something more fitting. But for now, I will be skipping that part.
We can start working on the actual functionality now.
Adding & Showing The Quotes
We will be writing all of the code in the show.go
file and inside the Run
function.
We can start by creating a Struct type that describes a Quote:
type Quote struct {
ID int
Author string
Text string
}
The ID
will be a unique, numerical value describing the quote. It will be in a series of sequential numbers starting from 1
.
Then we can create an array of Quotes:
quotes := []Quote{
{
ID: 1,
Author: "Alan Kay",
Text: "Simple things should be simple, complex things should be possible",
},
{
ID: 2,
Author: "Edsger W. Dijkstra",
Text: "Simplicity is prerequisite for reliability.",
},
{
ID: 3,
Author: "Ralph Johnson",
Text: "Before software can be reusable it first has to be usable.",
},
}
The code above uses the Quote struct to create an array of quotes. I chose my favorite quotes from this page: https://hackernoon.com/40-thought-provoking-software-engineering-quotes-xp2z3tdr
Since we have the quotes set, we can now start printing them. We can do it by adding the lines below:
for _, quote := range quotes {
fmt.Printf("%d. %s - %s\n", quote.ID, quote.Text, quote.Author)
}
The for
loop above basically loops over the quotes and prints them in a specific way.
We can now build and run the tool by running the commands below:
go build .
./quotes show
After running the above commands, you should see this output:
1. Simple things should be simple, complex things should be possible - Alan Kay
2. Simplicity is prerequisite for reliability. - Edsger W. Dijkstra
3. Before software can be reusable it first has to be usable. - Ralph Johnson
Great! We managed to create our first command and run it by using the keyword specified in the show.go
file under the Use
in the cobra.Command
struct. We can now expand and add an option to display a specific quote from the list.
Adding an ID Flag & Option
Now that we have the quotes in a specified Struct, we can add a flag that allows choosing which quote to display, based on the ID that was provided. We can start doing it by adding a flag at the end of the init
function in the show.go
file:
showCmd.Flags().Int("id", 0, "ID of the quote to show")
The line has three parts:
showCmd
- This specifies the command that we will be adding the option to, which is the
showCmd
command in our case
- This specifies the command that we will be adding the option to, which is the
.Flags()
- This adds a flag to the
showCmd
command.
- This adds a flag to the
.Int("id", 0, "ID of the quote to show")
- This adds the details of the flag we wish to add. It consists of four parts:
.Int(...
- This is the type of data we want to add. In our case, it is an Integer, therefore it is anInt
."id"
- This is the key or name of the value and how you would run it in the command.0
- This is the default value for that key. So if no value was specified by the user while running the command, this would be the value of theid
."ID of the quote to show"
- This is simply a description of the option we have.
- This adds the details of the flag we wish to add. It consists of four parts:
We can start implementing the functionality of it in the Run
command.
Create a new file right before the for
loop we added previously, and add the following commands:
...
id, _ := cmd.Flags().GetInt("id") // skipping error for simplicity
if id > 0 {
var printableQuote Quote
for _, quote := range quotes {
if quote.ID == id {
printableQuote = quote
break
}
}
if printableQuote.ID != 0 {
fmt.Printf("%d. %s - %s\n", printableQuote.ID, printableQuote.Text, printableQuote.Author)
}
return
}
...
We start off by retrieving the ID (it returns an Error but we will skip it for now).
id, _ := cmd.Flags().GetInt("id")
We access the command flags, and retrieve an Int
with the key id
, just like how it was specified previously.
We then check if the value of id
is greater than 0
. Keep in mind 0
was the default value, meaning we have to skip this piece of code if id
was equal to 0
.
Once it pass by the if
statement, it will find the quote using the id
. Once it finds it, it will print it using the same format we use previously in the for
loop. If the quote with the specified id
was not found, nothing happens.
We can run the command by first building the tool:
go build .
Then running the command:
./quotes show --id 3
We should see the quote with ID 3 get printed to the screen:
user@ubuntu ~/quotes> ./quotes show --id 3
3. Before software can be reusable it first has to be usable. - Ralph Johnson
If we try to run it with an id
that does not exist in the quotes
array (e.g. 4) nothing will happen.
Conclusion
I hope you found it easy building a CLI tool with Go and Cobra. It currently is my favorite way of building CLI-based tools for whatever purpose. Combining the features of Cobra with the speed and ease-of-use of Go makes it very entertaining to build a tool this way, while guaranteeing high speeds in the execution of the tool.
You can find the documentation for the Cobra tool in the link below: https://github.com/spf13/cobra
Thank you for reading this tutorial. I hope you enjoyed it.