Golang Cobra CLI Pattern
Date: May 15, 2020 Author: Brian HooperTLDR
This post is a quick walk-through of a simple pattern I often use for developing new Golang CLI tools.
The source code for this post is available via a snippets repo hosted on GitLab.
If you’d like to clone the snippets for this post along with others from my blog:
git clone git@gitlab.com:KnownTraveler/snippets.git
Introduction
This post covers setting up a new Command Line Utility (CLI) Project using Golang and Cobra which is a library for creating powerful modern CLI applications as well as a program to generate applications and command files. Cobra is used in many Go projects for example:
- Docker
- Golangci-lint
- Helm
- Hugo
- Kubernetes
- OpenShift
- … just to name a few
Configuration
Install Golang
Verify Golang Installation
# COMMAND
which go
# OUTPUT
# /usr/local/go/bin/go
Verify Golang Version
# COMMAND
go version
# OUTPUT
# go version go1.15.6 linux/amd64
Set Environment Variables
Set golang environment variables in ~/.bashrc
# GOLANG
# ----------------------------------------
# Setting GOPATH
export GOPATH=$HOME/.go
# Setting GOROOT
export GOROOT=/usr/local/go
# Setting PATH for GOLANG BINARY
export PATH="$GOPATH/bin:$GOROOT/bin:$PATH"
Verify golang environment variables
# COMMAND
echo $GOROOT
# OUTPUT
# /usr/local/go
# COMMAND
echo $GOPATH
# OUTPUT
# /home/<user>/.go
Project Structure
Here is a base structure I use for developing Golang Command Line Utility:
./golang-cli # Golang CLI Project
|__ /bin # Binary Directory
| |__ /darwin
| | |__ tool-darwin-amd64 # Darwin Binary (make compile)
| |__ /linux
| | |__ tool-linux-amd64 # Linux Binary (make compile)
| |__ /windows
| | |__ tool-windows-amd64.exe # Windows Binary (make compile)
| |
| |__ tool # Symlink to Tool Binary (make)
| |__ tool-${GOOS}-${GOARCH} # Tool Binary (make)
|
|__ /cmd
| |__ /tool # Command Line Utility
| |__ main.go
| |__ root.go
| |__ version.go
|
|__ /log # Log Package, Custom for CLI
|__ /scripts
| |__ /build # Build Scripts
| |__ /deploy # Deploy Scripts
|__ /version # Version Package
|__ .gitignore
|__ .golangci.yaml # Linter Configuration
|__ go.mod
|__ go.sum
|__ LICENSE
|__ Makefile # Makefile for Common Task
|__ README.md
Components
This project uses make
to define and execute our build tasks.
make
Using make
runs the following tasks by default:
- clean - clean
/bin
directory - format - format .go files using
go fmt
- lint - lint .go files using
golangci-lint
- test - test _test.go files using
go test
- build - build binary target
/bin/tool-${GOOS}-${GOARCH}
via/scripts/build/binary.sh
- link - link binary target
/bin/tool
and$GOPATH/bin/tool
make compile
Using make compile
will run the following scripts:
- For Linux:
- Executes
/scripts/build/linux.sh
- Outputs
/bin/linux/tool-linux-amd64
- Executes
- For MacOS:
- Executes
/scripts/build/darwin.sh
- Outputs
/bin/linux/tool-darwin-amd64
- Executes
- For Windows:
- Executes
/scripts/build/windows.sh
- Outputs
/bin/linux/tool-windows-amd64.exe
- Executes
make (task)
Using make <task>
will run individual tasks:
# Formatting
make format
# Linting
make lint
# Testing
make test
Linking
The make
process outlined above will manage symlinks to the utility in your local workspace. This is especially helpful so that you can quickly iterate on development, testing and demonstration.
A link is created within the project’s /bin
directory
/bin
|__ tool -> tool-linux-amd64
|__ tool-linux-amd64
Another link is also created at $GOPATH/bin
# COMMAND
ls -al $GOPATH/bin
# OUTPUT
# tool -> /home/<user>/GitLab/knowntraveler/snippets/golang-cli/bin/tool-linux-amd64
Which means the latest build of the utility is immediately available for you across projects
# COMMAND
which tool
# OUTPUT
#/home/<user>/.go/bin/tool
# COMMAND
tool version
# OUTPUT
#tool v0.0.1 (alpha)
Next
The above structure and configuration is useful for developing new Golang Command Line Utility (CLI) projects and maintaining them over time as they scale. The workflow via make
is beneficial because it standardizes and reinforces a consistent process and good practices (formatting, linting, testing, etc). As a practitioner I am often building different tools and utilities. Using a standardized approach is especially helpful when working across multiple projects or rapidly prototyping new tools.
Resources
Updates
2021-01-10
- Updated to use latest version of Golang go1.15.6
2020-05-15
- In upcoming posts I will be using the above approach to develop additional custom tools which includes work that I am doing in separate open source projects like Anvil and Supply. More to come!
Let's Work Together
Let's Get Ship Done!
Handcrafted by a @KnownTraveler