Create a command line tool with go and cobra
Tags: go, cobra
Published: 2019-11-05 17:06:13

---
title: Create a command line tool with go and cobra
published: true
description: 
tags: [golang, cobra]
---

Every now and then, I like to start playing with a new programming language. The one I picked this was [go](www.golang.org). To start simple, I decided to build a simple command line utility that would do some simple tasks, which I usually do with bash scripts. Reading about how to implement this, I discovered a very cool project called [cobra](https://github.com/spf13/cobra), that would make the task a lot easier. And apparently is used by a lot of big projects.

If my bash scripts are working, why rewrite them? Because with go it is easy to generate an executable. Which means I can distribute them easily in my team, without worrying about their setup. 

## The development environment

Since I don't want to install go in my machine, and I loved working with VS Code remote containers, I will create one with go. If you don't know what I am talking about, [check it out](https://code.visualstudio.com/docs/remote/containers).

I will create a folder called uc (this will be the root of my project), and initialize the container inside it.

Once my container is ready, I need to install cobra. 

```
go get -u github.com/spf13/cobra/cobra
```

Cobra has a nice utility to help you get started, called cobra generator. 

To create the initial structure of the project:

```
cobra init --pkg-name github.com/kgoedert/uc . 
```

If you want to create your project somewhere else, other than the current directory, just pass it as a parameter.

```
cobra init --pkg-name github.com/kgoedert/uc /path/to/some/folder
```

Which will give me an output like this:

```
Your Cobra applicaton is ready at
/workspaces/uc
```

You should get an output close to this one:

```
uc
├── LICENSE
└── src
    └── github.com
        └── kgoedert
            └── uc
                ├── cmd
                │   └── root.go
                └── main.go
```

You already have something you can build and run.

Since I am working inside the container, I can build and install my package with:

```Ctrl + Shift + P > Go: Install Current Package```

Just remember to execute this with the main.go file open and selected.

A binary will be generated on a bin directory. You can open a terminal and run it with

```
./uc
```

You should see an output like this:

```
root@10b479f71c07:/workspaces/uc/bin# ./uc 
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.

subcommand is required
```

**Important:** Since I am using visual code inside a container, my GOPATH is different, if I am using the terminal than if I am using the plugin commands from the go extension menu. More informations here [https://github.com/Microsoft/vscode-go/wiki/GOPATH-in-the-VS-Code-Go-extension](https://github.com/Microsoft/vscode-go/wiki/GOPATH-in-the-VS-Code-Go-extension)

## Adding commands

I will add a command called *createFolder*, that will simply create an empty folder.

To do that, open a terminal go to the directory that has the *main.go* file in it, in my case it is *uc*, and type:

```
cobra add createFolder
```

This will allow me to have the command *uc createFolder* in my project. I will also add a parameter to specify the name of the folder I want to create. 

Cobra add a file called *createFolder.go* in my *cmd* folder. At this point, my project structure, looks like this:

```
uc
├── bin
│   └── uc
├── LICENSE
└── src
    └── github.com
        └── kgoedert
            └── uc
                ├── cmd
                │   ├── createFolder.go
                │   └── root.go
                └── main.go
```

## Command implementation

In the *createFolder.go*, cobra created some templates to get you started. There is a variable to represent your command. What it will do, its name, and some help to your users.

```
var createFolderCmd = &cobra.Command{
	Use:   "createFolder",
	Short: "Creates a folder",
	Long:  `Creates a folder with a name as parameter`,
	Run: func(cmd *cobra.Command, args []string) {
		createFolder(cmd)
	},
}

func createFolder(cmd *cobra.Command) error {
	name, _ := cmd.Flags().GetString("name")

	if name == "" {
		return errors.New("Your folder needs a name")
	}

	err := os.MkdirAll(name, os.ModePerm)
	if err != nil {
		fmt.Printf("Could not create the directory %v", err)
	}
	fmt.Println("Folder " + name + " created.")

	return nil
}

```

You can see my implementation is very simple. In the function called *init* is where you are going to determine to which other command your command is a subcommand to, and add parameters to it.

```
func init() {
	rootCmd.AddCommand(createFolderCmd)

	createFolderCmd.Flags().StringP("name", "", false, "Name of the folder you want to create.")
}

```

My *createFolder* command will be a subcommand to the root command, and will have one parameter, which will be *n*, for the folder name. So when called it will look like

```
./uc createFolder -n newFolder
```

Now, let's add another simple command. Initializing a git repository inside a given folder.

My code on the *gitInit.go* file, looks like this:

```
var gitInitCmd = &cobra.Command{
	Use:   "gitInit",
	Short: "Initializes a git repository on a given path",
	Run: func(cmd *cobra.Command, args []string) {
		gitInit(cmd)
	},
}

func gitInit(cmd *cobra.Command) error {
	folder, _ := cmd.Flags().GetString("folder")

	if folder == "" {
		return errors.New("Your need to inform a path to initialize the git repository")
	}

	command := exec.Command("git", "init", folder)
	err := command.Run()
	if err != nil {
		fmt.Printf("Could not create the directory %v", err)
	}
	fmt.Println("Git repository initialized in " + folder)

	return nil
}

func init() {
	rootCmd.AddCommand(gitInitCmd)

	gitInitCmd.Flags().StringP("folder", "f", "", "Path where the git repository will be initialized.")
}
```

My command will again, be a subcommand of the root command. And will take a *-f* as a parameter for the name of the folder.

Now, suppose you want to make a command that will aggregate some of the commands you have previously created. One possible solution would be to create a command called for example, *all*, that would execute the other two. Like this:

```
var allCmd = &cobra.Command{
	Use:   "all",
	Short: "Executes both commands",
	RunE: func(cmd *cobra.Command, args []string) error {
		createFolder(cmd)
		gitInit(cmd)

		return nil
	},
}

func init() {
	rootCmd.AddCommand(allCmd)

	allCmd.Flags().StringP("folder", "f", "", "Path where the git repository will be initialized.")
	allCmd.Flags().StringP("name", "n", "", "Name of the folder you want to create.")
}
```

To use your *all* command, you would:

```
./uc all -n newProject -f newProject
```

This would create a folder and initialize a git repository on it.

Although the commands I used were very simple, I hope I was able to show how you can compose your commands to create something very powerful.

You can find the source code [here](https://github.com/kgoedert/uc)