Kong is a command-line parser for Go

Overview

Kong is a command-line parser for Go

CircleCI Go Report Card Slack chat

Introduction

Kong aims to support arbitrarily complex command-line structures with as little developer effort as possible.

To achieve that, command-lines are expressed as Go types, with the structure and tags directing how the command line is mapped onto the struct.

For example, the following command-line:

shell rm [-f] [-r] <paths> ...
shell ls [<paths> ...]

Can be represented by the following command-line structure:

package main

import "github.com/alecthomas/kong"

var CLI struct {
  Rm struct {
    Force     bool `help:"Force removal."`
    Recursive bool `help:"Recursively remove files."`

    Paths []string `arg name:"path" help:"Paths to remove." type:"path"`
  } `cmd help:"Remove files."`

  Ls struct {
    Paths []string `arg optional name:"path" help:"Paths to list." type:"path"`
  } `cmd help:"List paths."`
}

func main() {
  ctx := kong.Parse(&CLI)
  switch ctx.Command() {
  case "rm <path>":
  case "ls":
  default:
    panic(ctx.Command())
  }
}

Help

Help as a user of a Kong application

Every Kong application includes a --help flag that will display auto-generated help.

eg.

$ shell --help
usage: shell <command>

A shell-like example app.

Flags:
  --help   Show context-sensitive help.
  --debug  Debug mode.

Commands:
  rm <path> ...
    Remove files.

  ls [<path> ...]
    List paths.

If a command is provided, the help will show full detail on the command including all available flags.

eg.

$ shell --help rm
usage: shell rm <paths> ...

Remove files.

Arguments:
  <paths> ...  Paths to remove.

Flags:
      --debug        Debug mode.

  -f, --force        Force removal.
  -r, --recursive    Recursively remove files.

Defining help in Kong

Help is automatically generated from the command-line structure itself, including help:"" and other tags. Variables will also be interpolated into the help string.

Finally, any command, argument, or flag type implementing the interface Help() string will have this function called to retrieve the help string. This allows for much more descriptive text than can fit in Go tags.

Command handling

There are two ways to handle commands in Kong.

Switch on the command string

When you call kong.Parse() it will return a unique string representation of the command. Each command branch in the hierarchy will be a bare word and each branching argument or required positional argument will be the name surrounded by angle brackets. Here's an example:

There's an example of this pattern here.

eg.

package main

import "github.com/alecthomas/kong"

var CLI struct {
  Rm struct {
    Force     bool `help:"Force removal."`
    Recursive bool `help:"Recursively remove files."`

    Paths []string `arg name:"path" help:"Paths to remove." type:"path"`
  } `cmd help:"Remove files."`

  Ls struct {
    Paths []string `arg optional name:"path" help:"Paths to list." type:"path"`
  } `cmd help:"List paths."`
}

func main() {
  ctx := kong.Parse(&CLI)
  switch ctx.Command() {
  case "rm <path>":
  case "ls":
  default:
    panic(ctx.Command())
  }
}

This has the advantage that it is convenient, but the downside that if you modify your CLI structure, the strings may change. This can be fragile.

Attach a Run(...) error method to each command

A more robust approach is to break each command out into their own structs:

  1. Break leaf commands out into separate structs.
  2. Attach a Run(...) error method to all leaf commands.
  3. Call kong.Kong.Parse() to obtain a kong.Context.
  4. Call kong.Context.Run(bindings...) to call the selected parsed command.

Once a command node is selected by Kong it will search from that node back to the root. Each encountered command node with a Run(...) error will be called in reverse order. This allows sub-trees to be re-used fairly conveniently.

In addition to values bound with the kong.Bind(...) option, any values passed through to kong.Context.Run(...) are also bindable to the target's Run() arguments.

Finally, hooks can also contribute bindings via kong.Context.Bind() and kong.Context.BindTo().

There's a full example emulating part of the Docker CLI here.

eg.

type Context struct {
  Debug bool
}

type RmCmd struct {
  Force     bool `help:"Force removal."`
  Recursive bool `help:"Recursively remove files."`

  Paths []string `arg name:"path" help:"Paths to remove." type:"path"`
}

func (r *RmCmd) Run(ctx *Context) error {
  fmt.Println("rm", r.Paths)
  return nil
}

type LsCmd struct {
  Paths []string `arg optional name:"path" help:"Paths to list." type:"path"`
}

func (l *LsCmd) Run(ctx *Context) error {
  fmt.Println("ls", l.Paths)
  return nil
}

var cli struct {
  Debug bool `help:"Enable debug mode."`

  Rm RmCmd `cmd help:"Remove files."`
  Ls LsCmd `cmd help:"List paths."`
}

func main() {
  ctx := kong.Parse(&cli)
  // Call the Run() method of the selected parsed command.
  err := ctx.Run(&Context{Debug: cli.Debug})
  ctx.FatalIfErrorf(err)
}

Hooks: BeforeResolve(), BeforeApply(), AfterApply() and the Bind() option

If a node in the grammar has a BeforeResolve(...), BeforeApply(...) error and/or AfterApply(...) error method, those methods will be called before validation/assignment and after validation/assignment, respectively.

The --help flag is implemented with a BeforeApply hook.

Arguments to hooks are provided via the Run(...) method or Bind(...) option. *Kong, *Context and *Path are also bound and finally, hooks can also contribute bindings via kong.Context.Bind() and kong.Context.BindTo().

eg.

// A flag with a hook that, if triggered, will set the debug loggers output to stdout.
type debugFlag bool

func (d debugFlag) BeforeApply(logger *log.Logger) error {
  logger.SetOutput(os.Stdout)
  return nil
}

var cli struct {
  Debug debugFlag `help:"Enable debug logging."`
}

func main() {
  // Debug logger going to discard.
  logger := log.New(ioutil.Discard, "", log.LstdFlags)

  ctx := kong.Parse(&cli, kong.Bind(logger))

  // ...
}

Flags

Any mapped field in the command structure not tagged with cmd or arg will be a flag. Flags are optional by default.

eg. The command-line app [--flag="foo"] can be represented by the following.

type CLI struct {
  Flag string
}

Commands and sub-commands

Sub-commands are specified by tagging a struct field with cmd. Kong supports arbitrarily nested commands.

eg. The following struct represents the CLI structure command [--flag="str"] sub-command.

type CLI struct {
  Command struct {
    Flag string

    SubCommand struct {
    } `cmd`
  } `cmd`
}

If a sub-command is tagged with default:"1" it will be selected if there are no further arguments.

Branching positional arguments

In addition to sub-commands, structs can also be configured as branching positional arguments.

This is achieved by tagging an unmapped nested struct field with arg, then including a positional argument field inside that struct with the same name. For example, the following command structure:

app rename <name> to <name>

Can be represented with the following:

var CLI struct {
  Rename struct {
    Name struct {
      Name string `arg` // <-- NOTE: identical name to enclosing struct field.
      To struct {
        Name struct {
          Name string `arg`
        } `arg`
      } `cmd`
    } `arg`
  } `cmd`
}

This looks a little verbose in this contrived example, but typically this will not be the case.

Terminating positional arguments

If a mapped type is tagged with arg it will be treated as the final positional values to be parsed on the command line.

If a positional argument is a slice, all remaining arguments will be appended to that slice.

Slices

Slice values are treated specially. First the input is split on the sep:"<rune>" tag (defaults to ,), then each element is parsed by the slice element type and appended to the slice. If the same value is encountered multiple times, elements continue to be appended.

To represent the following command-line:

cmd ls <file> <file> ...

You would use the following:

var CLI struct {
  Ls struct {
    Files []string `arg type:"existingfile"`
  } `cmd`
}

Maps

Maps are similar to slices except that only one key/value pair can be assigned per value, and the sep tag denotes the assignment character and defaults to =.

To represent the following command-line:

cmd config set <key>=<value> <key>=<value> ...

You would use the following:

var CLI struct {
  Config struct {
    Set struct {
      Config map[string]float64 `arg type:"file:"`
    } `cmd`
  } `cmd`
}

For flags, multiple key+value pairs should be separated by mapsep:"rune" tag (defaults to ;) eg. --set="key1=value1;key2=value2".

Custom named decoders

Kong includes a number of builtin custom type mappers. These can be used by specifying the tag type:"<type>". They are registered with the option function NamedMapper(name, mapper).

Name Description
path A path. ~ expansion is applied. - is accepted for stdout, and will be passed unaltered.
existingfile An existing file. ~ expansion is applied. - is accepted for stdin, and will be passed unaltered.
existingdir An existing directory. ~ expansion is applied.
counter Increment a numeric field. Useful for -vvv. Can accept -s, --long or --long=N.

Slices and maps treat type tags specially. For slices, the type:"" tag specifies the element type. For maps, the tag has the format tag:"[<key>]:[<value>]" where either may be omitted.

Supported field types

Custom decoders (mappers)

Any field implementing encoding.TextUnmarshaler or json.Unmarshaler will use those interfaces for decoding values. Kong also includes builtin support for many common Go types:

Type Description
time.Duration Populated using time.ParseDuration().
time.Time Populated using time.Parse(). Format defaults to RFC3339 but can be overridden with the format:"X" tag.
*os.File Path to a file that will be opened, or - for os.Stdin. File must be closed by the user.
*url.URL Populated with url.Parse().

For more fine-grained control, if a field implements the MapperValue interface it will be used to decode arguments into the field.

Supported tags

Tags can be in two forms:

  1. Standard Go syntax, eg. kong:"required,name='foo'".
  2. Bare tags, eg. required name:"foo"

Both can coexist with standard Tag parsing.

Tag Description
cmd If present, struct is a command.
arg If present, field is an argument.
env:"X" Specify envar to use for default value.
name:"X" Long name, for overriding field name.
help:"X" Help text.
type:"X" Specify named types to use.
placeholder:"X" Placeholder text.
default:"X" Default value.
default:"1" On a command, make it the default.
short:"X" Short name, if flag.
aliases:"X,Y" One or more aliases (for cmd).
required If present, flag/arg is required.
optional If present, flag/arg is optional.
hidden If present, command or flag is hidden.
negatable If present on a bool field, supports prefixing a flag with --no- to invert the default value
format:"X" Format for parsing input, if supported.
sep:"X" Separator for sequences (defaults to ","). May be none to disable splitting.
mapsep:"X" Separator for maps (defaults to ";"). May be none to disable splitting.
enum:"X,Y,..." Set of valid values allowed for this flag.
group:"X" Logical group for a flag or command.
xor:"X" Exclusive OR group for flags. Only one flag in the group can be used which is restricted within the same command.
prefix:"X" Prefix for all sub-flags.
set:"K=V" Set a variable for expansion by child elements. Multiples can occur.
embed If present, this field's children will be embedded in the parent. Useful for composition.
- Ignore the field. Useful for adding non-CLI fields to a configuration struct.

Plugins

Kong CLI's can be extended by embedding the kong.Plugin type and populating it with pointers to Kong annotated structs. For example:

var pluginOne struct {
  PluginOneFlag string
}
var pluginTwo struct {
  PluginTwoFlag string
}
var cli struct {
  BaseFlag string
  kong.Plugins
}
cli.Plugins = kong.Plugins{&pluginOne, &pluginTwo}

Additionally if an interface type is embedded, it can also be populated with a Kong annotated struct.

Variable interpolation

Kong supports limited variable interpolation into help strings, enum lists and default values.

Variables are in the form:

${<name>}
${<name>=<default>}

Variables are set with the Vars{"key": "value", ...} option. Undefined variable references in the grammar without a default will result in an error at construction time.

Variables can also be set via the set:"K=V" tag. In this case, those variables will be available for that node and all children. This is useful for composition by allowing the same struct to be reused.

When interpolating into flag or argument help strings, some extra variables are defined from the value itself:

${default}
${enum}

For flags with associated environment variables, the variable ${env} can be interpolated into the help string. In the absence of this variable in the help string, Kong will append ($$${env}) to the help string.

eg.

type cli struct {
  Config string `type:"path" default:"${config_file}"`
}

func main() {
  kong.Parse(&cli,
    kong.Vars{
      "config_file": "~/.app.conf",
    })
}

Validation

Kong does validation on the structure of a command-line, but also supports extensible validation. Any node in the tree may implement the following interface:

type Validatable interface {
    Validate() error
 }

+If one of these nodes is in the active command-line it will be called during +normal validation.

Modifying Kong's behaviour

Each Kong parser can be configured via functional options passed to New(cli interface{}, options...Option).

The full set of options can be found here.

Name(help) and Description(help) - set the application name description

Set the application name and/or description.

The name of the application will default to the binary name, but can be overridden with Name(name).

As with all help in Kong, text will be wrapped to the terminal.

Configuration(loader, paths...) - load defaults from configuration files

This option provides Kong with support for loading defaults from a set of configuration files. Each file is opened, if possible, and the loader called to create a resolver for that file.

eg.

kong.Parse(&cli, kong.Configuration(kong.JSON, "/etc/myapp.json", "~/.myapp.json"))

See the tests for an example of how the JSON file is structured.

Resolver(...) - support for default values from external sources

Resolvers are Kong's extension point for providing default values from external sources. As an example, support for environment variables via the env tag is provided by a resolver. There's also a builtin resolver for JSON configuration files.

Example resolvers can be found in resolver.go.

*Mapper(...) - customising how the command-line is mapped to Go values

Command-line arguments are mapped to Go values via the Mapper interface:

// A Mapper knows how to map command-line input to Go.
type Mapper interface {
  // Decode scan into target.
  //
  // "ctx" contains context about the value being decoded that may be useful
  // to some mappers.
  Decode(ctx *MapperContext, scan *Scanner, target reflect.Value) error
}

All builtin Go types (as well as a bunch of useful stdlib types like time.Time) have mappers registered by default. Mappers for custom types can be added using kong.??Mapper(...) options. Mappers are applied to fields in four ways:

  1. NamedMapper(string, Mapper) and using the tag key type:"<name>".
  2. KindMapper(reflect.Kind, Mapper).
  3. TypeMapper(reflect.Type, Mapper).
  4. ValueMapper(interface{}, Mapper), passing in a pointer to a field of the grammar.

ConfigureHelp(HelpOptions) and Help(HelpFunc) - customising help

The default help output is usually sufficient, but if not there are two solutions.

  1. Use ConfigureHelp(HelpOptions) to configure how help is formatted (see HelpOptions for details).
  2. Custom help can be wired into Kong via the Help(HelpFunc) option. The HelpFunc is passed a Context, which contains the parsed context for the current command-line. See the implementation of PrintHelp for an example.
  3. Use HelpFormatter(HelpValueFormatter) if you want to just customize the help text that is accompanied by flags and arguments.
  4. Use Groups([]Group) if you want to customize group titles or add a header.

Bind(...) - bind values for callback hooks and Run() methods

See the section on hooks for details.

Other options

The full set of options can be found here.

Issues
  • Add support for negatable boolean flags

    Add support for negatable boolean flags

    Adds support for a new negatable struct tag, which will automatically set a boolean Struct flag value to false if the flag's name starts with --no-, even when the default is true. This idea is shamelessly stolen from the commander npm library

    For example:

    type Cmd struct {
      Sort bool `default:"true",negatable`
    }
    

    Calling

    command
    # or
    command --sort
    

    Will set Sort to true, but

    command --no-sort
    

    Will set Sort to false.

    Why do this?

    It avoids the situation of having to do:

    type Cmd struct {
      NoSort bool
    }
    
    func (c *Cmd) Run() error {
      if !c.NoSort {
        // Double negations are hard to read!
      }
      return nil
    }
    
    opened by josephschmitt 16
  • `-x` in an `arg` string slice confuses kong

    `-x` in an `arg` string slice confuses kong

    I'm writing a program that takes a bunch of arguments, as well as a shell command as input (interpreted by kong as an arg) and exec's that command after doing a little processing. Unfortunately, kong doesn't deal well with the presence of short arguments given to the command-as-argument confuse kong and it errors. Here's an example:

    package main
    
    import (
    	"fmt"
    
    	"github.com/alecthomas/kong"
    )
    
    var CLI struct {
    	Run struct {
    		Cmd []string `arg name:"command" help:"Command to run."`
    	} `cmd help:"Run command."`
    }
    
    func main() {
    	ctx := kong.Parse(&CLI)
    	switch ctx.Command() {
    	case "run <command>":
    		fmt.Println(CLI)
    	default:
    		panic(ctx.Command())
    	}
    }
    

    Results

    A plain command works:

    $ go run main.go run ls
    {{[ls]}}
    

    A command with flags works after a --:

    $ go run main.go run -- ls -la
    {{[ls -la]}}
    

    A command with flags is interpreted by kong and errors:

    $ go run main.go run ls -la
    main: error: unknown flag -l
    exit status 1
    

    Expected result

    I would have expected kong to stop parsing further arguments once it finds things fitting the terminating argument string slice & treat the command line given above like it does it in the "works after a --" example.

    Workaround

    Have users pass in the -- flag, but that they can pass the un----escaped command line means if they start out without the escape and then add flags later, the command may fail without them realizing (or expecting) it.

    opened by antifuchs 16
  • Example of custom validation?

    Example of custom validation?

    I had a look around the README and the examples and couldn't see an example of the canonical way to do custom validation for a flag. Apologies the issue question!

    I assume I need a custom type and then to use BeforeApply to validate it, but I can't figure out what to bind in there, I tried kong.Value, but no luck (e.g couldn't find binding of type kong.Value...)

    Am Iooking at this wrong? Does Validation occur elseswhere?

    For context, the problem I am trying to solve is a complex relationship between flags (more than what xor can handle).

    opened by lox 11
  • Is there a way to validate a flag which is set via an ENV variable?

    Is there a way to validate a flag which is set via an ENV variable?

    So if i have a flag as follows, can i validate this is set without adding required?

    The rational is I just want to know if it was defaulted from an ENV or set via a command flag.

    Stage      stageFlag  `help:"The stage this is deployed." env:"STAGE"`
    

    Cheers

    opened by wolfeidau 11
  • Allow single dash as argument value

    Allow single dash as argument value

    Some CLI tools traditionally use the single dash to mean "stdin".

    Examples are tar tvfz - or cat -

    Kong however chokes when encountering a - not followed with a letter with error: expected short flag.

    opened by mkmik 10
  • Discussion about Help

    Discussion about Help

    While I was working on custom HelpPrinter implementation I realized that I had to copy a lot of code from the DefaultHelpPrinter function. And with the newly added interpolation of the $env variable, the current HelpPrinter function is very close to what I was looking for. To close the gap I would like propose the following changes:

    1) Better representation of enum values as placeholder

    For the following Tags:

    StringFlag string `enum:"a,b,c" help:"Help."`
    

    I would like to see the following placeholder:

    Flags:
          --string-flag=a|b|c        Help.
    

    And if there is a default tag:

    StringFlag string `enum:"a,b,c" help:"Help." default:"b"`
    

    Maybe something like this:

    Flags:
          --string-flag=a|(b)|c        Help.
    

    Or maybe even make the selected value bold with github.com/fatih/color.

    2) AutomaticEnv option

    Add a new Option AutomaticEnv which would do the following:

    Visit all flags and args and sets the empty env field tags with the following:

    strings.ToUpper(app.Model.Name+"_"+strings.ReplaceAll(flag.Name,"-","_"))
    

    So a flag-name would correspond to APPNAME_FLAG_NAME

    Let me know what you think. I am willing to do the implementation if you want.

    opened by zbindenren 10
  • How to load JSON with hierarchy

    How to load JSON with hierarchy

    How to load this kind of JSON to flags:

    {
    	"Name": "Some name",
    	"db": {
    		"host": "localhost",
                    "port": "9000",
    	}
    }
    
    opened by yazver 9
  • command aliases (short commands)

    command aliases (short commands)

    Some flag handling packages allow shortcuts for commands. I can do it my putting commands double in the parent struct like this, but it is kind of ugly:

    type rootCmd struct {
    	LongName    longNameCmd        `cmd:"" help:"Do something."`
    	X1                 longNameCmd        `cmd:"" name:"ln"`
    }
    

    Is there a better way? Should I (try to) make a pull request for an extra tag, or to enable the short tag for commands as well?

    opened by plmuon 9
  • structs should default to

    structs should default to "embed" behaviour if "cmd" isn't specified.

    just getting going with kong, liking it a lot more than other cli parsers I've used. however, there's one minor feature request I'd love. If I've got some nested structs, and the internat struct is tagged with neither "embed" or "cmd", it'd be great if it defaults to the "embed" behaviour, rather than throwing an error:

    type sample struct {
      Inline struct {
        New bool `default:"true"`
      }
    }
    
    func main() {
      s := new(sample)
      kong.Parse(s)
      log.Println(s)
    }
    

    without any tags on Inline:

    panic: unsupported field type main.sample.Inline (of type struct { New bool "default:\"true\" negetable" }), perhaps missing a cmd:"" tag?
    
    goroutine 1 [running]:
    github.com/alecthomas/kong.Parse(0x566380, 0xc0000c00a0, 0x0, 0x0, 0x0, 0x0)
    	/home/tessa/code/go/pkg/mod/github.com/alecthomas/[email protected]/global.go:11 +0x145
    main.main()
    	/home/tessa/code/mine/cnafu/example/main.go:21 +0x5c
    exit status 2
    

    with the embed tag:

    2021/05/08 19:44:24 &{{true}}
    

    I don't see the value in having an untagged embedded struct cause a runtime panic, I think it'd be more intuitive if embed was the default.

    opened by nergdron 9
  • Add UsageOnMissing option to Kong

    Add UsageOnMissing option to Kong

    Adds a UsageOnMissing option which configures Kong to omit errors and display usage when a command is invalid due to a missing argument. This can be useful if a user wishes for the top-level command print usage when no args are supplied, or if they would prefer child commands which have their own subcommands to print usage specific to that command.

    Signed-off-by: hasheddan [email protected]

    I have provided some examples below when used with crank the CLI for @crossplane. Note that they all also have UsageOnError() supplied. This demonstrates how UsageOnMissing() plays nicely (i.e. no duplicate usage msg) by not returning an error.

    Top-level command without UsageOnMissing()

    $ crank
    Usage: crank <command>
    
    A tool for building platforms on Crossplane.
    
    Flags:
      -h, --help    Show context-sensitive help.
    
    Commands:
      configuration get [<name>]
        Get installed Configurations.
    
      configuration create <name> <package>
        Create a Configuration.
    
      provider get [<name>]
        Get installed Providers.
    
      provider create <name> <package>
        Create a Provider.
    
      pkg build [<name>]
        Build a Crossplane package.
    
      pkg push [<name>]
        Push a Crossplane package to a registry.
    
    Run "crank <command> --help" for more information on a command.
    
    crank: error: expected one of "configuration",  "provider",  "pkg"
    exit status 1
    
    

    Top-level command with UsageOnMissing()

    $ crank
    Usage: crank <command>
    
    A tool for building platforms on Crossplane.
    
    Flags:
      -h, --help    Show context-sensitive help.
    
    Commands:
      configuration get [<name>]
        Get installed Configurations.
    
      configuration create <name> <package>
        Create a Configuration.
    
      provider get [<name>]
        Get installed Providers.
    
      provider create <name> <package>
        Create a Provider.
    
      pkg build [<name>]
        Build a Crossplane package.
    
      pkg push [<name>]
        Push a Crossplane package to a registry.
    
    Run "crank <command> --help" for more information on a command.
    

    Example child command without UsageOnMissing()

    $ crank pkg
    Usage: crank pkg <command>
    
    Build and publish packages.
    
    Flags:
      -h, --help    Show context-sensitive help.
    
    Commands:
      pkg build [<name>]
        Build a Crossplane package.
    
      pkg push [<name>]
        Push a Crossplane package to a registry.
    
    crank: error: expected one of "build",  "push"
    exit status 1
    
    

    Example child command with UsageOnMissing()

    $ crank pkg
    Usage: crank pkg <command>
    
    Build and publish packages.
    
    Flags:
      -h, --help    Show context-sensitive help.
    
    Commands:
      pkg build [<name>]
        Build a Crossplane package.
    
      pkg push [<name>]
        Push a Crossplane package to a registry.
    
    opened by hasheddan 8
  • Add --help-all (or equivalent) for advanced help

    Add --help-all (or equivalent) for advanced help

    I'm not a huge fan of expanding subcommands by default for a large CLI tree (so I set NoExpandSubcommands to true)-- however, it seems like a good "advanced" option to provide if a user really wants all of their options.

    So maybe you have normal help (NoExpandSubcommands is true):

    $ go run main.go --help
    Usage: app <command>
    
    complicated app
    
    Flags:
      -h, --help    Show context-sensitive help.
    
    Commands:
      a    Command A
      b    Command B
    
    Run "app <command> --help" for more information on a command.
    

    And then (NoExpandSubcommands is still true) advanced help:

    $ go run main.go --help-all
    Usage: app <command>
    
    complicated app
    
    Flags:
      -h, --help    Show context-sensitive help.
    
    Commands:
      a sub-cmd-a    Subcommand A
      a sub-cmd-b    Subcommand B
      b sub-cmd-a    Subcommand A
      b sub-cmd-b    Subcommand B
    
    Run "app <command> --help" for more information on a command.
    
    opened by virtuald 2
  • Bool type doesn't work for arguments

    Bool type doesn't work for arguments

    package main
    
    import "github.com/alecthomas/kong"
    
    var CLI struct {
    	Rm struct {
    		Force bool `arg:"" help:"Force removal."`
    	} `cmd help:"Remove files."`
    }
    
    func main() {
    	ctx := kong.Parse(&CLI)
    	switch ctx.Command() {
    	case "rm <path>":
    	default:
    		panic(ctx.Command())
    	}
    }
    
    go run main.go rm true
    main: error: unexpected argument true
    exit status 1
    
    opened by krasi-georgiev 5
  • Embedded struct with prefix does not work well with `kong.ConfigFlag`

    Embedded struct with prefix does not work well with `kong.ConfigFlag`

    The following examples:

    package main
    
    import (
            "fmt"
    
            "github.com/alecthomas/kong"
    )
    
    func main() {
            var cli1 struct {
                    Module struct {
                            ConfigFile kong.ConfigFlag
                            Val1       int
                            Val2       int
                    } `embed:"" prefix:"module."`
                    // other params
            }
    
            kong.Parse(&cli1, kong.Configuration(kong.JSON))
    
            fmt.Println(cli1)
    
            var cli2 struct {
                    Module struct {
                            ConfigFile kong.ConfigFlag
                            Module     struct {
                                    Val1 int
                                    Val2 int
                            } `embed:"" prefix:"module."`
                    } `embed:"" prefix:"module."`
                    // other params
            }
            kong.Parse(&cli2, kong.Configuration(kong.JSON))
    
            fmt.Println(cli2)
    }
    

    With a config file that looks like

    {
        "val1": 1,
        "val2": 2
    }
    

    When run with the command go run main.go --module.config-file ./config.json

    Will output:

    {{./config.json 0 0}}
    {{./config.json {0 0}}}
    

    Am I doing something wrong ? Or this is a bug/side effect ?

    opened by midnightexigent 1
  • Usage shouldn't be printed for logic errors

    Usage shouldn't be printed for logic errors

    I've noticed that the usage confusingly gets printed when valid flags happen to fail some logic validation (as opposed to an actual parsing error). For instance:

    var cli struct {
        Dir string `type:"existingdir"`
    }
    
    kong.Parse(&cli, kong.UsageOnError())
    

    When passing --dir=/some/path/that/does/not/exist to the cli, the full usage gets printed, which not only distracts from the actual error message, but doesn't really make sense in my opinion since we didn't violate the command syntax. This also happens for *os.File fields when the file doesn't exist.

    Perhaps Kong.Parse should return a distinct error from ParseError for errors that aren't quite related to parsing but more about validation? Maybe have it so if a MapperFunc returns an error of a specific type, it would not get wrapped in a ParseError?

    opened by Snaipe 1
  • Support multiple ENV variables

    Support multiple ENV variables

    It would be useful to support multiple ENV variables at once separated by commas.

    Example:

    type Example struct {
        Port int `env:"APPLICATION_PORT,PORT" default:"8080"`
    }
    

    So use can provide the port argument with multiple environment variables.

    Thank you.

    opened by shellbear 1
  • add native context support to Run methods

    add native context support to Run methods

    Currently you can manually bind a context.Context to a kong.Context, see #48, but this is not as turnkey as I would have hoped. Would it be possible to add an automatic bind, assuming one is not passed to override, for context.Context that is cancelled when kong.Context.Run returns?

    In the override case, I can see arguments for and against, but it may be useful to subcontext via context.WithCancel and explicitly cancel. Need to think through the implications on this one more.

    Happy to contribute such a fix, but I thought I would ask if this was wanted first.

    opened by carnott-snap 3
  • Bash completion does not work with sudo

    Bash completion does not work with sudo

    output like this:

    bash: compgen: warning: -C option may not work as you expect
    error: unexpected argument compgen
    
    opened by nsemikov 0
  • Hook ignored on nodes tagged with

    Hook ignored on nodes tagged with "embed"

    In the following example I created a struct called Common with an AfterApply hook. When this structure is embedded using Go struct embedding, the AfterApply hook gets invoked as per Go method discovery rules.

    However, if the struct is embedded using the kong embedding (via the embed tag), the hook is not invoked.

    I think that would be useful since this allows multiple structures with common flags to be embedded and have each of their hooks invoked. This is currently not possible with Go embedding since it doesn't work if more than one structure (including the containing struct) defines the hook method.

    package main
    
    import (
    	"fmt"
    
    	"github.com/alecthomas/kong"
    )
    
    type Common struct {
    	Foo bool `help:"FOO"`
    }
    
    func (*Common) AfterApply() error {
    	fmt.Printf("called Common.AfterApply\n")
    	return nil
    }
    
    var CLI struct {
    	Rm struct {
    		Common
    
    		Force     bool `help:"Force removal."`
    		Recursive bool `help:"Recursively remove files."`
    
    		Paths []string `arg name:"path" help:"Paths to remove." type:"path"`
    	} `cmd help:"Remove files."`
    
    	Ls struct {
    		C Common `embed:""`
    
    		Paths []string `arg optional name:"path" help:"Paths to list." type:"path"`
    	} `cmd help:"List paths."`
    }
    
    func main() {
    	ctx := kong.Parse(&CLI)
    	switch ctx.Command() {
    	case "rm <path>":
    	case "ls":
    	default:
    		panic(ctx.Command())
    	}
    }
    
    
    opened by mkmik 8
  • Shell completions

    Shell completions

    Would you be open to adding bash and/or zsh completion generation in a manner similar to cobra?

    opened by smlx 4
  • Print usage on input without commands/args/flags

    Print usage on input without commands/args/flags

    In the scenario of app invocation from terminal without any command/flags

    > my-app
    

    I'd expect the same output of:

    > my-app --help
    

    Instead, I have the error about the missing command.

    my-app: error: expected one of "cmd1", "cmd2"
    

    Is there a way/configuration to achieve my expected outcome?

    The closest function I've found is kong.UsageOnError(), except it prints usage help for any error (when for any other type of error I'd expect to just print the specific error).

    opened by lucagrulla 9
Owner
Alec Thomas
Alec Thomas
Query, update and convert data structures from the command line. Comparable to jq/yq but supports JSON, TOML, YAML, XML and CSV with zero runtime dependencies.

dasel Dasel (short for data-selector) allows you to query and modify data structures using selector strings. Comparable to jq / yq, but supports JSON,

Tom Wright 955 Jul 24, 2021
Argparse for golang. Just because `flag` sucks

Golang argparse Let's be honest -- Go's standard command line arguments parser flag terribly sucks. It cannot come anywhere close to the Python's argp

Alexey Kamenskiy 347 Jul 23, 2021
CLI - A package for building command line app with go

Command line interface Screenshot Key features Lightweight and easy to use. Defines flag by tag, e.g. flag name(short or/and long), description, defau

王仕晋 587 Jul 18, 2021
Fully featured Go (golang) command line option parser with built-in auto-completion support.

go-getoptions Go option parser inspired on the flexibility of Perl’s GetOpt::Long. Table of Contents Quick overview Examples Simple script Program wit

David Gamba 36 Jul 6, 2021
HAProxy configuration parser

HAProxy configuration parser autogenerated code if you change types/types.go you need to run go run generate/go-generate.go $(pwd) Contributing For co

haproxytech 60 Jul 21, 2021
CONTRIBUTIONS ONLY: A Go (golang) command line and flag parser

CONTRIBUTIONS ONLY What does this mean? I do not have time to fix issues myself. The only way fixes or new features will be added is by people submitt

Alec Thomas 3.1k Jul 26, 2021
A versatile library for building CLI applications in Go

mow.cli Package cli provides a framework to build command line applications in Go with most of the burden of arguments parsing and validation placed o

Jawher Moussa 741 Jul 18, 2021
A modern UNIX ed (line editor) clone written in Go

ed (the awesome UNIX line editor) ed is a clone of the UNIX command-line tool by the same name ed a line editor that was nortorious for being and most

James Mills 45 May 29, 2021
A Commander for modern Go CLI interactions

Cobra is both a library for creating powerful modern CLI applications as well as a program to generate applications and command files. Cobra is used i

Steve Francia 22.5k Jul 26, 2021
Simplistic interactive filtering tool

peco Simplistic interactive filtering tool NOTE: If you are viewing this on GitHub, this document refers to the state of peco in whatever current bran

null 6.5k Jul 27, 2021
:cherry_blossom: A command-line fuzzy finder

fzf is a general-purpose command-line fuzzy finder. It's an interactive Unix filter for command-line that can be used with any list; files, command hi

Junegunn Choi 38k Jul 25, 2021
An implementation of sed in Go. Just because!

Sed-Go An implementation of sed in Go. Just because! Status Command-Line processing: Done. It accepts '-e', '-f', '-n' and long versions of the same.

rwtodd 101 Jun 9, 2021
Idiomatic Go input parsing with subcommands, positional values, and flags at any position. No required project or package layout and no external dependencies.

Sensible and fast command-line flag parsing with excellent support for subcommands and positional values. Flags can be at any position. Flaggy has no

Eric Greer 725 Jul 23, 2021
Brigodier is a command parser & dispatcher, designed and developed for command lines such as for Discord bots or Minecraft chat commands. It is a complete port from Mojang's "brigadier" into Go.

brigodier Brigodier is a command parser & dispatcher, designed and developed to provide a simple and flexible command framework. It can be used in man

Minekube 10 Jun 19, 2021