GoでCLIを作る時に便利なurfave/cli

GoでCLIを作ろうとなった時には中身のロジックも勿論必要だけどユーザーが入力するインターフェースとなる部分が必要となる。
オプションをわたすようなものの場合は標準のflagパッケージを使えば良いんだけど、commandを指定して機能をわけたいとかなった時には面倒になる。
このあたりのコマンドとかオプションをよしなにしてくれるのが今回紹介するurfave/cli!!

github.com

urfave/cliとは?

A simple, fast, and fun package for building command line apps in Go

とある。
これを使うとコマンドラインからの引数や指定されたコマンドをうまい感じにハンドリングすることが出来る。

実績でいうとghqとかakamaicliとかもこのパッケージを使っている。

使い方

先に今回のサンプルコードをのせる。

package main

import (
    "fmt"
    "os"

    "github.com/urfave/cli"
)

func main() {
    var (
        suffix string
    )

    app := cli.NewApp()
    app.Name = "Hello xxxx"
    app.Usage = "Make `Hello xxx` for arbitrary text"
    app.Version = "0.1.0"

    app.Flags = []cli.Flag{
        cli.StringFlag{
            Name:        "suffix, s",
            Value:       "!!!",
            Usage:       "text after speaking something",
            Destination: &suffix,
            EnvVar:      "SUFFIX",
        },
    }

    app.Commands = []cli.Command{
        {
            Name:  "hello",
            Usage: "if use set -t or --text",
            Flags: []cli.Flag{
                cli.StringFlag{
                    Name:  "text, t",
                    Value: "world",
                    Usage: "hello xxx text",
                },
            },
            Action: func(c *cli.Context) error {
                fmt.Printf("Hello %s %s\n", c.String("text"), suffix)
                return nil
            },
        },
    }

    app.Run(os.Args)
}

ビルドしたパッケージは--helpを押すとプログラム内のUsageやFlagsなどに応じてヘルプが表示される。

$ ./cli --help
NAME:
   Hello xxxx - Make `Hello xxx` for arbitrary text

USAGE:
   cli [global options] command [command options] [arguments...]

VERSION:
   0.1.0

COMMANDS:
     hello    if use set -t or --text
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --suffix value, -s value  text after speaking something (default: "!!!") [$SUFFIX]
   --help, -h                show help
   --version, -v             print the version

実行自体は次のような感じになる。

$ ./cli hello
Hello world !!!

解説

コードの細い部分を解説していく。

app := cli.NewApp()
app.Name = "Hello xxxx"
app.Usage = "Make `Hello xxx` for arbitrary text"
app.Version = "0.1.0"

app := cli.NewApp()によりCLIを作る時土台を作り、バージョンや名前とか使い方のようなメタ情報を追加出来る。
ここで指定した情報はヘルプ時に表示される。

app.Flags = []cli.Flag{
    cli.StringFlag{
        Name:        "suffix, s",
        Value:       "!!!",
        Usage:       "text after speaking something",
        Destination: &suffix,
        EnvVar:      "SUFFIX",
    },
}

ここのFlagsで指定されたものはglobal optionとして使われるもので、コマンド間で共有されるオプションとなる。
今回はDestinationで格納先を指定したがそれを指定しなかった時はGlobalStringGlobalIntなどのように指定して参照することが出来る。
またEnvVarを指定することで環境変数からもオプションの値を指定することもで出来る。

$ SUFFIX='(+_+)' ./cli hello
Hello world (+_+)
app.Commands = []cli.Command{
    {
        Name:  "hello",
        Usage: "if use set -t or --text",
        Flags: []cli.Flag{
            cli.StringFlag{
                Name:  "text, t",
                Value: "world",
                Usage: "hello xxx text",
            },
        },
        Action: func(c *cli.Context) error {
            fmt.Printf("Hello %s %s\n", c.String("text"), suffix)
            return nil
        },
    },
}

この記載でコマンドを定義する。
そのコマンドの中でのオプションを定義したい時は Flagsをキーにしてglobal optionの時のような記載をする。
globalオプションと各コマンドのオプションを指定すると次のような実行になる。

$ ./cli --suffix hogehoge hello --text WORLD
Hello WORLD hogehoge

まとめ

今回はGoでCLIが作りやすくなるurfave/cliを紹介した。
これ便利だ