Membangun Aplikasi CLI dalam Go dengan Cobra & Viper
Pengembangan CLI dalam Go dengan kerangka Cobra dan Viper
Antarmuka baris perintah (CLI) adalah alat penting bagi pengembang, administrator sistem, dan profesional DevOps. Dua perpustakaan Go telah menjadi standar de facto untuk pengembangan CLI dalam Go: Cobra untuk struktur perintah dan Viper untuk manajemen konfigurasi.
Go telah muncul sebagai bahasa yang sangat baik untuk membangun alat CLI karena kinerjanya yang baik, pengiriman sederhana, dan dukungan lintas platform.

Mengapa Memilih Go untuk Aplikasi CLI
Go menawarkan keunggulan yang menarik untuk pengembangan CLI:
- Distribusi Binari Tunggal: Tidak diperlukan ketergantungan runtime atau manajer paket
- Eksekusi Cepat: Kompilasi native memberikan kinerja yang sangat baik
- Dukungan Lintas Platform: Kompilasi mudah untuk Linux, macOS, Windows, dan lainnya
- Perpustakaan Standar yang Kuat: Alat bantu kaya untuk I/O file, jaringan, dan pemrosesan teks
- Konkurensi: Goroutine bawaan untuk operasi paralel
- Tipe Statis: Menangkap kesalahan pada saat kompilasi
Alat CLI populer yang dibangun dengan Go termasuk Docker, Kubernetes (kubectl), Hugo, Terraform, dan GitHub CLI. Jika Anda baru dengan Go atau membutuhkan referensi cepat, lihat Kartu Referensi Go kami untuk sintaks dan pola Go yang penting.
Pengantar tentang Cobra
Cobra adalah perpustakaan yang menyediakan antarmuka sederhana untuk membuat aplikasi CLI modern yang kuat. Dibuat oleh Steve Francia (spf13), penulis yang sama di balik Hugo dan Viper, Cobra digunakan dalam banyak proyek Go paling populer.
Fitur Utama Cobra
Struktur Perintah: Cobra menerapkan pola perintah, memungkinkan Anda membuat aplikasi dengan perintah dan subperintah (seperti git commit atau docker run).
Pengelolaan Flag: Flag lokal dan persisten dengan parsing otomatis dan konversi tipe.
Bantuan Otomatis: Menghasilkan teks bantuan dan informasi penggunaan secara otomatis.
Saran Cerdas: Menyediakan saran saat pengguna membuat ejaan yang salah (“Apakah Anda maksud ‘status’?”).
Kompleksi Shell: Menghasilkan skrip kompletasi untuk bash, zsh, fish, dan PowerShell.
Output Fleksibel: Bekerja dengan lancar dengan formatter kustom dan gaya output.
Pengantar tentang Viper
Viper adalah solusi konfigurasi lengkap untuk aplikasi Go, dirancang untuk bekerja dengan lancar dengan Cobra. Ia menangani konfigurasi dari berbagai sumber dengan urutan prioritas yang jelas.
Fitur Utama Viper
Sumber Konfigurasi Beragam:
- File konfigurasi (JSON, YAML, TOML, HCL, INI, envfile, Java properties)
- Variabel lingkungan
- Flag baris perintah
- Sistem konfigurasi jarak jauh (etcd, Consul)
- Nilai default
Urutan Prioritas Konfigurasi:
- Panggilan eksplisit ke Set
- Flag baris perintah
- Variabel lingkungan
- File konfigurasi
- Penyimpanan key/value
- Nilai default
Pemantauan Langsung: Memantau file konfigurasi dan memuat ulang secara otomatis saat terjadi perubahan.
Konversi Tipe: Konversi otomatis ke berbagai tipe Go (string, int, bool, durasi, dll.).
Memulai: Instalasi
Pertama, inisialisasi modul Go baru dan instal kedua perpustakaan:
go mod init myapp
go get -u github.com/spf13/cobra@latest
go get -u github.com/spf13/viper@latest
Secara opsional, instal generator CLI Cobra untuk scaffolding:
go install github.com/spf13/cobra-cli@latest
Membangun Aplikasi CLI Pertama
Mari kita bangun contoh praktis: alat manajemen tugas CLI dengan dukungan konfigurasi.
Struktur Proyek
mytasks/
├── cmd/
│ ├── root.go
│ ├── add.go
│ ├── list.go
│ └── complete.go
├── config/
│ └── config.go
├── main.go
└── config.yaml
Titik Masuk Utama
// main.go
package main
import "mytasks/cmd"
func main() {
cmd.Execute()
}
Perintah Utama dengan Integrasi Viper
// cmd/root.go
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
var rootCmd = &cobra.Command{
Use: "mytasks",
Short: "Sebuah alat manajemen tugas sederhana",
Long: `MyTasks adalah alat manajemen tugas CLI yang membantu Anda mengatur
tugas harian Anda dengan mudah. Dibangun dengan Cobra dan Viper.`,
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "",
"file konfigurasi (default adalah $HOME/.mytasks.yaml)")
rootCmd.PersistentFlags().String("db", "",
"lokasi file database")
viper.BindPFlag("database", rootCmd.PersistentFlags().Lookup("db"))
}
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, err := os.UserHomeDir()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
viper.AddConfigPath(home)
viper.AddConfigPath(".")
viper.SetConfigType("yaml")
viper.SetConfigName(".mytasks")
}
viper.SetEnvPrefix("MYTASKS")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Menggunakan file konfigurasi:", viper.ConfigFileUsed())
}
}
Menambahkan Subperintah
// cmd/add.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var priority string
var addCmd = &cobra.Command{
Use: "add [deskripsi tugas]",
Short: "Tambahkan tugas baru",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
task := args[0]
db := viper.GetString("database")
fmt.Printf("Menambahkan tugas: %s\n", task)
fmt.Printf("Prioritas: %s\n", priority)
fmt.Printf("Database: %s\n", db)
// Di sini Anda akan menerapkan penyimpanan tugas sebenarnya
},
}
func init() {
rootCmd.AddCommand(addCmd)
addCmd.Flags().StringVarP(&priority, "priority", "p", "medium",
"prioritas tugas (rendah, sedang, tinggi)")
}
Perintah Daftar
// cmd/list.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var showCompleted bool
var listCmd = &cobra.Command{
Use: "list",
Short: "Daftar semua tugas",
Run: func(cmd *cobra.Command, args []string) {
db := viper.GetString("database")
fmt.Printf("Mendaftar tugas dari: %s\n", db)
fmt.Printf("Tampilkan tugas selesai: %v\n", showCompleted)
// Di sini Anda akan menerapkan daftar tugas sebenarnya
},
}
func init() {
rootCmd.AddCommand(listCmd)
listCmd.Flags().BoolVarP(&showCompleted, "completed", "c", false,
"tampilkan tugas selesai")
}
Menerapkan Pemeliharaan Data
Untuk aplikasi manajemen tugas CLI produksi, Anda perlu menerapkan penyimpanan data sebenarnya. Meskipun kami menggunakan konfigurasi jalur database sederhana di sini, Anda memiliki beberapa opsi untuk menyimpan data:
- SQLite: Database ringan tanpa server yang sempurna untuk alat CLI
- PostgreSQL/MySQL: Database lengkap untuk aplikasi yang lebih kompleks
- File JSON/YAML: Penyimpanan berbasis file sederhana untuk kebutuhan ringan
- Database Tersemat: BoltDB, BadgerDB untuk penyimpanan key-value
Jika Anda bekerja dengan database relasional seperti PostgreSQL, Anda ingin menggunakan ORM atau pembangun query. Untuk perbandingan menyeluruh dari perpustakaan database Go, lihat panduan kami tentang Membandingkan ORM Go untuk PostgreSQL: GORM vs Ent vs Bun vs sqlc.
Konfigurasi Lanjutan dengan Viper
Contoh File Konfigurasi
# .mytasks.yaml
database: ~/.mytasks.db
format: table
colors: true
notifications:
enabled: true
sound: true
priorities:
high: red
medium: yellow
low: green
Membaca Konfigurasi Bersarang
func getNotificationSettings() {
enabled := viper.GetBool("notifications.enabled")
sound := viper.GetBool("notifications.sound")
fmt.Printf("Notifikasi aktif: %v\n", enabled)
fmt.Printf("Suara aktif: %v\n", sound)
}
func getPriorityColors() map[string]string {
return viper.GetStringMapString("priorities")
}
Variabel Lingkungan
Viper secara otomatis membaca variabel lingkungan dengan prefix yang dikonfigurasi:
export MYTASKS_DATABASE=/tmp/tasks.db
export MYTASKS_NOTIFICATIONS_ENABLED=false
mytasks list
Muat Ulang Konfigurasi Langsung
func watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("File konfigurasi berubah:", e.Name)
// Muat ulang komponen yang bergantung pada konfigurasi
})
}
Praktik Terbaik
1. Gunakan Generator Cobra untuk Konsistensi
cobra-cli init
cobra-cli add serve
cobra-cli add create
2. Organisasi Perintah dalam File Terpisah
Jaga setiap perintah dalam file sendiri di bawah direktori cmd/ untuk keterpeliharaan.
3. Manfaatkan Flag Persisten
Gunakan flag persisten untuk opsi yang berlaku untuk semua subperintah:
rootCmd.PersistentFlags().StringP("output", "o", "text",
"format output (text, json, yaml)")
4. Implementasikan Penangan Kesalahan yang Tepat
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "Aplikasi saya",
RunE: func(cmd *cobra.Command, args []string) error {
if err := doSomething(); err != nil {
return fmt.Errorf("gagal melakukan sesuatu: %w", err)
}
return nil
},
}
5. Sediakan Teks Bantuan yang Berguna
var cmd = &cobra.Command{
Use: "deploy [lingkungan]",
Short: "Deploy aplikasi ke lingkungan yang ditentukan",
Long: `Deploy membangun dan mendeploy aplikasi ke
lingkungan yang ditentukan. Lingkungan yang didukung adalah:
- pengembangan (dev)
- staging
- produksi (prod)`,
Example: ` myapp deploy staging
myapp deploy production --version=1.2.3`,
}
6. Tetapkan Default yang Masuk Akal
func init() {
viper.SetDefault("port", 8080)
viper.SetDefault("timeout", "30s")
viper.SetDefault("retries", 3)
}
7. Validasi Konfigurasi
func validateConfig() error {
port := viper.GetInt("port")
if port < 1024 || port > 65535 {
return fmt.Errorf("port tidak valid: %d", port)
}
return nil
}
Pengujian Aplikasi CLI
Pengujian Perintah
// cmd/root_test.go
package cmd
import (
"bytes"
"testing"
)
func TestRootCommand(t *testing.T) {
cmd := rootCmd
b := bytes.NewBufferString("")
cmd.SetOut(b)
cmd.SetArgs([]string{"--help"})
if err := cmd.Execute(); err != nil {
t.Fatalf("eksekusi perintah gagal: %v", err)
}
}
Pengujian dengan Konfigurasi Berbeda
func TestWithConfig(t *testing.T) {
viper.Set("database", "/tmp/test.db")
viper.Set("debug", true)
// Jalankan pengujian Anda
viper.Reset() // Bersihkan
}
Menghasilkan Skrip Kompleksi Shell
Cobra dapat menghasilkan skrip kompleksi untuk berbagai shell:
// cmd/completion.go
package cmd
import (
"os"
"github.com/spf13/cobra"
)
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Hasilkan skrip kompleksi",
Long: `Untuk memuat kompleksi:
Bash:
$ source <(mytasks completion bash)
Zsh:
$ source <(mytasks completion zsh)
Fish:
$ mytasks completion fish | source
PowerShell:
PS> mytasks completion powershell | Out-String | Invoke-Expression
`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
}
},
}
func init() {
rootCmd.AddCommand(completionCmd)
}
Membangun dan Distribusi
Membangun untuk Platform Beragam
# Linux
GOOS=linux GOARCH=amd64 go build -o mytasks-linux-amd64
# macOS
GOOS=darwin GOARCH=amd64 go build -o mytasks-darwin-amd64
GOOS=darwin GOARCH=arm64 go build -o mytasks-darwin-arm64
# Windows
GOOS=windows GOARCH=amd64 go build -o mytasks-windows-amd64.exe
Mengurangi Ukuran Binari
go build -ldflags="-s -w" -o mytasks
Menambahkan Informasi Versi
// cmd/version.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var (
Version = "dev"
Commit = "none"
BuildTime = "unknown"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "Cetak informasi versi",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Versi: %s\n", Version)
fmt.Printf("Commit: %s\n", Commit)
fmt.Printf("Dibuat: %s\n", BuildTime)
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}
Bangun dengan informasi versi:
go build -ldflags="-X 'mytasks/cmd.Version=1.0.0' \
-X 'mytasks/cmd.Commit=$(git rev-parse HEAD)' \
-X 'mytasks/cmd.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)'" \
-o mytasks
Contoh Nyata
Alat open-source populer yang dibangun dengan Cobra dan Viper:
- kubectl: Alat CLI Kubernetes
- Hugo: Generator situs statis
- GitHub CLI (gh): CLI resmi GitHub
- Docker CLI: Manajemen kontainer
- Helm: Manajer paket Kubernetes
- Skaffold: Alat alur kerja pengembangan Kubernetes
- Cobra CLI: Self-hosting - Cobra menggunakan dirinya sendiri!
Selain alat DevOps tradisional, kemampuan CLI Go juga mencakup aplikasi AI dan pembelajaran mesin. Jika Anda tertarik membangun alat CLI yang berinteraksi dengan model bahasa besar, lihat panduan kami tentang Membatasi LLM dengan Output Terstruktur menggunakan Ollama dan Go, yang menunjukkan bagaimana membangun aplikasi Go yang bekerja dengan model AI.
Sumber Daya Berguna
- Repository GitHub Cobra
- Repository GitHub Viper
- Panduan Pengguna Cobra
- Dokumentasi Viper
- Praktik Terbaik Aplikasi CLI Go
Kesimpulan
Cobra dan Viper bersama-sama menyediakan fondasi yang kuat untuk membangun aplikasi CLI profesional dalam Go. Cobra menangani struktur perintah, parsing flag, dan pembuatan bantuan, sementara Viper mengelola konfigurasi dari berbagai sumber dengan prioritas cerdas.
Kombinasi ini memungkinkan Anda membuat alat CLI yang:
- Mudah digunakan dengan perintah intuitif dan bantuan otomatis
- Fleksibel dengan berbagai sumber konfigurasi
- Profesional dengan kompletasi shell dan penangan kesalahan yang tepat
- Dapat dipelihara dengan organisasi kode yang bersih
- Portabel lintas platform
Baik Anda sedang membangun alat pengembang, utilitas sistem, atau otomasi DevOps, Cobra dan Viper menyediakan fondasi padat yang Anda butuhkan untuk membuat aplikasi CLI yang akan disukai pengguna.