Git Product home page Git Product logo

kingpin's Introduction

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 submitting PRs. If you are interested in taking over maintenance and have a history of contributions to Kingpin, please let me know.

Current status. Kingpin is largely feature stable. There hasn't been a need to add new features in a while, but there are some bugs that should be fixed.

Why? I no longer use Kingpin personally (I now use kong). Rather than leave the project in a limbo of people filing issues and wondering why they're not being worked on, I believe this notice will more clearly set expectations.

Kingpin - A Go (golang) command line and flag parser

CI

Overview

Kingpin is a fluent-style, type-safe command-line parser. It supports flags, nested commands, and positional arguments.

Install it with:

$ go get github.com/alecthomas/kingpin/v2

It looks like this:

var (
  verbose = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
  name    = kingpin.Arg("name", "Name of user.").Required().String()
)

func main() {
  kingpin.Parse()
  fmt.Printf("%v, %s\n", *verbose, *name)
}

More examples are available.

Second to parsing, providing the user with useful help is probably the most important thing a command-line parser does. Kingpin tries to provide detailed contextual help if --help is encountered at any point in the command line (excluding after --).

Features

  • Help output that isn't as ugly as sin.
  • Fully customisable help, via Go templates.
  • Parsed, type-safe flags (kingpin.Flag("f", "help").Int())
  • Parsed, type-safe positional arguments (kingpin.Arg("a", "help").Int()).
  • Parsed, type-safe, arbitrarily deep commands (kingpin.Command("c", "help")).
  • Support for required flags and required positional arguments (kingpin.Flag("f", "").Required().Int()).
  • Support for arbitrarily nested default commands (command.Default()).
  • Callbacks per command, flag and argument (kingpin.Command("c", "").Action(myAction)).
  • POSIX-style short flag combining (-a -b -> -ab).
  • Short-flag+parameter combining (-a parm -> -aparm).
  • Read command-line from files (@<file>).
  • Automatically generate man pages (--help-man).

User-visible changes between v1 and v2

Flags can be used at any point after their definition.

Flags can be specified at any point after their definition, not just immediately after their associated command. From the chat example below, the following used to be required:

$ chat --server=chat.server.com:8080 post --image=~/Downloads/owls.jpg pics

But the following will now work:

$ chat post --server=chat.server.com:8080 --image=~/Downloads/owls.jpg pics

Short flags can be combined with their parameters

Previously, if a short flag was used, any argument to that flag would have to be separated by a space. That is no longer the case.

API changes between v1 and v2

  • ParseWithFileExpansion() is gone. The new parser directly supports expanding @<file>.
  • Added FatalUsage() and FatalUsageContext() for displaying an error + usage and terminating.
  • Dispatch() renamed to Action().
  • Added ParseContext() for parsing a command line into its intermediate context form without executing.
  • Added Terminate() function to override the termination function.
  • Added UsageForContextWithTemplate() for printing usage via a custom template.
  • Added UsageTemplate() for overriding the default template to use. Two templates are included:
    1. DefaultUsageTemplate - default template.
    2. CompactUsageTemplate - compact command template for larger applications.

Versions

The current stable version is github.com/alecthomas/kingpin/v2. The previous version, gopkg.in/alecthomas/kingpin.v1, is deprecated and in maintenance mode.

V2 is the current stable version

Installation:

$ go get github.com/alecthomas/kingpin/v2

V1 is the OLD stable version

Installation:

$ go get gopkg.in/alecthomas/kingpin.v1

Change History

  • 2015-09-19 -- Stable v2.1.0 release.

    • Added command.Default() to specify a default command to use if no other command matches. This allows for convenient user shortcuts.
    • Exposed HelpFlag and VersionFlag for further customisation.
    • Action() and PreAction() added and both now support an arbitrary number of callbacks.
    • kingpin.SeparateOptionalFlagsUsageTemplate.
    • --help-long and --help-man (hidden by default) flags.
    • Flags are "interspersed" by default, but can be disabled with app.Interspersed(false).
    • Added flags for all simple builtin types (int8, uint16, etc.) and slice variants.
    • Use app.Writer(os.Writer) to specify the default writer for all output functions.
    • Dropped os.Writer prefix from all printf-like functions.
  • 2015-05-22 -- Stable v2.0.0 release.

    • Initial stable release of v2.0.0.
    • Fully supports interspersed flags, commands and arguments.
    • Flags can be present at any point after their logical definition.
    • Application.Parse() terminates if commands are present and a command is not parsed.
    • Dispatch() -> Action().
    • Actions are dispatched after all values are populated.
    • Override termination function (defaults to os.Exit).
    • Override output stream (defaults to os.Stderr).
    • Templatised usage help, with default and compact templates.
    • Make error/usage functions more consistent.
    • Support argument expansion from files by default (with @).
    • Fully public data model is available via .Model().
    • Parser has been completely refactored.
    • Parsing and execution has been split into distinct stages.
    • Use go generate to generate repeated flags.
    • Support combined short-flag+argument: -fARG.
  • 2015-01-23 -- Stable v1.3.4 release.

    • Support "--" for separating flags from positional arguments.
    • Support loading flags from files (ParseWithFileExpansion()). Use @FILE as an argument.
    • Add post-app and post-cmd validation hooks. This allows arbitrary validation to be added.
    • A bunch of improvements to help usage and formatting.
    • Support arbitrarily nested sub-commands.
  • 2014-07-08 -- Stable v1.2.0 release.

    • Pass any value through to Strings() when final argument. Allows for values that look like flags to be processed.
    • Allow --help to be used with commands.
    • Support Hidden() flags.
    • Parser for units.Base2Bytes type. Allows for flags like --ram=512MB or --ram=1GB.
    • Add an Enum() value, allowing only one of a set of values to be selected. eg. Flag(...).Enum("debug", "info", "warning").
  • 2014-06-27 -- Stable v1.1.0 release.

    • Bug fixes.
    • Always return an error (rather than panicing) when misconfigured.
    • OpenFile(flag, perm) value type added, for finer control over opening files.
    • Significantly improved usage formatting.
  • 2014-06-19 -- Stable v1.0.0 release.

    • Support cumulative positional arguments.
    • Return error rather than panic when there are fatal errors not caught by the type system. eg. when a default value is invalid.
    • Use gokpg.in.
  • 2014-06-10 -- Place-holder streamlining.

    • Renamed MetaVar to PlaceHolder.
    • Removed MetaVarFromDefault. Kingpin now uses heuristics to determine what to display.

Examples

Simple Example

Kingpin can be used for simple flag+arg applications like so:

$ ping --help
usage: ping [<flags>] <ip> [<count>]

Flags:
  --debug            Enable debug mode.
  --help             Show help.
  -t, --timeout=5s   Timeout waiting for ping.

Args:
  <ip>        IP address to ping.
  [<count>]   Number of packets to send
$ ping 1.2.3.4 5
Would ping: 1.2.3.4 with timeout 5s and count 5

From the following source:

package main

import (
  "fmt"

  "github.com/alecthomas/kingpin/v2"
)

var (
  debug   = kingpin.Flag("debug", "Enable debug mode.").Bool()
  timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").Envar("PING_TIMEOUT").Short('t').Duration()
  ip      = kingpin.Arg("ip", "IP address to ping.").Required().IP()
  count   = kingpin.Arg("count", "Number of packets to send").Int()
)

func main() {
  kingpin.Version("0.0.1")
  kingpin.Parse()
  fmt.Printf("Would ping: %s with timeout %s and count %d\n", *ip, *timeout, *count)
}

Reading arguments from a file

Kingpin supports reading arguments from a file. Create a file with the corresponding arguments:

echo -t=5\n > args

And now supply it:

$ ping @args

Complex Example

Kingpin can also produce complex command-line applications with global flags, subcommands, and per-subcommand flags, like this:

$ chat --help
usage: chat [<flags>] <command> [<flags>] [<args> ...]

A command-line chat application.

Flags:
  --help              Show help.
  --debug             Enable debug mode.
  --server=127.0.0.1  Server address.

Commands:
  help [<command>]
    Show help for a command.

  register <nick> <name>
    Register a new user.

  post [<flags>] <channel> [<text>]
    Post a message to a channel.

$ chat help post
usage: chat [<flags>] post [<flags>] <channel> [<text>]

Post a message to a channel.

Flags:
  --image=IMAGE  Image to post.

Args:
  <channel>  Channel to post to.
  [<text>]   Text to post.

$ chat post --image=~/Downloads/owls.jpg pics
...

From this code:

package main

import (
  "os"
  "strings"
  "github.com/alecthomas/kingpin/v2"
)

var (
  app      = kingpin.New("chat", "A command-line chat application.")
  debug    = app.Flag("debug", "Enable debug mode.").Bool()
  serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP()

  register     = app.Command("register", "Register a new user.")
  registerNick = register.Arg("nick", "Nickname for user.").Required().String()
  registerName = register.Arg("name", "Name of user.").Required().String()

  post        = app.Command("post", "Post a message to a channel.")
  postImage   = post.Flag("image", "Image to post.").File()
  postChannel = post.Arg("channel", "Channel to post to.").Required().String()
  postText    = post.Arg("text", "Text to post.").Strings()
)

func main() {
  switch kingpin.MustParse(app.Parse(os.Args[1:])) {
  // Register user
  case register.FullCommand():
    println(*registerNick)

  // Post message
  case post.FullCommand():
    if *postImage != nil {
    }
    text := strings.Join(*postText, " ")
    println("Post:", text)
  }
}

Reference Documentation

Displaying errors and usage information

Kingpin exports a set of functions to provide consistent errors and usage information to the user.

Error messages look something like this:

<app>: error: <message>

The functions on Application are:

Function Purpose
Errorf(format, args) Display a printf formatted error to the user.
Fatalf(format, args) As with Errorf, but also call the termination handler.
FatalUsage(format, args) As with Fatalf, but also print contextual usage information.
FatalUsageContext(context, format, args) As with Fatalf, but also print contextual usage information from a ParseContext.
FatalIfError(err, format, args) Conditionally print an error prefixed with format+args, then call the termination handler

There are equivalent global functions in the kingpin namespace for the default kingpin.CommandLine instance.

Sub-commands

Kingpin supports nested sub-commands, with separate flag and positional arguments per sub-command. Note that positional arguments may only occur after sub-commands.

For example:

var (
  deleteCommand     = kingpin.Command("delete", "Delete an object.")
  deleteUserCommand = deleteCommand.Command("user", "Delete a user.")
  deleteUserUIDFlag = deleteUserCommand.Flag("uid", "Delete user by UID rather than username.")
  deleteUserUsername = deleteUserCommand.Arg("username", "Username to delete.")
  deletePostCommand = deleteCommand.Command("post", "Delete a post.")
)

func main() {
  switch kingpin.Parse() {
  case deleteUserCommand.FullCommand():
  case deletePostCommand.FullCommand():
  }
}

Custom Parsers

Kingpin supports both flag and positional argument parsers for converting to Go types. For example, some included parsers are Int(), Float(), Duration() and ExistingFile() (see parsers.go for a complete list of included parsers).

Parsers conform to Go's flag.Value interface, so any existing implementations will work.

For example, a parser for accumulating HTTP header values might look like this:

type HTTPHeaderValue http.Header

func (h *HTTPHeaderValue) Set(value string) error {
  parts := strings.SplitN(value, ":", 2)
  if len(parts) != 2 {
    return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
  }
  (*http.Header)(h).Add(parts[0], parts[1])
  return nil
}

func (h *HTTPHeaderValue) String() string {
  return ""
}

As a convenience, I would recommend something like this:

func HTTPHeader(s Settings) (target *http.Header) {
  target = &http.Header{}
  s.SetValue((*HTTPHeaderValue)(target))
  return
}

You would use it like so:

headers = HTTPHeader(kingpin.Flag("header", "Add a HTTP header to the request.").Short('H'))

Repeatable flags

Depending on the Value they hold, some flags may be repeated. The IsCumulative() bool function on Value tells if it's safe to call Set() multiple times or if an error should be raised if several values are passed.

The built-in Values returning slices and maps, as well as Counter are examples of Values that make a flag repeatable.

Boolean values

Boolean values are uniquely managed by Kingpin. Each boolean flag will have a negative complement: --<name> and --no-<name>.

Default Values

The default value is the zero value for a type. This can be overridden with the Default(value...) function on flags and arguments. This function accepts one or several strings, which are parsed by the value itself, so they must be compliant with the format expected.

Place-holders in Help

The place-holder value for a flag is the value used in the help to describe the value of a non-boolean flag.

The value provided to PlaceHolder() is used if provided, then the value provided by Default() if provided, then finally the capitalised flag name is used.

Here are some examples of flags with various permutations:

--name=NAME           // Flag(...).String()
--name="Harry"        // Flag(...).Default("Harry").String()
--name=FULL-NAME      // Flag(...).PlaceHolder("FULL-NAME").Default("Harry").String()

Consuming all remaining arguments

A common command-line idiom is to use all remaining arguments for some purpose. eg. The following command accepts an arbitrary number of IP addresses as positional arguments:

./cmd ping 10.1.1.1 192.168.1.1

Such arguments are similar to repeatable flags, but for arguments. Therefore they use the same IsCumulative() bool function on the underlying Value, so the built-in Values for which the Set() function can be called several times will consume multiple arguments.

To implement the above example with a custom Value, we might do something like this:

type ipList []net.IP

func (i *ipList) Set(value string) error {
  if ip := net.ParseIP(value); ip == nil {
    return fmt.Errorf("'%s' is not an IP address", value)
  } else {
    *i = append(*i, ip)
    return nil
  }
}

func (i *ipList) String() string {
  return ""
}

func (i *ipList) IsCumulative() bool {
  return true
}

func IPList(s Settings) (target *[]net.IP) {
  target = new([]net.IP)
  s.SetValue((*ipList)(target))
  return
}

And use it like so:

ips := IPList(kingpin.Arg("ips", "IP addresses to ping."))

Bash/ZSH Shell Completion

By default, all flags and commands/subcommands generate completions internally.

Out of the box, CLI tools using kingpin should be able to take advantage of completion hinting for flags and commands. By specifying --completion-bash as the first argument, your CLI tool will show possible subcommands. By ending your argv with --, hints for flags will be shown.

To allow your end users to take advantage you must package a /etc/bash_completion.d script with your distribution (or the equivalent for your target platform/shell). An alternative is to instruct your end user to source a script from their bash_profile (or equivalent).

Fortunately Kingpin makes it easy to generate or source a script for use with end users shells. ./yourtool --completion-script-bash and ./yourtool --completion-script-zsh will generate these scripts for you.

Installation by Package

For the best user experience, you should bundle your pre-created completion script with your CLI tool and install it inside /etc/bash_completion.d (or equivalent). A good suggestion is to add this as an automated step to your build pipeline, in the implementation is improved for bug fixed.

Installation by bash_profile

Alternatively, instruct your users to add an additional statement to their bash_profile (or equivalent):

eval "$(your-cli-tool --completion-script-bash)"

Or for ZSH

eval "$(your-cli-tool --completion-script-zsh)"

Additional API

To provide more flexibility, a completion option API has been exposed for flags to allow user defined completion options, to extend completions further than just EnumVar/Enum.

Provide Static Options

When using an Enum or EnumVar, users are limited to only the options given. Maybe we wish to hint possible options to the user, but also allow them to provide their own custom option. HintOptions gives this functionality to flags.

app := kingpin.New("completion", "My application with bash completion.")
app.Flag("port", "Provide a port to connect to").
    Required().
    HintOptions("80", "443", "8080").
    IntVar(&c.port)

Provide Dynamic Options Consider the case that you needed to read a local database or a file to provide suggestions. You can dynamically generate the options

func listHosts() []string {
  // Provide a dynamic list of hosts from a hosts file or otherwise
  // for bash completion. In this example we simply return static slice.

  // You could use this functionality to reach into a hosts file to provide
  // completion for a list of known hosts.
  return []string{"sshhost.example", "webhost.example", "ftphost.example"}
}

app := kingpin.New("completion", "My application with bash completion.")
app.Flag("flag-1", "").HintAction(listHosts).String()

EnumVar/Enum When using Enum or EnumVar, any provided options will be automatically used for bash autocompletion. However, if you wish to provide a subset or different options, you can use HintOptions or HintAction which will override the default completion options for Enum/EnumVar.

Examples You can see an in depth example of the completion API within examples/completion/main.go

Supporting -h for help

kingpin.CommandLine.HelpFlag.Short('h')

Short help is also available when creating a more complicated app:

var (
	app = kingpin.New("chat", "A command-line chat application.")
  // ...
)

func main() {
	app.HelpFlag.Short('h')
	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
  // ...
  }
}

Custom help

Kingpin v2 supports templatised help using the text/template library (actually, a fork).

You can specify the template to use with the Application.UsageTemplate() function.

There are four included templates: kingpin.DefaultUsageTemplate is the default, kingpin.CompactUsageTemplate provides a more compact representation for more complex command-line structures, kingpin.SeparateOptionalFlagsUsageTemplate looks like the default template, but splits required and optional command flags into separate lists, and kingpin.ManPageTemplate is used to generate man pages.

See the above templates for examples of usage, and the the function UsageForContextWithTemplate() method for details on the context.

Default help template

$ go run ./examples/curl/curl.go --help
usage: curl [<flags>] <command> [<args> ...]

An example implementation of curl.

Flags:
  --help            Show help.
  -t, --timeout=5s  Set connection timeout.
  -H, --headers=HEADER=VALUE
                    Add HTTP headers to the request.

Commands:
  help [<command>...]
    Show help.

  get url <url>
    Retrieve a URL.

  get file <file>
    Retrieve a file.

  post [<flags>] <url>
    POST a resource.

Compact help template

$ go run ./examples/curl/curl.go --help
usage: curl [<flags>] <command> [<args> ...]

An example implementation of curl.

Flags:
  --help            Show help.
  -t, --timeout=5s  Set connection timeout.
  -H, --headers=HEADER=VALUE
                    Add HTTP headers to the request.

Commands:
  help [<command>...]
  get [<flags>]
    url <url>
    file <file>
  post [<flags>] <url>

kingpin's People

Contributors

alecthomas avatar bbkane avatar bengadbois avatar davidmz avatar github-brice-jaglin avatar heewa avatar hupfdule avatar jpcoenen avatar kyoh86 avatar lalloni avatar lmb avatar magnushiie avatar mlitvin avatar mrueg avatar muesli avatar nickbp avatar nthnca avatar nyxtom avatar omerlh avatar pothix avatar purple4reina avatar raphael avatar renovate[bot] avatar simonbarendse avatar stapelberg avatar szank avatar yonpols avatar zan-xhipe avatar zealws avatar zoni avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kingpin's Issues

Support optional Arg

I have a need for an optional argument or two.

This program would behave differently based on whether it has 0, 1, or 2 arguments. I don't see a way to handle that cleanly in kingpin. I suspect that is because of the multi-level command support.

Could we add OptionalArg which cannot be followed by anything else (except another OptionalArg)?

  debug =       kingpin.Flag("debug", "Enable debug mode.").Bool(),
  flagged =     kingpin.Flag("flagged", "Enable flagged mode. (make each key name as to whether it came from vault (v) or consul (c)").Bool(),
  productGroup = kingpin.OptionalArg("product-group", "product group to list.").String(),
  template_file = kingpin.OptionalArg("template-filename", "filename of the template file to verify.").String(),

ehj
ehj group
ehj group template_file

Improve documentation for Dispatch() and FullCommand()

I wanted to define my kingpin commands with a pattern where adding a new command only required adding new code (i.e. not modifying an existing switch statement), and also I didn't need to duplicate a string constant for my command name.

I propose additions to the kingpin API to support a similar pattern along the lines of this example:

// my-app.go:
var app kingpin.Application = kingpin.New("my-app", "My app")a

func main() {
    cmd, err := app.Parse(os.Args[1:])
    if err != nil || cmd == "" {
        app.Usage(os.Stderr)
        return
    }

    app.RunAction(cmd)
}
// my_job.go:
func init() {
    cmd := app.Command("greet", "Greet a user")
    argGreetName := cmd.Arg("greet-name", "Name of user to greet").Required().String()
    cmd.Action(func() {
        fmt.Printf("Hello, %s!", *argGreetName)
    })
}

The new kingpin interfaces could be:

// Set the action function for a command
func (*CmdClause) Action(action CommandAction) {}

type CommandAction func(*ActionContext) (interface{}, error)

type ActionContext struct {
    Application *Application
    Command *CmdClause
    // Other stuff here?
}

// Returns whatever values the CommandAction returned.
// cmdName should be a result from Application.Parse()
// Panics if cmdName isn't the name of a registered command, as this is a programmer error.
func (*Application) RunAction(command string) (interface{}, error) {}

// Parses arguments and prints usage on parse error or runs RunAction.
// Other possible names for this function: Run, ParseAndRun, DefaultRun.
func (*Application) ParseAndRunAction(args []string) (interface{}, error) {}

Any thoughts?

Happy to write this up as a PR if people like the idea.

Distinguishing between required and optional flags in default usage template

Hi,
We have a cli application with many (~35) flags. This amount of flags is unfortunately necessary.
Using the default help template the usage line doesn't display optional flags. It would be nice to print them in brackets, like in man pages :
usage : app command --required-flag1 --required-flag-2 ( --optional-flag-1 )

also in the flag section there is no differentiation between required and optional flags. It would be nice if you could add this.

Last question :
Would it be possible to add another template, which would print usage section like this :
usage : app command --flag-1
--flag-2
--flag-3
--flag-4 \

this would greatly increase readability for applications with many flags ( like ours )

Best regards,
Maciej

Strings() doesn't work with flags

According to https://godoc.org/gopkg.in/alecthomas/kingpin.v2#FlagClause.Strings a flag should allow the type to be a list of strings:

func (p *FlagClause) Strings() (target *[]string)

Strings accumulates string values into a slice.

Using the following code:

package main

import (
    "gopkg.in/alecthomas/kingpin.v2"
    "os"
)

var (
    app         = kingpin.New("my-app", "It does cool things!")
    someStrings = app.Flag("strings", "Give me some strings baby!").Short('s').Strings()
)

func main() {
    kingpin.MustParse(app.Parse(os.Args[1:]))
}

And running the test app like so: ./test -s hello world

I get the following error: test: error: unexpected world, try --help

This is the help output:

usage: my-app [<flags>]

It does cool things!

Flags:
  --help  Show help (also see --help-long and --help-man).
  -s, --strings=STRINGS
          Give me some strings baby!

Make help less busy by removing angle brackets

Currently help prints text like:

usage: chat [<flags>] <command> [<flags>] [<args> ...]

All the angle brackets make it hard to read. I suggest something simpler like:

usage: chat [options] command [command options] [arguments...]

Installation error

$ go get gopkg.in/alecthomas/kingpin.v1

shows

# gopkg.in/alecthomas/kingpin.v1
../../../gopkg.in/alecthomas/kingpin.v1/global.go:46: CommandLine.ParseWithFileExpansion undefined (type *Application has no field or method ParseWithFileExpansion)
../../../gopkg.in/alecthomas/kingpin.v1/global.go:46: not enough arguments in call to MustParse

go versions 1.3.3 and 1.4.1

I want to tell if an option was set

I'm using this style of setup:

    update := servers.Command("update", "Update a new cloud server").Action(cmd.update)
    update.Arg("identifier", "Identifier of servers to update").Required().StringVar(&cmd.Id)
    update.Flag("name", "New name to give server").Short('n').StringVar(&cmd.Name)

But I need to know if the name option was actually provided or not - I can't just check that cmd.Name is empty or not (because the user might be trying to delete the name by providing an empty string).

If I could pass a pointer to a string into StringVar, I could check if it's nil or not, but that's not currently supported.

I could implement my own value type for it I suppose, but it seems like a common requirement - is there a way to do this? If not, should kingpin support it? Should it support the same for all types?

--no-interaction flag is not recognized

I want to have --no-interaction flag in my program so I created the following code:

package main

import (
    "fmt"
    "os"

    "gopkg.in/alecthomas/kingpin.v2"
)

var (
    app           = kingpin.New("Test", "")
    foobar        = app.Flag("foo-bar", "A flag containing a minus").Default("false").Bool()
    noInteraction = app.Flag("no-interaction", "Do not ask for any user input").Default("false").Bool()
)

func main() {
    kingpin.MustParse(app.Parse(os.Args[1:]))
    fmt.Printf("Foobar: %+v\n", *foobar)
    fmt.Printf("noInteraction: %+v\n", *noInteraction)
}

When I run this with go run example.go --foo-bar it works as intendended but when I want to use the no-interaction flag kingpin doesnt recognize the flag even though it is listed in go run example.go --help

$ go run example.go --help
usage: Test [<flags>]

Flags:
  --help            Show help (also see --help-long and --help-man).
  --foo-bar         A flag containing a minus
  --no-interaction  Do not ask for any user input

exit status 1
$ go run example.go --no-interaction
example: error: unknown long flag '--no-interaction', try --help
exit status 1

Am I doing something wrong?

Arg Strings() attempts to parse

My understanding of the following:

jobId = kingpin.Arg("jobid", "").String()
argString = kingpin.Arg("argstring", "").Strings()

Would allow you to pass argString with strings that look like normal args. Yet I seem to be running into an issue:

bin/rundeck-run-job 41972a2d-5929-464e-9ba2-fa30cc3591f8 -aws_secret_key ${AWS_SECRET_ACCESS_KEY} -orgname jv-test-org -aws_access_key ${AWS_ACCESS_KEY_ID} 
rundeck-run-job: error: unknown short flag '-a', try --help

The Rundeck API for calling jobs, takes arguments in a format that look like arguments (http://rundeck.org/docs/api/#running-a-job)

Am I mistaken that my above example SHOULD work or am I using it wrong?

How to change help text?

This is more like a question (but maybe a feature request as well).

I am new to Go and very new to Kingpin. I'm about to re-create a simple CLI app in Go which I originally written some time ago in Node.js. (I do not need subcommands for my simple CLI app.) I'm progressing well.
However there are a few customization I would like to make to Kingpin. These are:

  1. I prefer to have -v, --version with the text Version information instead of --version with the text Show application version.
  2. I prefer to have -h, --help with the text Display help and usage details instead of --help with the text Show help (also see --help-long and --help-man). I don't need --help-long and --help-man at all, I just need -h, --help with customized description.

So the first one above I managed to solve by replacing

  kingpin.Version("0.0.1")

to this:

  kingpin.Flag("version", "Version information").Short('v').Action(func (*kingpin.ParseContext) error {
    fmt.Println("0.0.1")
    os.Exit(0)
    return nil
  }).Bool()

This works perfectly the way I want. I can now use the -v flag or --version flag and both print out the version info. (Note: The solution to this was not documented at all, I managed to come up with this solution by investigating the source code of Kingpin.)

But the second one (changing --help to -h, --help and its description) I cannot figure out.

Is there any way I can customize the default help flag? Especially in a simple CLI app (an app with no subcommands)?

Please let me know,

Thanks!

TestParseExistingFile fails on Windows

--- FAIL: TestParseExistingFile (0.00s)
Location: parsers_test.go:57
Error: No error is expected but got path '/etc/hosts' does not exist

    Location:       parsers_test.go:58
    Error:          Not equal: "/etc/hosts" (expected)
                            != "" (actual)

Help Message with Nested Commands

When there are many nested subcommands the Usage information is huge and impossible to understand.
I was thinking of a different approach to Usage information and I would like to propose it.
I think it Application Usage could be greatly simplified by making Usage print only the first set of available commands.
Then, It would be great too if when specifying a correct first command but no subsequent subcommands we could show the Usage for that specific command instead of the whole app.
And so on, instead of having the Application print every possible combination of commands, we could just show the next step in the tree of combinations.

Instead of:
app would print:

app
Flags:
Commands:
delete user [<flags>]
delete post [<flags>]

For example:
app would print:

app
Flags:
Commands:
delete <command>

app delete would print:

app delete
Flags:

Commands:
user [<flags>]
post [<flags>]

I dont know if i make myself clear.
Hope you like my proposal
Cheers!

No Float32 option.

current float parser forces float64.
should be split into two separate parsers.

Float32Var(var * float32) and Float64(var * float64)

Same for int32 holds true for int32 etc.

Problem with actions

Hi, this is a simple program that doesn't seem to work as intended :
I have checked out v2.0.8 tag

package main

import (
    "fmt"
    "os"

    "gopkg.in/alecthomas/kingpin.v2"
)

func main() {
    app := kingpin.New("test", "testApp").Action(action).PreAction(preAction).Terminate(terminate)

    fmt.Println(app.Parse(os.Args[1:]))

}

func action(c *kingpin.ParseContext) error {
    fmt.Println("ACTION")
    return nil
}

func preAction(c *kingpin.ParseContext) error {
    fmt.Println("PRE ACTION")
    return nil
}

func terminate(code int) {
    fmt.Println("TERMINATE")
    os.Exit(code)
}

returns

ACTION
ACTION
 <nil>

Positional arguments can't be before flags

I want to be able to have:

executable build myproject:123 <flags>

right now, the myproject:123 is registered as an Arg() and is always placed at the end of the flags. It makes no sense for the users of my CLI tool to put the project name at the end of the flags. I need to at least support it at the beginning, if not anywhere when considered as a positional argument.

Is that possible, how complex would it be to implement ? I was thinking at an option on the Command() to mark it as not having any sub-commands, so it can handle positional arguments before the flags. What do you think ?

flag parsing prevents using "-" or @FILE in conventional ways

I'm a first-time Kingpin user and I love it so far!

Unix programs often allow users to specify - to read from stdin, or allow values to be read from files using @filename syntax. kingpin returns a parse error when - is passed as a positional argument, and doesn't allow a curl-style use of @- because the @ anywhere on the command line appears to read command line arguments from a file.

The currently implemented behavior is just different enough from expectations to have caused me grief.

Feature requests:

  • Allow flags and positional arguments to receive strings prefixed with @ as arguments, as opposed to loading command line flags. For example, if I'm writing a key-value store, it should allow ./main put key @valuefile.txt to store the contents of valuefile.txt rather than have its contents treated as a command line.
  • Refine parser to not treat a single - as the beginning of a command line flag.

Thanks!

Required Flags

If I get a chance to add this I will do so and file a merge request, in the meantime:

I have a use case where there are a set of 4 flags, with the following names:

  1. IDA
  2. IDV
  3. ODIN
  4. UM5

At a bare minimum, ONE of the above flags is required. It would be great to be able to have a group of flags where one flag is required.

Make usage functions consistent

Some functions exit, some don't. Some write directly to os.Stderr, some don't. Come up with a way to make this consistent.

For example, the "help" command writes to os.Stderr with no way to modify the destination. Perhaps being able to specify the destination io.Writer via a function?

Ability to make a subcommand the default if none specified

Right now, if you have a Command added to your kingpin.New() app, and no sub-command is specified at runtime, usage is printed and the program aborts.

My attempt to achieve this with the below failed:

var (
    appCommand          = kingpin.New("myapp", "Yum yum yum.")
    mySubcommand = appCommand.Command("mysubcommand", "Frob the bars")
)

func main() {
    switch kingpin.MustParse(appCommand.Parse(os.Args[1:])) {
    case mySubcommand():
                log.Print("my subcommand was selected")
    default:
        // default action - alas, usage is printed and we never make it here
                log.Print("my default action")
    }
}

If a flag is not Required(), it does not show up in main command --help output

If a flag is not Required(), it does not show up in main command --help output; it only shows up in the --help for that specific sub-command. Can this behavior be controlled (I would prefer to have the full flags list in the help at the top level, detail on each flag would still remain in the sub-command specific help)

Can't figure out how to use EnumVar

I'm trying to use EnumVar but can't figure out the interface and can't find an example for it.

create := token.Command("create", "create a token").Action(cmd.create)
create.Flag("format", "the output format: text, json or curl").Default("text").EnumVar(&cmd.Format, "text", "json", "curl")

I'm a bit of a Go newbie so I assume I'm just missing something, but I think an example in the docs would be useful to others!

Bool flag always false

Here's my code:

var (
    stress = kingpin.Flag("stress", "Stress Test Mode").Bool()
)

func main() {
    kingpin.Parse()
    fmt.Printf("stress: %v\n", *stress)
}

And running it:

$ go run main.go --stress
stress: false

I'm expecting stress to be true, since I'm passing the --stress flag. What am I doing wrong?

Command/Flag Aliasing

It'd be great if we could alias commands and/or flags. For example, something like:

someFlag = app.Flag("v", "Be verbose!").Bool()
app.Flag("verbose").alias(someFlag)

Short flags for help and version

It would be nice if kingpin supported short flags for the builtin --helpand --version flags. The only reason I see not is it might collide with user added -h or -v but couldn't we check for those?

If not automatically having them how can I set them myself?

Support unambiguous arbitrarily ordered flags

Is it possible to support flags in any order? For example:

clitool subcommand arg

clitool subcommand --help arg

clitool subcommand arg --help

Reasoning; with complex commands that have many or long arguments it's fairly annoying to have to seek back to before the arguments and add a flag. Especially if that flag is --help.

I believe flags can be parsed unambiguously so they could technically be mixed with args, but I know this is probably a complex topic.

Negative numbers in int positional arguments

Love the library, man. Thank you for your contribution!

You might wanna add getopt-like double dash (--) behavior so that negative numbers can be used in positional arguments. I tried to do that today in 1.3.2, but it assumes I'm trying to specify a flag. My example had a string positional argument and an Int64 positional argument, like so:

% cmd "my string" -123
error: unexpected arguments '-1 -2 -3', try --help

I was hoping to get -123 as an Int64.

With getopt, I think you can do this and have it work correctly:

% cmd -- "my string" -123

Basically the -- by itself means "everything after this is not a flag".

Showing command hint on error is much harder in v2

In version 2 some of the base functionality has been un-exported. Instead of showing the entire usage when a command fails I wrote a function to show just the subcommand help if we could understand the command that far. This was the code:

func generateHint(app *kingpin.Application, args []string) (string, error) {
    output := bytes.NewBuffer(nil)
    context := kingpin.Tokenize(args)

    token := &kingpin.Token{0, ""}
    for token.Type != kingpin.TokenEOL {
        token = context.Peek()
        if token.Type == kingpin.TokenArg {
            if _, ok := commands[token.String()]; ok {
                app.CommandUsage(output, token.String())
                return output.String(), nil
            } else {
                // If we hit a real arg and it isn't a sub-command exit.
                break
            }
        } else {
            // Skip one additional time to pass over Flag value
            context.Next()
        }
        context.Next()
    }

    return "", fmt.Errorf("No hints")
}

I don't know if this is the right way to do this, but that is no longer possible. Since our command has about 12 subcommands it's very irritating to see the entire help tree when really they're struggling with a single command.

Parse and Execute in separated steps?

Is it possible to do something like this?

I'd like to use Dispatch/Action but I would like to initialize some stuff first (config file, checks, etc), before actually dispatching the func.

This way I could parse, get, work with the values of flags, etc, initialize my stuff, and then execute. Currently I can do that with the switch, but it would be much easier to just use dispatch if this was possible.

Or maybe a .Before() and .After() that would be called before/after Dispatch. That would be awesome aswell.

Doable?

Unexpected crash

Hi, I was playing with actions a bit more today (on commit d447053 )
I run this app :

package main

import (
    "fmt"
    "os"

    "gopkg.in/alecthomas/kingpin.v2"
)

func main() {
    app := kingpin.New("test", "testApp").Action(action)
    app.Parse(os.Args[1:])

}

func action(c *kingpin.ParseContext) error {
    fmt.Println("ACTION")
    return nil
}

and got this :

 $ go run main.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x602c3]

goroutine 1 [running]:
gopkg.in/alecthomas/kingpin%2ev2.(*Application).applyPreActions(0xc20806a8c0, 0xc208030200, 0x0, 0x0)
    /Users/mgalkowski/work/go/src/gopkg.in/alecthomas/kingpin.v2/app.go:462 +0x63
gopkg.in/alecthomas/kingpin%2ev2.(*Application).execute(0xc20806a8c0, 0xc208030200, 0x0, 0x0, 0x0, 0x0)
    /Users/mgalkowski/work/go/src/gopkg.in/alecthomas/kingpin.v2/app.go:312 +0x16e
gopkg.in/alecthomas/kingpin%2ev2.(*Application).Parse(0xc20806a8c0, 0xc20800a000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
    /Users/mgalkowski/work/go/src/gopkg.in/alecthomas/kingpin.v2/app.go:134 +0x273
main.main()
    /Users/mgalkowski/work/go/src/bitbucket.org/globalsign/restfulca/tools/testTool/main.go:12 +0xd5

goroutine 2 [runnable]:
runtime.forcegchelper()
    /usr/local/Cellar/go/1.4.2/libexec/src/runtime/proc.go:90
runtime.goexit()
    /usr/local/Cellar/go/1.4.2/libexec/src/runtime/asm_amd64.s:2232 +0x1

goroutine 3 [runnable]:
runtime.bgsweep()
    /usr/local/Cellar/go/1.4.2/libexec/src/runtime/mgc0.go:82
runtime.goexit()
    /usr/local/Cellar/go/1.4.2/libexec/src/runtime/asm_amd64.s:2232 +0x1

goroutine 4 [runnable]:
runtime.runfinq()
    /usr/local/Cellar/go/1.4.2/libexec/src/runtime/malloc.go:712
runtime.goexit()
    /usr/local/Cellar/go/1.4.2/libexec/src/runtime/asm_amd64.s:2232 +0x1
exit status 2

Any ideas?

App flags forced back to default by command clause flag parse (v2)

Consider this test case:

package main

import (
    "gopkg.in/alecthomas/kingpin.v2-unstable"
    "fmt"
    "os"
)

var (
    app = kingpin.New("test", "Test case.")
    appflag = app.Flag("appflag", "An app flag.").Default("default").String()

    something = app.Command("something", "Some command.")
)

func main() {
    kingpin.MustParse(app.Parse(os.Args[1:]))

    fmt.Printf("App flag is %s\n", *appflag)
}

With v1 it works fine, but with v2-unstable:

$ go run test.go --appflag 123 something
App flag is default

--version not working with .Required() fields

When I add .Version to my kingpin parser, when there are other .Required fields, instead of printing version on --version, it wants to have the required fields. They should not be required for --version.

I noticed there is a test in flags_test.go, but its missing one assertion at the end of the function and that should be failing right now.

default command with arguments is not we want at the most time

I'm pleasure that default command feature is added, however, it seems a problem here.

package main

import (
    "fmt"
    "gopkg.in/alecthomas/kingpin.v2"
)

func main() {
    modules := kingpin.Command("enable", "").Default().Arg("modules", "").Strings()
    fmt.Println(kingpin.Parse())
    fmt.Println(*modules)
}

if this file is run like "cmd aaa", the output will be:

enable
[aaa]

What I expect is "unknown command", isn't you?

Also see TestDefaultSubcommandWithArg. This looks nice, but I don't think this is what we want at the most time. This behaviour is confusing. I do not known is there any program works like this either.

kingpin.v2 seems to have broken the help command

tl;dr kingpin help command seems to have broken in v2. In v1, help without arguments showed the top-level application help, and help with arguments showed the help of that command. In v2, help without arguments seems to show the default help for the help command itself, and adding arguments does nothing.

Here is the example code I'm using:

package main

import "gopkg.in/alecthomas/kingpin.v1"

var (
    debug    = kingpin.Flag("debug", "enable debug mode").Default("false").Bool()
    serverIP = kingpin.Flag("server", "server address").Default("127.0.0.1").IP()

    register     = kingpin.Command("register", "Register a new user.")
    registerNick = register.Arg("nick", "nickname for user").Required().String()
    registerName = register.Arg("name", "name of user").Required().String()

    post        = kingpin.Command("post", "Post a message to a channel.")
    postImage   = post.Flag("image", "image to post").ExistingFile()
    postChannel = post.Arg("channel", "channel to post to").Required().String()
    postText    = post.Arg("text", "text to post").String()
)

func main() {
    switch kingpin.Parse() {
    // Register user
    case "register":
        println(*registerNick)

    // Post message
    case "post":
        if postImage != nil {
        }
        if *postText != "" {
        }
    }
}

With kingpin.v1 (tag v1.3.7), the help command works fine:

$ ./kingpin_example
usage: kingpin_example [<flags>] <command> [<flags>] [<args> ...]

Flags:
  --help              Show help.
  --debug             enable debug mode
  --server=127.0.0.1  server address

Commands:
  help [<command>]
    Show help for a command.

  register <nick> <name>
    Register a new user.

  post [<flags>] <channel> [<text>]
    Post a message to a channel.

$ ./kingpin_example help
usage: kingpin_example [<flags>] <command> [<flags>] [<args> ...]

Flags:
  --help              Show help.
  --debug             enable debug mode
  --server=127.0.0.1  server address

Commands:
  help [<command>]
    Show help for a command.

  register <nick> <name>
    Register a new user.

  post [<flags>] <channel> [<text>]
    Post a message to a channel.

$ ./kingpin_example help register
usage: kingpin_example [<flags>] register <nick> <name>

Register a new user.

Args:
  <nick>  nickname for user
  <name>  name of user

But if I update the example to kingpin.v2 (tag v2.1.0), I get this output instead:

$ ./kingpin_example
usage: kingpin_example [<flags>] <command> [<args> ...]

Flags:
  --help              Show context-sensitive help (also try --help-long and --help-man).
  --debug             enable debug mode
  --server=127.0.0.1  server address

Commands:
  help [<command>...]
    Show help.

  register <nick> <name>
    Register a new user.

  post [<flags>] <channel> [<text>]
    Post a message to a channel.


$ ./kingpin_example help
usage: kingpin_example help [<command>...]

Show help.

Flags:
  --help              Show context-sensitive help (also try --help-long and --help-man).
  --debug             enable debug mode
  --server=127.0.0.1  server address

Args:
  [<command>]  Show help on command.

Subcommands:
$ ./kingpin_example help register
usage: kingpin_example help [<command>...]

Show help.

Flags:
  --help              Show context-sensitive help (also try --help-long and --help-man).
  --debug             enable debug mode
  --server=127.0.0.1  server address

Args:
  [<command>]  Show help on command.

Subcommands:

how to handle multiple sub-commands that use the same argument sets

Currently, I have 3 sub commands that take the exact same flags and args. What follows is an example of defining two of them. Is there a cleaner way to do this?

  create               = app.Command("create", "initial create/deploy of an app")
  create_cpu           = create.Flag("cpu", "cpu share").Short('c').Required().Float()
  create_mem           = create.Flag("mem", "mem share (integer MB)").Short('m').Required().Int()
  create_instances     = create.Flag("instances", "instance count").Short('i').Required().Int()
  create_app_name      = create.Arg("app_name", "application name").Required().String()
  create_app_build     = create.Arg("app_build", "application build").Required().String()
  create_app_revision  = create.Arg("app_revision", "application revision name").Required().String()
  create_app_extension = create.Arg("app_extension", "application file extension").Required().String()
  create_app_run_as    = create.Arg("app_run_as", "application run as user").Required().String()

  update               = app.Command("update", "update definition of an app (automatically deploys new definition)")
  update_cpu           = update.Flag("cpu", "cpu share").Short('c').Required().Float()
  update_mem           = update.Flag("mem", "mem share (integer MB)").Short('m').Required().Int()
  update_instances     = update.Flag("instances", "instance count").Short('i').Required().Int()
  update_app_name      = update.Arg("app_name", "application name").Required().String()
  update_app_build     = update.Arg("app_build", "application build").Required().String()
  update_app_revision  = update.Arg("app_revision", "application revision name").Required().String()
  update_app_extension = update.Arg("app_extension", "application file extension").Required().String()
  update_app_run_as    = update.Arg("app_run_as", "application run as user").Required().String()

Print help on STDOUT instead of STDERR

It would be nice if it was possible to print the help on STDOUT instead of STDERR so that when the help text gets long there is no need to redirect before piping to less for example. FWIW standard Linux tools don't seem to print help to STDERR (try ls --help, cp --help).

Parser hangs on Strings() argument

Hi,

Here is an example:

package main

import (
    "fmt"
    "os"

    "gopkg.in/alecthomas/kingpin.v1"
)

var (
    application = kingpin.New("test", "Some test program")
    failed_opt  = application.Arg("opts", "Some options").
            Required().
            Strings()
)

func main() {
    command := kingpin.MustParse(application.Parse(os.Args[1:]))
    fmt.Println(*failed_opt)
}

When I compile and run it with normal string argument, it works as expected:

$ go run test.go hello
[hello]

but if any argument has - as prefix, program just hangs on parsing. Example:

$ go run test.go hello -world

I understand, it looks like an option but I would like to implement some kind of argument which accepts strings as arguments without any attempts to parse them as an options. Something like -- notation.

[v2-unstable] Since last commit, --help fails

The interspearsed arguments are pretty cool, but since the last commit you can't call with "--help" !

...
  download [<flags>] <projectRev>
    Retrieve a build Artifact

then:

$ alchemist download --help
alchemist: error: required argument 'projectRev' not provided, try --help

Make os.Exit(0) on help optional.

Currently the Help command forces an exit of the application. There a way we can override the dispatch behaviour of help so it doesn't exit?

Document v2

  • New features.
  • Differences + migration instructions.
  • How to get v1 + maintenance state of v1.

Fix formatting for long default values.

Flags:
  --help                                                                      Show help.
  --bind=127.0.0.1:8239                                                       Host and port to bind service to.
  --log-level=info                                                            Set the default log level.
  --log-file=PATH                                                             Enable file logging to PATH.
  --log-stderr                                                                Log to stderr (defaults to true).
  --debug                                                                     Enable debug mode.
  -p, --pid-file=/var/folders/sj/cq5qhlq11p1537yg0pp21pb80000gn/T/sbusd.pid   Write PID file to PATH.
  -d, --daemonize                                                             Daemonize the process.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.