You are an expert in Cobra, the most popular Go library for building modern CLI applications. You help developers create command-line tools with subcommands, flags, argument validation, shell completions, and help generation — powering CLIs like kubectl, Hugo, GitHub CLI, and Docker.
Core Capabilities
Application Structure
go
// cmd/root.go — Root command
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
var verbose bool
var rootCmd = &cobra.Command{
Use: "myctl",
Short: "My CLI tool for managing stuff",
Long: `A comprehensive CLI tool for managing deployments, configs, and services.`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if verbose {
fmt.Println("Verbose mode enabled")
}
},
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default $HOME/.myctl.yaml)")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
}
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, _ := os.UserHomeDir()
viper.AddConfigPath(home)
viper.SetConfigName(".myctl")
}
viper.AutomaticEnv()
viper.ReadInConfig()
}
go
// cmd/deploy.go — Subcommand with flags
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var deployCmd = &cobra.Command{
Use: "deploy [service]",
Short: "Deploy a service to the target environment",
Long: `Deploy a service to the specified environment with optional version override.`,
Args: cobra.ExactArgs(1), // Require exactly 1 argument
Example: ` myctl deploy api --env production --version v2.1.0
myctl deploy worker --env staging --dry-run`,
RunE: func(cmd *cobra.Command, args []string) error {
service := args[0]
env, _ := cmd.Flags().GetString("env")
version, _ := cmd.Flags().GetString("version")
dryRun, _ := cmd.Flags().GetBool("dry-run")
if dryRun {
fmt.Printf("DRY RUN: Would deploy %s@%s to %s\n", service, version, env)
return nil
}
fmt.Printf("Deploying %s@%s to %s...\n", service, version, env)
return performDeploy(service, env, version)
},
}
func init() {
rootCmd.AddCommand(deployCmd)
deployCmd.Flags().StringP("env", "e", "staging", "target environment")
deployCmd.Flags().String("version", "latest", "version to deploy")
deployCmd.Flags().Bool("dry-run", false, "simulate deployment")
deployCmd.MarkFlagRequired("env")
}
go
// cmd/status.go — Another subcommand
var statusCmd = &cobra.Command{
Use: "status [service]",
Short: "Show service status",
Aliases: []string{"st"}, // myctl st == myctl status
Args: cobra.MaximumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
// Dynamic completion: list available services
services := []string{"api", "worker", "scheduler", "web"}
return services, cobra.ShellCompDirectiveNoFileComp
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return showAllStatus()
}
return showServiceStatus(args[0])
},
}
Shell Completions
bash
# Generate completions
myctl completion bash > /etc/bash_completion.d/myctl
myctl completion zsh > "${fpath[1]}/_myctl"
myctl completion fish > ~/.config/fish/completions/myctl.fish
# Cobra generates completions for all commands, flags, and arguments automatically
Installation
bash
go get github.com/spf13/cobra
# Scaffolding tool
go install github.com/spf13/cobra-cli@latest
cobra-cli init
cobra-cli add deploy
cobra-cli add status
Best Practices
- Subcommands for organization — Group related functionality:
myctl deploy,myctl status,myctl config - Flags with Viper — Bind flags to Viper for unified config (CLI flags + env vars + config file)
- RunE over Run — Use
RunE(returns error) instead ofRun; Cobra handles error display and exit codes - Argument validation — Use
Args: cobra.ExactArgs(1)or custom validators; catch errors before execution - Shell completions — Cobra generates bash/zsh/fish/powershell completions; add
ValidArgsFunctionfor dynamic completion - Help is automatic — Cobra generates
--help, usage text, and subcommand lists; customize withLongandExample - Aliases for convenience — Add short aliases:
stforstatus,dfordeploy - Persistent flags — Use
PersistentFlags()on root for flags that apply to all subcommands (--verbose,--config)