Como criar CLIs simples com cobra para automações internas em Go
1. Introdução ao Cobra e seu papel em automações internas
Cobra é um framework CLI para Go, criado por Steve Francia (spf13) e usado por projetos como Kubernetes, Hugo e Docker. Ele fornece uma estrutura robusta para criar interfaces de linha de comando com suporte a subcomandos, flags e argumentos, sendo ideal para automações internas.
Por que usar Cobra para automações internas? A simplicidade de criação de comandos aninhados, o suporte nativo a flags persistentes e locais, e a facilidade de gerar documentação automática fazem dele uma escolha natural para scripts que precisam ser usados por equipes.
Pré-requisitos: Go instalado (versão 1.16+), familiaridade com go mod e conhecimento básico de linha de comando.
2. Configuração inicial e criação do projeto
Primeiro, instale a ferramenta cobra-cli:
go install github.com/spf13/cobra-cli@latest
Crie um novo módulo Go e inicialize o projeto:
mkdir automacao-interna
cd automacao-interna
go mod init github.com/seu-usuario/automacao-interna
cobra-cli init
A estrutura gerada será:
automacao-interna/
├── cmd/
│ └── root.go
├── main.go
├── go.mod
└── go.sum
O arquivo main.go já vem configurado para executar o comando raiz:
package main
import "github.com/seu-usuario/automacao-interna/cmd"
func main() {
cmd.Execute()
}
3. Estrutura de comandos e subcomandos
Vamos criar um comando backup com subcomando database. Primeiro, crie o comando raiz customizado em cmd/root.go:
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "automacao",
Short: "Ferramenta de automação interna",
Long: `CLI para automações internas da equipe de infraestrutura`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Use 'automacao --help' para ver os comandos disponíveis")
},
}
func Execute() {
cobra.CheckErr(rootCmd.Execute())
}
Agora, crie cmd/backup.go:
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var backupCmd = &cobra.Command{
Use: "backup",
Short: "Gerencia backups do sistema",
Long: `Comandos para criar e gerenciar backups de diferentes recursos`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Use 'automacao backup --help' para ver os subcomandos")
},
}
func init() {
rootCmd.AddCommand(backupCmd)
}
E cmd/backup_database.go:
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var backupDatabaseCmd = &cobra.Command{
Use: "database",
Short: "Faz backup do banco de dados",
Long: `Executa dump do banco de dados PostgreSQL e compacta o resultado`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Executando backup do banco de dados...")
},
}
func init() {
backupCmd.AddCommand(backupDatabaseCmd)
}
Para testar:
go run main.go backup database
4. Trabalhando com flags e argumentos
Vamos enriquecer o comando backup database com flags e argumentos. Modifique cmd/backup_database.go:
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var (
outputDir string
compress bool
)
var backupDatabaseCmd = &cobra.Command{
Use: "database [filepath]",
Short: "Faz backup do banco de dados",
Long: `Executa dump do banco de dados PostgreSQL e salva no diretório especificado`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
filepath := args[0]
fmt.Printf("Backup do banco de dados será salvo em: %s/%s\n", outputDir, filepath)
if compress {
fmt.Println("Compactação habilitada")
}
},
}
func init() {
backupDatabaseCmd.Flags().StringVarP(&outputDir, "output", "o", "./backups", "Diretório de saída")
backupDatabaseCmd.Flags().BoolVarP(&compress, "compress", "c", false, "Compactar o backup")
backupCmd.AddCommand(backupDatabaseCmd)
}
Flags persistentes (disponíveis em todos os subcomandos) vs flags locais (apenas no comando atual):
// Flag persistente no comando backup
backupCmd.PersistentFlags().String("log-level", "info", "Nível de log")
// Flag local apenas no comando database
backupDatabaseCmd.Flags().Bool("dry-run", false, "Simular execução sem alterar dados")
5. Implementando lógica de automação prática
Vamos implementar um backup real usando os/exec. Modifique o Run do comando database:
package cmd
import (
"fmt"
"os"
"os/exec"
"time"
"github.com/spf13/cobra"
)
var (
dbName string
dbUser string
outputDir string
compress bool
)
var backupDatabaseCmd = &cobra.Command{
Use: "database",
Short: "Faz backup do banco de dados",
Long: `Executa dump do banco PostgreSQL usando pg_dump`,
Run: func(cmd *cobra.Command, args []string) {
timestamp := time.Now().Format("20060102_150405")
filename := fmt.Sprintf("%s/backup_%s.sql", outputDir, timestamp)
fmt.Printf("Iniciando backup do banco %s...\n", dbName)
dumpCmd := exec.Command("pg_dump", "-U", dbUser, "-h", "localhost", dbName)
output, err := os.Create(filename)
if err != nil {
fmt.Printf("Erro ao criar arquivo: %v\n", err)
return
}
defer output.Close()
dumpCmd.Stdout = output
dumpCmd.Stderr = os.Stderr
if err := dumpCmd.Run(); err != nil {
fmt.Printf("Erro no backup: %v\n", err)
return
}
fmt.Printf("Backup concluído: %s\n", filename)
if compress {
gzipCmd := exec.Command("gzip", filename)
if err := gzipCmd.Run(); err != nil {
fmt.Printf("Erro na compressão: %v\n", err)
return
}
fmt.Printf("Arquivo compactado: %s.gz\n", filename)
}
},
}
func init() {
backupDatabaseCmd.Flags().StringVarP(&dbName, "database", "d", "", "Nome do banco de dados (obrigatório)")
backupDatabaseCmd.Flags().StringVarP(&dbUser, "user", "u", "postgres", "Usuário do banco")
backupDatabaseCmd.Flags().StringVarP(&outputDir, "output", "o", "./backups", "Diretório de saída")
backupDatabaseCmd.Flags().BoolVarP(&compress, "compress", "c", false, "Compactar com gzip")
backupDatabaseCmd.MarkFlagRequired("database")
backupCmd.AddCommand(backupDatabaseCmd)
}
6. Boas práticas para automações internas
Tratamento de erros com mensagens amigáveis:
if err := dumpCmd.Run(); err != nil {
cobra.CheckErr(fmt.Errorf("falha no backup: %w", err))
}
Logs estruturados com fmt para rastreamento:
fmt.Fprintf(os.Stderr, "[INFO] Iniciando backup às %s\n", time.Now().Format(time.RFC3339))
Testes unitários com testing:
package cmd
import (
"testing"
"github.com/spf13/cobra"
)
func TestBackupDatabaseCommand(t *testing.T) {
cmd := &cobra.Command{}
cmd.SetArgs([]string{"database", "--database=testdb"})
err := cmd.Execute()
if err != nil {
t.Errorf("Comando falhou: %v", err)
}
}
7. Distribuição e uso em equipe
Compile para binário único:
go build -o automacao main.go
Adicione ao PATH:
sudo mv automacao /usr/local/bin/
Ou crie um alias no shell:
alias auto='~/projetos/automacao-interna/automacao'
Documente os comandos gerando help automático:
automacao --help
automacao backup --help
automacao backup database --help
8. Exemplo completo: CLI de automação de deploys
Crie cmd/deploy.go:
package cmd
import (
"fmt"
"os"
"os/exec"
"time"
"github.com/spf13/cobra"
)
var (
branch string
dryRun bool
timeout int
)
var deployCmd = &cobra.Command{
Use: "deploy [environment]",
Short: "Faz deploy da aplicação",
Long: `Automação de deploy para ambientes staging e production`,
Args: cobra.ExactValidArgs(1),
ValidArgs: []string{"staging", "production"},
Run: func(cmd *cobra.Command, args []string) {
env := args[0]
fmt.Printf("Iniciando deploy para %s\n", env)
fmt.Printf("Branch: %s\n", branch)
if dryRun {
fmt.Println("[DRY-RUN] Simulação concluída")
return
}
// Simula execução de deploy
timeoutDur := time.Duration(timeout) * time.Second
deployCmd := exec.Command("sh", "-c", fmt.Sprintf(
"echo 'Deployando %s em %s com timeout de %ds'",
branch, env, timeout,
))
deployCmd.Stdout = os.Stdout
deployCmd.Stderr = os.Stderr
if err := deployCmd.Run(); err != nil {
fmt.Printf("Erro no deploy: %v\n", err)
os.Exit(1)
}
fmt.Printf("Deploy para %s concluído com sucesso!\n", env)
_ = timeoutDur // Usado em implementação real
},
}
var deployStagingCmd = &cobra.Command{
Use: "staging",
Short: "Faz deploy no ambiente de staging",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Executando deploy no staging...")
// Reaproveita lógica do comando pai
},
}
var deployProductionCmd = &cobra.Command{
Use: "production",
Short: "Faz deploy no ambiente de produção",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Executando deploy na produção...")
// Implementar validações extras
},
}
func init() {
deployCmd.Flags().StringVarP(&branch, "branch", "b", "main", "Branch para deploy")
deployCmd.Flags().BoolVarP(&dryRun, "dry-run", "n", false, "Simular sem executar")
deployCmd.Flags().IntVarP(&timeout, "timeout", "t", 300, "Timeout em segundos")
deployCmd.AddCommand(deployStagingCmd)
deployCmd.AddCommand(deployProductionCmd)
rootCmd.AddCommand(deployCmd)
}
Uso completo:
# Deploy normal
automacao deploy staging --branch feature-x
# Simulação
automacao deploy production --branch main --dry-run
# Com timeout customizado
automacao deploy staging --branch hotfix --timeout 600
# Help completo
automacao deploy --help
O Cobra oferece uma base sólida para construir CLIs poderosos e bem estruturados. Com esses padrões, você pode criar automações internas que são fáceis de manter, testar e compartilhar com sua equipe.
Referências
- Documentação oficial do Cobra — Repositório oficial com exemplos, guias e documentação completa da biblioteca
- Cobra CLI Generator — Ferramenta oficial para scaffolding de projetos Cobra com geração automática de comandos
- Tutorial: Criando CLIs em Go com Cobra — Guia passo a passo da DigitalOcean cobrindo desde a instalação até exemplos práticos
- Go by Example: Command-Line Flags — Exemplos concisos de manipulação de flags em Go, complementar ao uso com Cobra
- Effective Go: Command Design — Documentação oficial sobre boas práticas em Go, incluindo design de comandos e interfaces
- Building CLI Applications in Go — Livro da O'Reilly com capítulos dedicados ao Cobra e padrões de automação