Cobra
GoでモダンなCLIアプリケーションを作成するための強力なライブラリ。gitやkubectlのようなツールにインスパイアされています。
GitHub概要
スター41,221
ウォッチ369
フォーク2,966
作成日:2013年9月3日
言語:Go
ライセンス:Apache License 2.0
トピックス
clicli-appcobracobra-generatorcobra-librarycommandcommand-cobracommand-linecommandlinegogolanggolang-applicationgolang-libraryposixposix-compliant-flagssubcommands
スター履歴
データ取得日時: 2025/7/25 02:05
フレームワーク
Cobra
概要
CobraはGoで強力でモダンなコマンドラインインターフェース(CLI)を作成するためのライブラリです。Kubernetes、Hugo、GitHub CLIなど、多くの有名なプロジェクトで採用されています。サブコマンドベースのCLI、POSIXに準拠したフラグ、自動ヘルプ生成などの機能を提供し、Gitのような複雑なCLIアプリケーションを簡単に構築できます。最新バージョンはv1.8.1(2024年6月更新)で、Go界隈で最も信頼されているCLI構築ライブラリの地位を確立しています。
詳細
CobraはCommands
、Args
、Flags
の3つの基本概念を中心に構築されています。アプリケーションはAPPNAME COMMAND ARG --FLAG
という構造に従い、直感的で一貫性のあるインターフェースを提供します。spf13(Steve Francia)によって開発され、彼はGoogleのGo言語プロダクトリーダーとしても知られています。
Go界隈での圧倒的な地位
CobraがGo CLIライブラリで最も人気な理由:
- 業界標準の採用: Kubernetes、Hugo、GitHub CLI、Docker CLIなどの主要プロジェクトで使用
- 完全な機能セット: 単純なフラグ解析から複雑なサブコマンド構造まで対応
- 優れた開発者体験: 自動補完、ヘルプ生成、エラーメッセージが標準装備
- 成熟した設計: 長年の開発と実用によって洗練されたAPI
- フレームワークとライブラリのバランス: 制御権を保ちながら豊富な機能を提供
主な特徴
- サブコマンドベースのCLI:
app server
やapp fetch
のような階層的なコマンド構造 - POSIX準拠のフラグ: 短縮形と長形式の両方をサポート(
-n
と--name
) - ネストされたサブコマンド: より組織的で直感的なCLI構造の作成
- 自動ヘルプ生成: すべてのコマンドとフラグに対して自動的にヘルプを生成
- インテリジェントな提案: タイポに対して正しいコマンドを提案(Levenshtein距離を使用)
- シェル自動補完: Bash、Zsh、Fish、PowerShellの補完スクリプトを自動生成
- コマンドエイリアス: 既存のワークフローを壊さずにコマンド名を変更可能
- フラググループ: フラグ間の関係を定義(必須共存、相互排他など)
高度な機能
- カスタム補完:
ValidArgsFunction
とRegisterFlagCompletionFunc
による動的補完 - アクティブヘルプ: シェル補完時にユーザーをガイドするヒントメッセージ
- ライフサイクルフック:
PreRun
、PostRun
、PersistentPreRun
などのフック関数 - Viperとの統合: 設定管理ライブラリViperとの簡単な統合(現在は独立)
最新の変更(2024-2025)
- 依存関係の軽量化: コアライブラリからViperと間接依存関係を除去
- cobra-cliの分離: コード生成ツールを独立リポジトリ(spf13/cobra-cli)に移行
- リリース戦略の変更: 季節リリースから汎用ポイントリリースへ移行
- アクティブヘルプの改善: Tab補完時のインライン警告とヒント機能の強化
- 継続的な補完機能強化: シェル補完機能の継続的な改善
メリット・デメリット
メリット
- 大規模で複雑なCLIアプリケーションに最適
- 豊富な機能とカスタマイズオプション
- 多くの有名プロジェクトでの採用実績(Kubernetes、Hugo、GitHub CLI等)
- 優れたドキュメントとコミュニティサポート
- 自動補完とヘルプ生成による優れたUX
- プラグイン可能なアーキテクチャ
- cobra-cliによる自動コード生成とスキャフォールディング
- 成熟したエコシステムとViperとの統合
デメリット
- 小規模なプロジェクトには過剰な場合がある(urfave/cliなどの軽量な代替がある)
- 初期設定がやや複雑(特に初心者には学習コストが高い)
- pflagライブラリへの依存(標準のflagパッケージとは非互換)
- 標準フラグライブラリとは異なるAPI
- 多機能ゆえの複雑性(全機能を理解するのは困難)
主要リンク
書き方の例
基本的な構造
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func main() {
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "MyAppは素晴らしいCLIアプリケーションです",
Long: `MyAppは素晴らしいCLIアプリケーションで、
様々な便利な機能を提供します。`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello from MyApp!")
},
}
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
サブコマンドとフラグの例
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var (
verbose bool
source string
)
func main() {
var rootCmd = &cobra.Command{
Use: "app",
Short: "アプリケーションの簡単な説明",
}
// グローバルフラグ
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "詳細な出力")
// serveサブコマンド
var port int
var serveCmd = &cobra.Command{
Use: "serve",
Short: "サーバーを起動します",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("サーバーをポート%dで起動しています...\n", port)
if verbose {
fmt.Println("詳細モードが有効です")
}
},
}
serveCmd.Flags().IntVarP(&port, "port", "p", 8080, "サーバーポート")
// configサブコマンド
var configCmd = &cobra.Command{
Use: "config",
Short: "設定を管理します",
}
var configSetCmd = &cobra.Command{
Use: "set KEY VALUE",
Short: "設定値を設定します",
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("設定 %s = %s\n", args[0], args[1])
},
}
// コマンドの階層を構築
rootCmd.AddCommand(serveCmd, configCmd)
configCmd.AddCommand(configSetCmd)
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
動的補完の例
package main
import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
func main() {
var rootCmd = &cobra.Command{
Use: "kubectl",
Short: "Kubernetesクラスタマネージャー",
}
var getCmd = &cobra.Command{
Use: "get TYPE [NAME]",
Short: "リソースを表示します",
ValidArgs: []string{"pod", "service", "deployment", "node"},
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("%sを取得しています...\n", args[0])
},
}
// 動的補完関数
getCmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) == 0 {
// リソースタイプの補完
return []cobra.Completion{
{CompletionText: "pod", Description: "Podリソース"},
{CompletionText: "service", Description: "Serviceリソース"},
{CompletionText: "deployment", Description: "Deploymentリソース"},
{CompletionText: "node", Description: "Nodeリソース"},
}, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 1 {
// リソース名の補完(実際のクラスタから取得することを想定)
return getResourceNames(args[0], toComplete), cobra.ShellCompDirectiveNoFileComp
}
return nil, cobra.ShellCompDirectiveNoFileComp
}
// フラグの動的補完
var outputFormat string
getCmd.Flags().StringVarP(&outputFormat, "output", "o", "json", "出力形式")
getCmd.RegisterFlagCompletionFunc("output", func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
return []cobra.Completion{
{CompletionText: "json", Description: "JSON形式"},
{CompletionText: "yaml", Description: "YAML形式"},
{CompletionText: "table", Description: "テーブル形式"},
}, cobra.ShellCompDirectiveNoFileComp
})
rootCmd.AddCommand(getCmd)
rootCmd.Execute()
}
func getResourceNames(resourceType, prefix string) []cobra.Completion {
// 実際のアプリケーションではAPIを呼び出してリソース名を取得
mockResources := map[string][]string{
"pod": {"nginx-pod", "mysql-pod", "redis-pod"},
"service": {"nginx-svc", "mysql-svc", "redis-svc"},
}
var completions []cobra.Completion
if resources, ok := mockResources[resourceType]; ok {
for _, r := range resources {
if strings.HasPrefix(r, prefix) {
completions = append(completions, cobra.Completion{CompletionText: r})
}
}
}
return completions
}
フラググループの例
package main
import (
"fmt"
"github.com/spf13/cobra"
)
func main() {
var (
username string
password string
token string
json bool
yaml bool
xml bool
)
rootCmd := &cobra.Command{
Use: "api",
Short: "APIクライアント",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("APIを呼び出しています...")
},
}
// 認証フラグ
rootCmd.Flags().StringVar(&username, "username", "", "ユーザー名")
rootCmd.Flags().StringVar(&password, "password", "", "パスワード")
rootCmd.Flags().StringVar(&token, "token", "", "認証トークン")
// 出力形式フラグ
rootCmd.Flags().BoolVar(&json, "json", false, "JSON形式で出力")
rootCmd.Flags().BoolVar(&yaml, "yaml", false, "YAML形式で出力")
rootCmd.Flags().BoolVar(&xml, "xml", false, "XML形式で出力")
// フラググループの設定
rootCmd.MarkFlagsRequiredTogether("username", "password") // usernameとpasswordは一緒に必要
rootCmd.MarkFlagsMutuallyExclusive("json", "yaml", "xml") // 出力形式は1つだけ
rootCmd.MarkFlagsOneRequired("json", "yaml", "xml") // 出力形式は必須
// 認証方法は1つだけ
rootCmd.MarkFlagsMutuallyExclusive("token", "username")
rootCmd.Execute()
}
ライフサイクルフックの例
package main
import (
"fmt"
"github.com/spf13/cobra"
)
func main() {
var rootCmd = &cobra.Command{
Use: "app",
Short: "アプリケーション",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("設定を読み込んでいます...")
},
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("初期化中...")
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("メイン処理を実行中...")
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("クリーンアップ中...")
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("ログを保存しています...")
},
}
rootCmd.Execute()
}
実用的なCLI開発ツールの例
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
configFile string
verbose bool
outputFormat string
)
func main() {
rootCmd := &cobra.Command{
Use: "devtool",
Short: "開発者向けユーティリティツール",
Long: `devtoolは、日常的な開発タスクを効率化するためのCLIツールです。
プロジェクト管理、ファイル操作、ビルド自動化などの機能を提供します。`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
initConfig()
if verbose {
fmt.Printf("設定ファイル: %s\n", viper.ConfigFileUsed())
fmt.Printf("出力形式: %s\n", outputFormat)
}
},
}
// グローバルフラグ
rootCmd.PersistentFlags().StringVar(&configFile, "config", "", "設定ファイル (デフォルト: $HOME/.devtool.yaml)")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "詳細な出力")
rootCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "table", "出力形式 (table, json, yaml)")
// サブコマンドを追加
rootCmd.AddCommand(projectCmd(), buildCmd(), deployCmd())
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "エラー: %v\n", err)
os.Exit(1)
}
}
func projectCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "project",
Short: "プロジェクト管理コマンド",
Long: "プロジェクトの初期化、テンプレート作成、設定管理を行います。",
}
initCmd := &cobra.Command{
Use: "init [NAME]",
Short: "新しいプロジェクトを初期化",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
projectName := args[0]
template, _ := cmd.Flags().GetString("template")
force, _ := cmd.Flags().GetBool("force")
if verbose {
fmt.Printf("プロジェクト初期化: %s (テンプレート: %s)\n", projectName, template)
}
if !force && directoryExists(projectName) {
fmt.Fprintf(os.Stderr, "エラー: ディレクトリ '%s' は既に存在します。--force を使用して上書きしてください。\n", projectName)
os.Exit(1)
}
createProject(projectName, template)
fmt.Printf("プロジェクト '%s' が正常に作成されました。\n", projectName)
},
}
initCmd.Flags().StringP("template", "t", "basic", "プロジェクトテンプレート (basic, web, api, cli)")
initCmd.Flags().BoolP("force", "f", false, "既存のディレクトリを上書き")
listCmd := &cobra.Command{
Use: "list",
Short: "利用可能なテンプレートを一覧表示",
Run: func(cmd *cobra.Command, args []string) {
templates := []string{"basic", "web", "api", "cli", "microservice"}
fmt.Println("利用可能なテンプレート:")
for _, template := range templates {
fmt.Printf(" - %s\n", template)
}
},
}
cmd.AddCommand(initCmd, listCmd)
return cmd
}
func buildCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "build",
Short: "プロジェクトのビルド",
Long: "プロジェクトをビルドし、成果物を生成します。",
}
var (
target string
optimize bool
parallel int
)
buildCmd := &cobra.Command{
Use: "run [TARGET...]",
Short: "指定されたターゲットをビルド",
Run: func(cmd *cobra.Command, args []string) {
targets := args
if len(targets) == 0 {
targets = []string{"default"}
}
fmt.Printf("ビルド開始 (並列度: %d, 最適化: %t)\n", parallel, optimize)
for _, target := range targets {
if verbose {
fmt.Printf(" ターゲット '%s' をビルド中...\n", target)
}
// ビルド処理のシミュレーション
time.Sleep(time.Millisecond * 500)
switch outputFormat {
case "json":
fmt.Printf(`{"target": "%s", "status": "success", "duration": "500ms"}%s`, target, "\n")
case "yaml":
fmt.Printf("target: %s\nstatus: success\nduration: 500ms\n---\n", target)
default:
fmt.Printf("✓ %s (500ms)\n", target)
}
}
fmt.Println("ビルド完了")
},
}
buildCmd.Flags().StringVarP(&target, "target", "t", "", "特定のターゲットを指定")
buildCmd.Flags().BoolVarP(&optimize, "optimize", "O", false, "最適化ビルドを有効化")
buildCmd.Flags().IntVarP(¶llel, "parallel", "j", 4, "並列ビルド数")
cleanCmd := &cobra.Command{
Use: "clean",
Short: "ビルド成果物をクリーンアップ",
Run: func(cmd *cobra.Command, args []string) {
if verbose {
fmt.Println("ビルド成果物をクリーンアップ中...")
}
// クリーンアップ処理のシミュレーション
time.Sleep(time.Millisecond * 200)
fmt.Println("クリーンアップ完了")
},
}
cmd.AddCommand(buildCmd, cleanCmd)
return cmd
}
func deployCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "deploy",
Short: "アプリケーションのデプロイ",
Long: "ビルドされたアプリケーションを指定された環境にデプロイします。",
}
var (
environment string
dryRun bool
confirm bool
)
deployCmd := &cobra.Command{
Use: "run",
Short: "デプロイを実行",
PreRun: func(cmd *cobra.Command, args []string) {
if environment == "production" && !confirm {
fmt.Print("本番環境へのデプロイを実行しますか? (y/N): ")
var response string
fmt.Scanln(&response)
if strings.ToLower(response) != "y" && strings.ToLower(response) != "yes" {
fmt.Println("デプロイをキャンセルしました")
os.Exit(0)
}
}
},
Run: func(cmd *cobra.Command, args []string) {
if dryRun {
fmt.Printf("[ドライラン] %s環境へのデプロイをシミュレーション\n", environment)
} else {
fmt.Printf("%s環境へデプロイ中...\n", environment)
}
// デプロイ処理のシミュレーション
steps := []string{"設定検証", "アップロード", "サービス再起動", "ヘルスチェック"}
for i, step := range steps {
if verbose {
fmt.Printf(" [%d/%d] %s...\n", i+1, len(steps), step)
}
time.Sleep(time.Millisecond * 300)
}
if !dryRun {
fmt.Printf("✓ %s環境へのデプロイが完了しました\n", environment)
} else {
fmt.Printf("✓ ドライランが完了しました(実際のデプロイは実行されませんでした)\n")
}
},
}
deployCmd.Flags().StringVarP(&environment, "env", "e", "staging", "デプロイ環境 (staging, production)")
deployCmd.Flags().BoolVarP(&dryRun, "dry-run", "n", false, "ドライランモード")
deployCmd.Flags().BoolVarP(&confirm, "yes", "y", false, "確認プロンプトをスキップ")
// 環境の検証
deployCmd.MarkFlagRequired("env")
deployCmd.RegisterFlagCompletionFunc("env", func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
return []cobra.Completion{
{CompletionText: "staging", Description: "ステージング環境"},
{CompletionText: "production", Description: "本番環境"},
{CompletionText: "development", Description: "開発環境"},
}, cobra.ShellCompDirectiveNoFileComp
})
statusCmd := &cobra.Command{
Use: "status",
Short: "デプロイ状況を確認",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("現在のデプロイ状況:\n")
fmt.Printf(" 環境: %s\n", environment)
fmt.Printf(" 最終デプロイ: 2024-01-15 14:30:00\n")
fmt.Printf(" バージョン: v1.2.3\n")
fmt.Printf(" ステータス: 正常\n")
},
}
statusCmd.Flags().StringVarP(&environment, "env", "e", "staging", "確認する環境")
cmd.AddCommand(deployCmd, statusCmd)
return cmd
}
func initConfig() {
if configFile != "" {
viper.SetConfigFile(configFile)
} else {
home, err := os.UserHomeDir()
cobra.CheckErr(err)
viper.AddConfigPath(home)
viper.SetConfigType("yaml")
viper.SetConfigName(".devtool")
}
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err == nil && verbose {
fmt.Printf("設定ファイルを読み込みました: %s\n", viper.ConfigFileUsed())
}
}
func directoryExists(path string) bool {
info, err := os.Stat(path)
if os.IsNotExist(err) {
return false
}
return info.IsDir()
}
func createProject(name, template string) {
// プロジェクト作成のシミュレーション
os.MkdirAll(name, 0755)
switch template {
case "web":
os.MkdirAll(filepath.Join(name, "static"), 0755)
os.MkdirAll(filepath.Join(name, "templates"), 0755)
case "api":
os.MkdirAll(filepath.Join(name, "handlers"), 0755)
os.MkdirAll(filepath.Join(name, "models"), 0755)
case "cli":
os.MkdirAll(filepath.Join(name, "cmd"), 0755)
}
// 基本ファイルの作成
os.WriteFile(filepath.Join(name, "README.md"), []byte("# "+name+"\n"), 0644)
os.WriteFile(filepath.Join(name, ".gitignore"), []byte("*.log\n*.tmp\n"), 0644)
}
cobra-cliツールの使用例
# cobra-cliのインストール
go install github.com/spf13/cobra-cli@latest
# 新しいCLIアプリケーションの初期化
cobra-cli init myapp
# サブコマンドの追加
cobra-cli add serve
cobra-cli add config
cobra-cli add create -p 'configCmd'
# 生成されたファイル構造
# myapp/
# ├── cmd/
# │ ├── root.go
# │ ├── serve.go
# │ ├── config.go
# │ └── create.go
# ├── main.go
# └── go.mod