Real-World Project - Building a CLI Tool

The Script That Grew Into a Production Tool

It started as a simple bash script. Our team needed to deploy services to Kubernetes, and I got tired of typing the same 15 commands every time:

#!/bin/bash
# deploy.sh

kubectl config use-context production
docker build -t myapp:$VERSION .
docker push myapp:$VERSION
kubectl set image deployment/myapp myapp=myapp:$VERSION
kubectl rollout status deployment/myapp

Copy, paste, hope nothing breaks. But then:

  • Someone deployed to production instead of staging (wrong context)

  • A deployment failed mid-rollout (no health checks)

  • Rollbacks required manual intervention

  • No audit log of who deployed what

After the third production incident, I spent a weekend building a proper CLI tool in Go:

# New workflow
deploy --env=staging --version=v1.2.3 --wait

# Output:
βœ“ Building Docker image myapp:v1.2.3
βœ“ Pushing to registry
βœ“ Deploying to staging
βœ“ Waiting for rollout... (45s)
βœ“ Running health checks
βœ“ Deployment successful!

Deployed by: [email protected]
Deploy time: 2024-01-15 14:23:45 UTC

The impact:

  • Zero wrong-environment deployments (validation built-in)

  • Automatic rollbacks on failed health checks

  • Complete audit trail (who, what, when)

  • Saved 2 hours per week per developer (15 developers = 30 hours/week)

That CLI tool has handled 2,000+ deployments without a single incident. This article covers how I built it.


Why Go for CLI Tools?

Single Binary

Ship one file. No runtime, no dependencies, no "works on my machine."

Cross-Compilation

Fast Startup

170x faster startup. Matters for automation scripts that run thousands of times.


Building CLI with Cobra

Cobra is the de facto standard for Go CLI tools. Used by Kubernetes, Hugo, GitHub CLI.

Installation

Basic CLI

Run it:

Adding a Command

Usage:

Adding Flags

Usage:


Configuration with Viper

Viper handles configuration from files, environment variables, and flags.

Installation

Basic Configuration

config.yaml:

Environment Variables

Combining Cobra and Viper

Priority (highest to lowest):

  1. Command-line flags

  2. Environment variables

  3. Config file

  4. Defaults


Structured Logging

Using logrus

Output:

Using zap (High Performance)

When to use:

  • logrus: Easy to use, good for most CLIs

  • zap: Maximum performance, structured logging at scale


Building the Binary

Basic Build

Optimized Build

Cross-Compilation

Build Script

Makefile:

Usage:


Distributing Binaries

GitHub Releases

  1. Tag a release:

  1. Build binaries:

  1. Upload to GitHub Releases manually or use goreleaser.

Using GoReleaser

.goreleaser.yaml:

Homebrew (macOS/Linux)

Create a Homebrew formula:

Users install with:


Real Example: Deployment Tool

The complete deployment tool I built:

main.go:

cmd/deploy.go:

cmd/status.go:

Usage:


Your Challenge

Build a file backup CLI tool:


Key Takeaways

  1. Single binary: No runtime dependencies

  2. Cross-compilation: Build for all platforms easily

  3. Cobra: Industry standard for CLI structure

  4. Viper: Configuration from files, env vars, flags

  5. Structured logging: logrus or zap for production

  6. Optimize builds: Use -ldflags="-s -w"

  7. GoReleaser: Automate releases

  8. Distribute: GitHub Releases, Homebrew, etc.


What I Learned

That deployment tool taught me that Go excels at CLI tools:

  • Single binary eliminated "dependencies not installed" issues

  • Fast startup (3ms) made automation scripts viable

  • Cross-compilation supported Mac, Linux, Windows teams

  • 2,000+ deployments without a single tool-related incident

Coming from Python scripts with virtual environments and Node.js tools with node_modules, Go's single binary felt magical. The deployment tool saved 30 hours per week across the team.

The time saved on deployment automation? 1,560 hours per year.


Next: Building a Real-World Microservice

In the next article, we'll build a production-ready microservice with health checks, metrics, graceful shutdown, and Kubernetes deployment. The final piece of the Go journey.

Last updated