Open7

Kong の使い方がよくわからない

Shunsuke SuzukiShunsuke Suzuki

https://github.com/alecthomas/kong

を試しているが使い方がよくわかりません。

  1. sub command をもたせた場合に global option だけを指定する方法 foo --version
  2. help message に何か文章を追加する方法
  3. help command の追加方法 foo help

例えば以下のような CLI foo を書きます。

package main

import (
	"fmt"
	"log"

	"github.com/alecthomas/kong"
)

func main() {
	if err := run(); err != nil {
		log.Fatal(err)
	}
}

type CLI struct {
	Add     struct{} `cmd:"" help:"Add a tool"`
	List    struct{} `cmd:"" help:"List tools"`
	Version bool     `help:"Show version" short:"v"`
}

func run() error {
	cli := &CLI{}
	kctx := kong.Parse(cli)
	if cli.Version {
		fmt.Println("v1.0.0")
		return nil
	}
	switch cmd := kctx.Command(); cmd {
	case "add":
		fmt.Println("add")
		return nil
	case "list":
		fmt.Println("list")
		return nil
	default:
		return fmt.Errorf("unknown command: %s", cmd)
	}

	return nil
}
foo add
foo list
foo --version
foo --help
  1. この場合、 foo --version は失敗します。 sub command が必須になってしまっています。
$ go run main.go --version
main: error: expected one of "add", "list"
exit status 1
  1. foo の help message に何か文章を追加する方法が分かりません。

例えば help の先頭にコマンドの説明とか GitHub Repository へのリンクとかを追加したいと思っています。

$ go run main.go --help   
Usage: main <command> [flags]

Flags:
  -h, --help       Show context-sensitive help.
  -v, --version    Show version

Commands:
  add [flags]
    Add a tool

  list [flags]
    List tools

Run "main <command> --help" for more information on a command.
  1. help command の追加方法が分かりません。

デフォルトで --help option は実装されています。しかし、 help command はされていません (まぁそれはそう)

$ go run main.go help  
main: error: unexpected argument help
exit status 1

そこで help command を追加して PrintUsage で usage を出力してみましたが、 PrintUsage の結果は help command の usage になってしまい、 foo コマンド全体の usage になりません。

package main

import (
	"fmt"
	"log"

	"github.com/alecthomas/kong"
)

func main() {
	if err := run(); err != nil {
		log.Fatal(err)
	}
}

type CLI struct {
	Add     struct{} `cmd:"" help:"Add a tool"`
	List    struct{} `cmd:"" help:"List tools"`
	Help    struct{} `cmd:"" help:"Show help"`
	Version bool     `help:"Show version" short:"v"`
}

func run() error {
	cli := &CLI{}
	kctx := kong.Parse(cli)
	if cli.Version {
		fmt.Println("v1.0.0")
		return nil
	}
	switch cmd := kctx.Command(); cmd {
	case "help":
		kctx.PrintUsage(false)
		return nil
	case "add":
		fmt.Println("add")
		return nil
	case "list":
		fmt.Println("list")
		return nil
	default:
		return fmt.Errorf("unknown command: %s", cmd)
	}

	return nil
}
$ go run main.go help
Usage: main help [flags]

Show help

Flags:
  -h, --help       Show context-sensitive help.
  -v, --version    Show version
Shunsuke SuzukiShunsuke Suzuki

ecspresso などの fujiwara-ware で使われている。

ecspresso に関して言うと --version option をサポートしていない。

$ ecspresso --version
2025/01/15 12:42:37 [ERROR] FAILED. failed to parse args: unknown flag --version

help message を見る限りカスタマイズしているようにも見えない。

$ ecspresso --help   
Usage: ecspresso <command>

Flags:
  -h, --help                      Show context-sensitive help.
      --envfile=ENVFILE,...       environment files ($ECSPRESSO_ENVFILE)
      --debug                     enable debug log ($ECSPRESSO_DEBUG)
      --ext-str=KEY=VALUE;...     external string values for Jsonnet ($ECSPRESSO_EXT_STR)
      --ext-code=KEY=VALUE;...    external code values for Jsonnet ($ECSPRESSO_EXT_CODE)
      --config="ecspresso.yml"    config file ($ECSPRESSO_CONFIG)
      --assume-role-arn=""        the ARN of the role to assume ($ECSPRESSO_ASSUME_ROLE_ARN)
      --timeout=TIMEOUT           timeout. Override in a configuration file ($ECSPRESSO_TIMEOUT).
      --filter-command=STRING     filter command ($ECSPRESSO_FILTER_COMMAND)
      --[no-]color                enable colorized output ($ECSPRESSO_COLOR)

Commands:
  appspec
    output AppSpec YAML for CodeDeploy to STDOUT

  delete
    delete service

  deploy
    deploy service

  deregister
    deregister task definition

  diff
    show diff between task definition, service definition with current running service and task definition

  exec
    execute command on task

  init --service=STRING --task-definition=STRING
    create configuration files from existing ECS service

  refresh
    refresh service. equivalent to deploy --skip-task-definition --force-new-deployment --no-update-service

  register
    register task definition

  render <targets>
    render config, service definition or task definition file to STDOUT

  revisions
    show revisions of task definitions

  rollback
    rollback service

  run
    run task

  scale
    scale service. equivalent to deploy --skip-task-definition --no-update-service

  status
    show status of service

  tasks
    list tasks that are in a service or having the same family

  verify
    verify resources in configurations

  wait
    wait until service stable

  version
    show version

Run "ecspresso <command> --help" for more information on a command.

help コマンドは自分で parse している。
これはこれで良いけど、自前で書かないといけないのがちょっといけてない気もする。

https://github.com/kayac/ecspresso/blob/8fda20769b51e62573eda892c8f5f516c6c5bf29/cliv2.go#L13-L15