0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GoでCobraを使ってCLIを作成してみた

Posted at

背景・目的

最近、CobraというCLIアプリケーションを作成するライブラリを知りました。
今回、整理し簡単に試してみたいと思います。

まとめ

下記に特徴を整理します。

特徴 説明
Cobraとは 強力な最新の CLI アプリケーションを作成するためのライブラリ
Kubernetes、Hugo、GitHub CLI など、多くの Go プロジェクトで使用されている
概要 git や go ツールに似た強力な最新の CLI インターフェースを作成するためのシンプルなインターフェースを提供するライブラリ
提供する機能 ・簡単なサブコマンドベースの CLI: アプリサーバー、アプリフェッチなど
・完全にPOSIX準拠のフラグ(短いバージョンと長いバージョンを含む)
・ネストされたサブコマンド
・グローバル、ローカル、カスケードフラグ
・インテリジェントな提案
・コマンドとフラグの自動ヘルプ生成
・サブコマンドのヘルプのグループ化
・-h、--help などのヘルプ フラグの自動認識
・アプリケーション用のシェルのオートコンプリートを自動的に生成
・コマンドエイリアスを使用すると、変更しても壊れることはない
・独自のヘルプや使用方法などを定義できる柔軟性
・Twelve-Factor AppのViperとの統合
コンセプト コマンド、引数、フラグの構造に基づいて構築されている
ライセンス Apache 2.0

概要

下記を基に整理します。

Cobra is a library for creating powerful modern CLI applications.
Cobra is used in many Go projects such as Kubernetes, Hugo, and GitHub CLI to name a few. This list contains a more extensive list of projects using Cobra.

  • Cobra は、強力な最新の CLI アプリケーションを作成するためのライブラリ
  • Cobra は、Kubernetes、Hugo、GitHub CLI など、多くの Go プロジェクトで使用されている
  • 他のCobraを使用するプロジェクトはこちらのリストを参照

Overview

Cobra is a library providing a simple interface to create powerful modern CLI interfaces similar to git & go tools.

  • git や go ツールに似た強力な最新の CLI インターフェースを作成するためのシンプルなインターフェースを提供するライブラリ

Cobra provides:

  • Easy subcommand-based CLIs: app server, app fetch, etc.
  • Fully POSIX-compliant flags (including short & long versions)
  • Nested subcommands
  • Global, local and cascading flags
  • Intelligent suggestions (app srver... did you mean app server?)
  • Automatic help generation for commands and flags
  • Grouping help for subcommands
  • Automatic help flag recognition of -h, --help, etc.
  • Automatically generated shell autocomplete for your application (bash, zsh, fish, powershell)
  • Automatically generated man pages for your application
  • Command aliases so you can change things without breaking them
  • The flexibility to define your own help, usage, etc.
  • Optional seamless integration with viper for 12-factor apps
  • Cobraが下記を提供している
    • 簡単なサブコマンドベースの CLI: アプリサーバー、アプリフェッチなど
    • 完全にPOSIX準拠のフラグ(短いバージョンと長いバージョンを含む)
    • ネストされたサブコマンド
    • グローバル、ローカル、カスケードフラグ
    • インテリジェントな提案
    • コマンドとフラグの自動ヘルプ生成
    • サブコマンドのヘルプのグループ化
    • -h、--help などのヘルプ フラグの自動認識
    • アプリケーション用のシェルのオートコンプリートを自動的に生成
    • コマンドエイリアスを使用すると、変更しても壊れることはない
    • 独自のヘルプや使用方法などを定義できる柔軟性
    • Twelve-Factor AppのViperとの統合

Concepts

Cobra is built on a structure of commands, arguments & flags.
Commands represent actions, Args are things and Flags are modifiers for those actions.
The best applications read like sentences when used, and as a result, users intuitively know how to interact with them.
The pattern to follow is APPNAME VERB NOUN --ADJECTIVE or APPNAME COMMAND ARG --FLAG.
A few good real world examples may better illustrate this point.
In the following example, 'server' is a command, and 'port' is a flag:

  • Cobra は、コマンド、引数、フラグの構造に基づいて構築されている
  • コマンドはアクションを表し、引数は物、フラグはそれらのアクションの修飾子
  • 最高のアプリケーションは、使用時に文章のように読み取られ、その結果、ユーザーは直感的に操作方法を知ることができる
  • 従うべきパターンは、APPNAME VERB NOUN --ADJECTIVE または APPNAME COMMAND ARG --FLAG
  • 例):「server」はコマンド、「port」はフラグ
    hugo server --port=1313
    
  • 例):このコマンドでは、Git に URL をそのままクローンするように指示している
    git clone URL --bare
    

Commands

Command is the central point of the application. Each interaction that the application supports will be contained in a Command. A command can have children commands and optionally run an action.

  • コマンドはアプリケーションの中心点
  • アプリケーションがサポートする各インタラクションはコマンドに含まれる
  • コマンドには子コマンドを含めることができ、オプションでアクションを実行できる

Flags

A flag is a way to modify the behavior of a command. Cobra supports fully POSIX-compliant flags as well as the Go flag package. A Cobra command can define flags that persist through to children commands and flags that are only available to that command.

  • フラグは、コマンドの動作を変更する方法
  • Cobra は、Go フラグ パッケージだけでなく、POSIX に完全準拠したフラグもサポートしている
  • Cobra コマンドでは、子コマンドにまで存続するフラグと、そのコマンドでのみ使用可能なフラグを定義できる

Flag functionality is provided by the pflag library, a fork of the flag standard library which maintains the same interface while adding POSIX compliance.

  • フラグ機能は、POSIX 準拠を追加しながら同じインターフェースを維持するフラグ標準ライブラリのフォークである pflag ライブラリによって提供される

実践

前提

下記を前提としています。

  • VSCode
  • MacOS:Sonoma
  • Go:1.23.0

セットアップ

プロジェクトの作成

  1. ディレクトリを作成します
    % mkdir go-cli
    % 
    
  2. 作成したディレクトリをVSCodeのワークスペースに追加します
  3. プロジェクトを初期化します
    % cd go-cli
    % go mod init go-cli
    go: creating new go.mod: module go-cli
    % cat go.mod 
    module go-cli
    
    go 1.23.0
    % 
    

Cobraのセットアップ

  1. Cobraをインストールします

    % go get -u github.com/spf13/cobra@latest
    go: downloading github.com/spf13/cobra v1.8.1
    go: downloading github.com/inconshreveable/mousetrap v1.1.0
    go: downloading github.com/spf13/pflag v1.0.5
    go: added github.com/inconshreveable/mousetrap v1.1.0
    go: added github.com/spf13/cobra v1.8.1
    go: added github.com/spf13/pflag v1.0.5
    % ls -ltr
    total 16
    -rw-r--r--  1 XXXX  XXXX  181 10 21 22:32 go.mod
    -rw-r--r--  1 XXXX  XXXX  896 10 21 22:32 go.sum
    % cat go.mod 
    module go-cli
    
    go 1.23.0
    
    require (
            github.com/inconshreveable/mousetrap v1.1.0 // indirect
            github.com/spf13/cobra v1.8.1 // indirect
            github.com/spf13/pflag v1.0.5 // indirect
    )
    % 
    
  2. CLIアプリケーションのベースを自動生成するために、CLIもインストールします

    % go install github.com/spf13/cobra-cli@latest
    go: downloading github.com/spf13/cobra-cli v1.3.0
    go: downloading github.com/spf13/cobra v1.3.0
    go: downloading github.com/spf13/viper v1.10.1
    go: downloading github.com/spf13/jwalterweatherman v1.1.0
    go: downloading github.com/magiconair/properties v1.8.5
    go: downloading github.com/mitchellh/mapstructure v1.4.3
    go: downloading github.com/spf13/afero v1.6.0
    go: downloading github.com/fsnotify/fsnotify v1.5.1
    go: downloading github.com/spf13/cast v1.4.1
    go: downloading github.com/subosito/gotenv v1.2.0
    go: downloading gopkg.in/ini.v1 v1.66.2
    go: downloading github.com/hashicorp/hcl v1.0.0
    go: downloading github.com/pelletier/go-toml v1.9.4
    go: downloading gopkg.in/yaml.v2 v2.4.0
    go: downloading golang.org/x/sys v0.0.0-20211210111614-af8b64212486
    go: downloading golang.org/x/text v0.3.7
    %
    
  3. PATHを確認します

    % go env GOPATH
    /Users/XXXX/go
    % 
    
  4. .zshrcにPATHを追加します

    export PATH="$PATH:$(go env GOPATH)/bin"
    
  5. 読み込みます

    source ~/.zshrc
    
  6. 実行できました

    % cobra-cli -h   
    Cobra is a CLI library for Go that empowers applications.
    This application is a tool to generate the needed files
    to quickly create a Cobra application.
    
    Usage:
      cobra-cli [command]
    
    Available Commands:
      add         Add a command to a Cobra Application
      completion  Generate the autocompletion script for the specified shell
      help        Help about any command
      init        Initialize a Cobra Application
    
    Flags:
      -a, --author string    author name for copyright attribution (default "YOUR NAME")
          --config string    config file (default is $HOME/.cobra.yaml)
      -h, --help             help for cobra-cli
      -l, --license string   name of license for the project
          --viper            use Viper for configuration
    
    Use "cobra-cli [command] --help" for more information about a command.
    % 
    

Cobraを使ったCLIアプリケーションの作成

プロジェクトの初期化

  1. 下記のコマンドを実行し、初期化します
    % cobra-cli init
    Your Cobra application is ready at
    /Users/XXXX/XXXX/go-cli
    % 
    
  2. LICENSEcmdディレクトリ、main.goオブジェクトが生成されました
    % ls -l
    total 24
    -rw-r--r--  1 XXX  XXX    0 10 21 22:53 LICENSE
    drwxr-x--x  3 XXX  XXX   96 10 21 22:53 cmd
    -rw-r--r--  1 XXX  XXX  181 10 21 22:32 go.mod
    -rw-r--r--  1 XXX  XXX  896 10 21 22:32 go.sum
    -rw-r--r--  1 XXX  XXX  117 10 21 22:53 main.go
    % 
    
  3. main.goを確認してみます
    % cat main.go 
    /*
    Copyright © 2024 NAME HERE <EMAIL ADDRESS>
    
    */
    package main
    
    import "go-cli/cmd"
    
    func main() {
            cmd.Execute()
    }
    % 
    
  4. cmd/root.goを確認してみます
    % cat cmd/root.go 
    /*
    Copyright © 2024 NAME HERE <EMAIL ADDRESS>
    
    */
    package cmd
    
    import (
            "os"
    
            "github.com/spf13/cobra"
    )
    
    
    
    // rootCmd represents the base command when called without any subcommands
    var rootCmd = &cobra.Command{
            Use:   "go-cli",
            Short: "A brief description of your application",
            Long: `A longer description that spans multiple lines and likely contains
    examples and usage of using your application. For example:
    
    Cobra is a CLI library for Go that empowers applications.
    This application is a tool to generate the needed files
    to quickly create a Cobra application.`,
            // Uncomment the following line if your bare application
            // has an action associated with it:
            // Run: func(cmd *cobra.Command, args []string) { },
    }
    
    // Execute adds all child commands to the root command and sets flags appropriately.
    // This is called by main.main(). It only needs to happen once to the rootCmd.
    func Execute() {
            err := rootCmd.Execute()
            if err != nil {
                    os.Exit(1)
            }
    }
    
    func init() {
            // Here you will define your flags and configuration settings.
            // Cobra supports persistent flags, which, if defined here,
            // will be global for your application.
    
            // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.go-cli.yaml)")
    
            // Cobra also supports local flags, which will only run
            // when this action is called directly.
            rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
    }
    % 
    

helloコマンドの追加

  1. コマンドを追加します

    % cobra-cli add hello
    hello created at /Users/XXXX/XXXX/go-cli
    % 
    
  2. helloファイルが追加されました

    % ls -l cmd 
    total 16
    -rw-r--r--  1 XXXX  XXXX  1072 10 21 23:00 hello.go
    -rw-r--r--  1 XXXX  XXXX  1438 10 21 22:53 root.go
    % 
    
  3. 下記のように編集します

    /*
    Copyright © 2024 NAME HERE <EMAIL ADDRESS>
    */
    package cmd
    
    import (
    	"fmt"
    
    	"github.com/spf13/cobra"
    )
    
    var name string
    
    // helloCmd represents the hello command
    var helloCmd = &cobra.Command{
    	Use:   "hello",
    	Short: "Say hello to someone",
    	Long:  `This command allows you to say hello to someone by specifying their name.`,
    	Run: func(cmd *cobra.Command, args []string) {
    		fmt.Printf("Hello, %s!\n", name)
    	},
    }
    
    func init() {
    	rootCmd.AddCommand(helloCmd)
    	helloCmd.Flags().StringVarP(&name, "name", "n", "World", "Name of the person to greet")
    }
    
  4. 実行します。想定通りの結果です

    % go run main.go hello --name Taro
    Hello, Taro!
    % 
    

ビルド

バイナリファイルを生成し、実行します

  1. go buildコマンドを実行します
    % go build -o hello 
    
  2. 確認します
    % ls -l            
    total 10680
    -rw-r--r--  1 XXXX  XXXX        0 10 21 22:53 LICENSE
    drwxr-x--x  4 XXXX  XXXX      128 10 21 23:00 cmd
    -rw-r--r--  1 XXXX  XXXX      181 10 21 23:13 go.mod
    -rw-r--r--  1 XXXX  XXXX      896 10 21 22:32 go.sum
    -rwxr-xr-x  1 XXXX  XXXX  5452224 10 21 23:21 hello
    -rw-r--r--  1 XXXX  XXXX      117 10 21 22:53 main.go
    % 
    
  3. 実行できました
    % ./hello hello --name Jiro
    Hello, Jiro!
    % 
    

フラグをトップレベルコマンドに適用する

hello --nameの形式で実行できるようにします。

  1. hello.goを削除します

    % rm cmd/hello.go
    
  2. root.goを修正します

    /*
    Copyright © 2024 NAME HERE <EMAIL ADDRESS>
    */
    package cmd
    
    import (
    	"fmt"
    	"os"
    
    	"github.com/spf13/cobra"
    )
    
    var name string
    
    // rootCmd represents the base command when called without any subcommands
    var rootCmd = &cobra.Command{
    	Use:   "hello",
    	Short: "Say hello to someone",
    	Long:  `This application allows you to say hello to a person by specifying their name.`,
    	Run: func(cmd *cobra.Command, args []string) {
    		fmt.Printf("Hello, %s!\n", name)
    	},
    }
    
    func Execute() {
    	err := rootCmd.Execute()
    	if err != nil {
    		fmt.Println(err)
    		os.Exit(1)
    	}
    }
    
    func init() {
    	rootCmd.Flags().StringVarP(&name, "name", "n", "World", "Name of the person to greet")
    }
    
  3. できました

    % ls -l
    total 10680
    -rw-r--r--  1 XXXX  XXXX        0 10 21 22:53 LICENSE
    drwxr-x--x  3 XXXX  XXXX       96 10 21 23:36 cmd
    -rw-r--r--  1 XXXX  XXXX      181 10 21 23:13 go.mod
    -rw-r--r--  1 XXXX  XXXX      896 10 21 22:32 go.sum
    -rwxr-xr-x  1 XXXX  XXXX  5452128 10 21 23:37 hello
    -rw-r--r--  1 XXXX  XXXX      117 10 21 22:53 main.go
    % 
    
  4. 実行します。想定通りの結果です

    % ./hello --name Jiro
    Hello, Jiro!
    % 
    

考察

今回、Cobraの基本的な特徴を整理し、簡単な動作確認を行いました。シンプルなCLIツールを作成するところまで進めましたが、今後はサブコマンドを追加したり、より複雑な機能を持つCLIツールの実装を試したいと思います。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?