Golang Cobra CLI Pattern

Date: May 15, 2020 Author: Brian Hooper

TLDR

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
  • For MacOS:
    • Executes /scripts/build/darwin.sh
    • Outputs /bin/linux/tool-darwin-amd64
  • For Windows:
    • Executes /scripts/build/windows.sh
    • Outputs /bin/linux/tool-windows-amd64.exe

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!

© Traveler Theme 2023
Let's Stay Connected

Handcrafted by a @KnownTraveler