DevOps Control Plane CLI (devctl)
A complete CLI tool built with Cobra and Viper that demonstrates subcommands, flag handling, config file loading, environment variable binding, and multiple output formats. It simulates a devctl control plane with deploy and status subcommands.
package main
import (
"encoding/json"
"fmt"
"os"
"text/tabwriter"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
type ServiceStatus struct {
Name string `json:"name"`
Env string `json:"env"`
Status string `json:"status"`
Replicas int `json:"replicas"`
}
var rootCmd = &cobra.Command{
Use: "devctl",
Short: "DevOps control plane CLI",
}
var deployCmd = &cobra.Command{
Use: "deploy [service]",
Short: "Deploy a service",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
env := viper.GetString("env")
image, _ := cmd.Flags().GetString("image")
fmt.Fprintf(os.Stderr, "✓ Deployed %s to %s (image: %s)\n", args[0], env, image)
return nil
},
}
var statusCmd = &cobra.Command{
Use: "status",
Short: "Show service status",
RunE: func(cmd *cobra.Command, args []string) error {
services := []ServiceStatus{
{Name: "api", Env: "prod", Status: "running", Replicas: 3},
{Name: "worker", Env: "prod", Status: "running", Replicas: 2},
}
return printServices(viper.GetString("output"), services)
},
}
func printServices(format string, services []ServiceStatus) error {
switch format {
case "json":
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(services)
default:
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "NAME\tENV\tSTATUS\tREPLICAS")
for _, s := range services {
fmt.Fprintf(w, "%s\t%s\t%s\t%d\n", s.Name, s.Env, s.Status, s.Replicas)
}
return w.Flush()
}
}
func init() {
cobra.OnInitialize(func() {
viper.SetConfigName(".devctl")
viper.SetConfigType("yaml")
viper.AddConfigPath("$HOME")
viper.AddConfigPath(".")
viper.SetEnvPrefix("DEVCTL")
viper.AutomaticEnv()
viper.ReadInConfig()
})
rootCmd.PersistentFlags().StringP("output", "o", "table", "Output format: table|json|yaml")
rootCmd.PersistentFlags().StringP("env", "e", "staging", "Target environment")
viper.BindPFlag("output", rootCmd.PersistentFlags().Lookup("output"))
viper.BindPFlag("env", rootCmd.PersistentFlags().Lookup("env"))
deployCmd.Flags().String("image", "", "Container image to deploy")
deployCmd.MarkFlagRequired("image")
rootCmd.AddCommand(deployCmd)
rootCmd.AddCommand(statusCmd)
}
func main() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}