As Kong traces a sequence of command line arguments, it parses them and appends them to the parsed `Path` sequence. For each element in `Path`, these is a corresponding sequence of unparsed arguments. This change enables `Path` to yield these.
I have a package that uses Kong's hooks to instrument Kong applications (to monitor usage, reliability, etc of internal tools). I would like to instrument the commandline arguments as well.
This change would enable it to work roughly as follows:
```golang
func (Foo) BeforeApply(app *kong.Kong, ctx *kong.Context, t *Tracker) error {
command := []string{ctx.Model.Name}
var args []string
for _, path := range ctx.Path {
if path.Command != nil {
command = append(command, path.Command.Name)
args = path.Remainder()
}
}
app.Exit = t.exit(app.Exit)
t.WithCommand(strings.Join(command, " ")).WithArgs(args)
return nil
}
```
* feat: Support singleton providers
This change adds support for provider functions that are
not reinvoked even if requested by multiple other providers.
Instead, their value is cached and reused between invocations.
To make this possible, we change how bindings are stored:
instead of just a function reference, we now store a binding object
which records whether the binding is a singleton,
and records the resolved singleton value (if any).
Resolves#500
* refac(bindings): hide singleton status
Don't require callAnyFunction to be aware of
whether a binding is a singleton or not.
`passthrough:""` or `passthrough:"all"` (the default) will pass through
all further arguments including unrecognised flags.
`passthrough:"partial"` will validate flags up until the `--` or the
first positional argument, then pass through all subsequent flags and
arguments.
Given a grammar like this:
```golang
var cli struct {
Args []string `arg:"" optional:"" passthrough:""`
}
```
If Kong parses `cli foo -- bar`, it will populate `Args` with `[]string{"foo", "--", "bar"}` (including "`--`").
However, if Kong parses `cli -- foo bar`, will populate `Args` with `[]string{"foo", "bar"}` (leaving off `"--"`).
This differs from the behavior of a passthrough Command, where `"--"` is included with the args in both cases.
There are 3 places where `c.endParsing()` is called
1. When `node.Passthrough` is true: https://github.com/alecthomas/kong/blob/5f9c5cc822bdb888a3671c44d4688a6f602ecb90/context.go#L366-L368
2. When `arg.Passthrough` is true: https://github.com/alecthomas/kong/blob/5f9c5cc822bdb888a3671c44d4688a6f602ecb90/context.go#L451-L453
3. When `"--"` is encountered: https://github.com/alecthomas/kong/blob/5f9c5cc822bdb888a3671c44d4688a6f602ecb90/context.go#L384-L387
The first two do not also pop any tokens. The third one does.
This commit makes `c.scan.Pop()` conditional, skipping it when the next positional argument is passthrough.
I believe this will cause Kong to behave a little more consistently — and from my perspective, `--` is relevant for args intended to be passed through! — but it will change the behavior of existing projects that use `arg:"" passthrough:""`.
* fix: Check if negatable duplicates another flag
Add a check for flags with the `negatable` option if the negative flag
conflicts with another tag, such as:
Flag bool `negatable:""`
NoFlag bool
The flag `--no-flag` is ambiguous in this scenario.
* feat: Make negatable flag name customisable
Allow a value on the `negatable` tag to specify a flag name to use for
negation instead of using `--no-<flag-name>` as the flag.
e.g.
Approve bool `default:"true",negatable:"deny"`
This example will allow `--deny` to set the `Approve` field to false.
* Feat: Add xand group and check for missing
* Fix: Split and combine err in TestMultiand for consistency
* Feat: Check missing required flags in xand groups
* Feat: Handle combined xor and xand
* Docs: Add info about combined xand and required use
* Docs: Fix language error in xand description
Co-authored-by: Stautis <thkrst@gmail.com>
* Feat: Rename xand to and
* Refactor: Switch from fmt.Sprintf to err.Error
* Refactor: Get requiredAndGroup map in separate function
---------
Co-authored-by: Stautis <thkrst@gmail.com>
* feat: allow non-structs to be used as commands
This small MR allows using the func-to-interface trick to implement a command (see commandFunc in kong_test.go).
This is useful e.g. for commands that have no flags or arguments of their own, but instead receive all required information via bound parameters.
* fix: check DynamicCommand is runnable when adding
* ci: Add a test for positional args that are passthrough on a command that isn't passthrough
* fix: When a Grammar combines flags with passthrough args, see if an unrecognized flag may be treated as a positional argument
Given a grammar like this:
```golang
var cli struct {
Args []string `arg:"" optional:"" passthrough:""`
}
```
The first positional argument implies that it was preceded by `--`, so subsequent flags are not parsed.
If Kong parses `cli 1 --unknown 3`, it will populate `Args` with `[]string{"1", "--unknown", "3"}`.
However, if Kong parses `cli --unknown 2 3`, it will fail saying that `--unknown` is an unrecognized flag.
This commit changes the parser so that if an unknown flag _could_ be treated as the first passthrough argument, it is.
After this change, if Kong parses `cli --unknown 2 3`, it will populate `Args` with `[]string{"--unknown", "2", "3"}`.
* ci: Skip the `maintidx` linter for `trace()`
Aliases are currently only supported for sub-commands, but they're
useful for flags as well. E.g., when migrating from an old flag name
to a new flag name, while still supporting the old value.
The golangci-lint being used was quite dated.
This change upgrades to the latest version.
Adds and updates exclusions based on new failures.
Note on revive:
I've included an opt-out for unused parameters for revive
because turning `newThing(required bool)` to `newThing(_ bool)`
is a loss of useful information.
The changes to the Go files are to fix the following issues:
```
camelcase.go:16: File is not `gofmt`-ed with `-s` (gofmt)
config_test.go:50:18: directive `//nolint: gosec` is unused for linter "gosec" (nolintlint)
defaults_test.go:28:25: G601: Implicit memory aliasing in for loop. (gosec)
doc.go:5: File is not `gofmt`-ed with `-s` (gofmt)
kong.go:446:18: directive `//nolint: gosec` is unused for linter "gosec" (nolintlint)
kong_test.go:503:20: G601: Implicit memory aliasing in for loop. (gosec)
model.go:493:10: composites: reflect.ValueError struct literal uses unkeyed fields (govet)
scanner.go:112: File is not `gofmt`-ed with `-s` (gofmt)
```
And to address broken nolint directives reported as follows by
golangci-lint.
```
[.. skipped .. ]
tag.go:65:1: directive `// nolint:gocyclo` should be written without leading space as `//nolint:gocyclo` (nolintlint)
tag.go:206:51: directive `// nolint: gocyclo` should be written without leading space as `//nolint: gocyclo` (nolintlint)
util_test.go:23:43: directive `// nolint: errcheck` should be written without leading space as `//nolint: errcheck` (nolintlint)
util_test.go:51:22: directive `// nolint: errcheck` should be written without leading space as `//nolint: errcheck` (nolintlint)
```
The former allows arbitrary structs to be embedded in the root of the
CLI, with optional tags.
The latter allows an arbitrary function to be called using Kong's
binding functionality.
* Switch to the standard errors API, that was introduced in 1.13
* Fix linter errors 🤦
* Remove withStackError
* fix: freeze go version to 1.17, since 1.18 introduced generics, which are not supported by linters yet
* fix: freeze go version to 1.17, since 1.18 introduced generics, which are not supported by linters yet
* Move default command validation to build
Move the validation of default commands - checking if a node has
multiple default commands or a default command has children - to the
build phase rather than tracing. These errors are with the structure of
the CLI ast and are detectable before parsing the command line.
Add a couple of tests for some of the default command error cases.
* Disallow positional args on a default command
Do not allow a default command to have positional arguments. The current
check is only for branching args, but the error message implies that
positional args are not allowed either. So add a check for positional
args too, and add a test case for it.
This is breaking change to the API but is unlikely to have ill-effect as
positional args on a default command cannot be used without explicitly
naming the command (i.e. not using it as a default).
* Allow default commands with cmds/args/flags
Allow default commands to have sub-commands, args and flags when tagged
with `default:"withargs"`. This makes specifying the name of the
command on the CLI completely optional as long as the args to that
command are not ambiguous with other commands.
This new tag tells the parser to stop processing flags after the
positional argument is encountered.
This is particularly useful for subcommands that forward their arguments
to an external program, and makes it possible to implement commands
like `kubectl exec` or `docker run`.
Fixes#80.