diff --git a/go.mod b/go.mod
index 14cd07d0d7f..190c21b4d9f 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.23.3
require (
cloud.google.com/go/storage v1.41.0
- github.com/alecthomas/kong v0.8.0
+ github.com/alecthomas/kong v1.6.0
github.com/alicebob/miniredis/v2 v2.21.0
github.com/aws/aws-sdk-go v1.55.5
github.com/cespare/xxhash v1.1.0
@@ -117,8 +117,6 @@ require (
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0
- go.opentelemetry.io/otel/exporters/prometheus v0.55.0
- go.opentelemetry.io/otel/sdk/metric v1.33.0
go.opentelemetry.io/proto/otlp v1.4.0
golang.org/x/net v0.32.0
golang.org/x/oauth2 v0.24.0
@@ -348,11 +346,13 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.33.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
+ go.opentelemetry.io/otel/exporters/prometheus v0.55.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.9.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.33.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.33.0 // indirect
go.opentelemetry.io/otel/log v0.9.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.9.0 // indirect
+ go.opentelemetry.io/otel/sdk/metric v1.33.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/mod v0.22.0 // indirect
diff --git a/go.sum b/go.sum
index 14e76a2b8b5..a7e849453aa 100644
--- a/go.sum
+++ b/go.sum
@@ -76,14 +76,14 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0=
-github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
-github.com/alecthomas/kong v0.8.0 h1:ryDCzutfIqJPnNn0omnrgHLbAggDQM2VWHikE1xqK7s=
-github.com/alecthomas/kong v0.8.0/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
+github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
+github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
+github.com/alecthomas/kong v1.6.0 h1:mwOzbdMR7uv2vul9J0FU3GYxE7ls/iX1ieMg5WIM6gE=
+github.com/alecthomas/kong v1.6.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8=
github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c=
-github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
-github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
+github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
+github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
diff --git a/vendor/github.com/alecthomas/kong/.gitignore b/vendor/github.com/alecthomas/kong/.gitignore
index ba077a4031a..e69de29bb2d 100644
--- a/vendor/github.com/alecthomas/kong/.gitignore
+++ b/vendor/github.com/alecthomas/kong/.gitignore
@@ -1 +0,0 @@
-bin
diff --git a/vendor/github.com/alecthomas/kong/.golangci.yml b/vendor/github.com/alecthomas/kong/.golangci.yml
index 1ee8fa6f542..844092f993b 100644
--- a/vendor/github.com/alecthomas/kong/.golangci.yml
+++ b/vendor/github.com/alecthomas/kong/.golangci.yml
@@ -7,7 +7,6 @@ output:
linters:
enable-all: true
disable:
- - maligned
- lll
- gochecknoglobals
- wsl
@@ -17,11 +16,8 @@ linters:
- goprintffuncname
- paralleltest
- nlreturn
- - goerr113
- - ifshort
- testpackage
- wrapcheck
- - exhaustivestruct
- forbidigo
- gci
- godot
@@ -29,9 +25,6 @@ linters:
- cyclop
- errorlint
- nestif
- - golint
- - scopelint
- - interfacer
- tagliatelle
- thelper
- godox
@@ -41,10 +34,22 @@ linters:
- exhaustruct
- nonamedreturns
- nilnil
+ - depguard # nothing to guard against yet
+ - tagalign # hurts readability of kong tags
+ - mnd
+ - perfsprint
+ - err113
+ - copyloopvar
+ - intrange
+ - execinquery
linters-settings:
govet:
- check-shadowing: true
+ # These govet checks are disabled by default, but they're useful.
+ enable:
+ - niliness
+ - sortslice
+ - unusedwrite
dupl:
threshold: 100
gocyclo:
@@ -65,4 +70,15 @@ issues:
- 'bad syntax for struct tag key'
- 'bad syntax for struct tag pair'
- 'result .* \(error\) is always nil'
- - 'package io/ioutil is deprecated'
+ - 'Error return value of `fmt.Fprintln` is not checked'
+
+ exclude-rules:
+ # Don't warn on unused parameters.
+ # Parameter names are useful for documentation.
+ # Replacing them with '_' hides useful information.
+ - linters: [revive]
+ text: 'unused-parameter: parameter \S+ seems to be unused, consider removing or renaming it as _'
+
+ # Duplicate words are okay in tests.
+ - linters: [dupword]
+ path: _test\.go
diff --git a/vendor/github.com/alecthomas/kong/README.md b/vendor/github.com/alecthomas/kong/README.md
index 35b07c61862..ee4befea393 100644
--- a/vendor/github.com/alecthomas/kong/README.md
+++ b/vendor/github.com/alecthomas/kong/README.md
@@ -2,47 +2,49 @@
# Kong is a command-line parser for Go
+
[![](https://godoc.org/github.com/alecthomas/kong?status.svg)](http://godoc.org/github.com/alecthomas/kong) [![CircleCI](https://img.shields.io/circleci/project/github/alecthomas/kong.svg)](https://circleci.com/gh/alecthomas/kong) [![Go Report Card](https://goreportcard.com/badge/github.com/alecthomas/kong)](https://goreportcard.com/report/github.com/alecthomas/kong) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://gophers.slack.com/messages/CN9DS8YF3)
-
-
-
-
-1. [Introduction](#introduction)
-1. [Help](#help)
- 1. [Help as a user of a Kong application](#help-as-a-user-of-a-kong-application)
- 1. [Defining help in Kong](#defining-help-in-kong)
- 1. [Showing the _command_'s detailed help](#showing-the-_command_s-detailed-help)
- 1. [Showing an _argument_'s detailed help](#showing-an-_argument_s-detailed-help)
-1. [Command handling](#command-handling)
- 1. [Switch on the command string](#switch-on-the-command-string)
- 1. [Attach a `Run(...) error` method to each command](#attach-a-run-error-method-to-each-command)
-1. [Hooks: BeforeReset\(\), BeforeResolve\(\), BeforeApply\(\), AfterApply\(\) and the Bind\(\) option](#hooks-beforereset-beforeresolve-beforeapply-afterapply-and-the-bind-option)
-1. [Flags](#flags)
-1. [Commands and sub-commands](#commands-and-sub-commands)
-1. [Branching positional arguments](#branching-positional-arguments)
-1. [Positional arguments](#positional-arguments)
-1. [Slices](#slices)
-1. [Maps](#maps)
-1. [Nested data structure](#nested-data-structure)
-1. [Custom named decoders](#custom-named-decoders)
-1. [Supported field types](#supported-field-types)
-1. [Custom decoders \(mappers\)](#custom-decoders-mappers)
-1. [Supported tags](#supported-tags)
-1. [Plugins](#plugins)
-1. [Dynamic Commands](#dynamic-commands)
-1. [Variable interpolation](#variable-interpolation)
-1. [Validation](#validation)
-1. [Modifying Kong's behaviour](#modifying-kongs-behaviour)
- 1. [`Name(help)` and `Description(help)` - set the application name description](#namehelp-and-descriptionhelp---set-the-application-name-description)
- 1. [`Configuration(loader, paths...)` - load defaults from configuration files](#configurationloader-paths---load-defaults-from-configuration-files)
- 1. [`Resolver(...)` - support for default values from external sources](#resolver---support-for-default-values-from-external-sources)
- 1. [`*Mapper(...)` - customising how the command-line is mapped to Go values](#mapper---customising-how-the-command-line-is-mapped-to-go-values)
- 1. [`ConfigureHelp(HelpOptions)` and `Help(HelpFunc)` - customising help](#configurehelphelpoptions-and-helphelpfunc---customising-help)
- 1. [`Bind(...)` - bind values for callback hooks and Run\(\) methods](#bind---bind-values-for-callback-hooks-and-run-methods)
- 1. [Other options](#other-options)
-
-
+- [Kong is a command-line parser for Go](#kong-is-a-command-line-parser-for-go)
+ - [Version 1.0.0 Release](#version-100-release)
+ - [Introduction](#introduction)
+ - [Help](#help)
+ - [Help as a user of a Kong application](#help-as-a-user-of-a-kong-application)
+ - [Defining help in Kong](#defining-help-in-kong)
+ - [Command handling](#command-handling)
+ - [Switch on the command string](#switch-on-the-command-string)
+ - [Attach a `Run(...) error` method to each command](#attach-a-run-error-method-to-each-command)
+ - [Hooks: BeforeReset(), BeforeResolve(), BeforeApply(), AfterApply() and the Bind() option](#hooks-beforereset-beforeresolve-beforeapply-afterapply-and-the-bind-option)
+ - [Flags](#flags)
+ - [Commands and sub-commands](#commands-and-sub-commands)
+ - [Branching positional arguments](#branching-positional-arguments)
+ - [Positional arguments](#positional-arguments)
+ - [Slices](#slices)
+ - [Maps](#maps)
+ - [Pointers](#pointers)
+ - [Nested data structure](#nested-data-structure)
+ - [Custom named decoders](#custom-named-decoders)
+ - [Supported field types](#supported-field-types)
+ - [Custom decoders (mappers)](#custom-decoders-mappers)
+ - [Supported tags](#supported-tags)
+ - [Plugins](#plugins)
+ - [Dynamic Commands](#dynamic-commands)
+ - [Variable interpolation](#variable-interpolation)
+ - [Validation](#validation)
+ - [Modifying Kong's behaviour](#modifying-kongs-behaviour)
+ - [`Name(help)` and `Description(help)` - set the application name description](#namehelp-and-descriptionhelp---set-the-application-name-description)
+ - [`Configuration(loader, paths...)` - load defaults from configuration files](#configurationloader-paths---load-defaults-from-configuration-files)
+ - [`Resolver(...)` - support for default values from external sources](#resolver---support-for-default-values-from-external-sources)
+ - [`*Mapper(...)` - customising how the command-line is mapped to Go values](#mapper---customising-how-the-command-line-is-mapped-to-go-values)
+ - [`ConfigureHelp(HelpOptions)` and `Help(HelpFunc)` - customising help](#configurehelphelpoptions-and-helphelpfunc---customising-help)
+ - [`Bind(...)` - bind values for callback hooks and Run() methods](#bind---bind-values-for-callback-hooks-and-run-methods)
+ - [Other options](#other-options)
+
+## Version 1.0.0 Release
+
+Kong has been stable for a long time, so it seemed appropriate to cut a 1.0 release.
+
+There is one breaking change, [#436](https://github.com/alecthomas/kong/pull/436), which should effect relatively few users.
## Introduction
@@ -137,13 +139,14 @@ also be interpolated into the help string.
Finally, any command, or argument type implementing the interface
`Help() string` will have this function called to retrieve more detail to
augment the help tag. This allows for much more descriptive text than can
-fit in Go tags. [See _examples/shell/help](./_examples/shell/help)
+fit in Go tags. [See \_examples/shell/help](./_examples/shell/help)
#### Showing the _command_'s detailed help
A command's additional help text is _not_ shown from top-level help, but can be displayed within contextual help:
**Top level help**
+
```bash
$ go run ./_examples/shell/help --help
Usage: help
@@ -159,6 +162,7 @@ Commands:
```
**Contextual**
+
```bash
$ go run ./_examples/shell/help echo --help
Usage: help echo
@@ -180,6 +184,7 @@ Flags:
Custom help will only be shown for _positional arguments with named fields_ ([see the README section on positional arguments for more details on what that means](../../../README.md#branching-positional-arguments))
**Contextual argument help**
+
```bash
$ go run ./_examples/shell/help msg --help
Usage: help echo
@@ -329,7 +334,7 @@ var cli struct {
func main() {
// Debug logger going to discard.
- logger := log.New(ioutil.Discard, "", log.LstdFlags)
+ logger := log.New(io.Discard, "", log.LstdFlags)
ctx := kong.Parse(&cli, kong.Bind(logger))
@@ -337,9 +342,43 @@ func main() {
}
```
+Another example of using hooks is load the env-file:
+
+```go
+package main
+
+import (
+ "fmt"
+ "github.com/alecthomas/kong"
+ "github.com/joho/godotenv"
+)
+
+type EnvFlag string
+
+// BeforeResolve loads env file.
+func (c EnvFlag) BeforeReset(ctx *kong.Context, trace *kong.Path) error {
+ path := string(ctx.FlagValue(trace.Flag).(EnvFlag)) // nolint
+ path = kong.ExpandPath(path)
+ if err := godotenv.Load(path); err != nil {
+ return err
+ }
+ return nil
+}
+
+var CLI struct {
+ EnvFile EnvFlag
+ Flag `env:"FLAG"`
+}
+
+func main() {
+ _ = kong.Parse(&CLI)
+ fmt.Println(CLI.Flag)
+}
+```
+
## Flags
-Any [mapped](#mapper---customising-how-the-command-line-is-mapped-to-go-values) field in the command structure *not* tagged with `cmd` or `arg` will be a flag. Flags are optional by default.
+Any [mapped](#mapper---customising-how-the-command-line-is-mapped-to-go-values) 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.
@@ -479,25 +518,22 @@ Kong includes a number of builtin custom type mappers. These can be used by
specifying the tag `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`. |
+| 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`. |
| `filecontent` | Read the file at path into the field. ~ expansion is applied. `-` is accepted for stdin, and will be passed unaltered. |
-
Slices and maps treat type tags specially. For slices, the `type:""` tag
specifies the element type. For maps, the tag has the format
`tag:"[]:[]"` 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:
@@ -522,36 +558,42 @@ Tags can be in two forms:
Both can coexist with standard Tag parsing.
| Tag | Description |
-|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cmd:""` | If present, struct is a command. |
| `arg:""` | If present, field is an argument. Required by default. |
| `env:"X,Y,..."` | Specify envars to use for default value. The envs are resolved in the declared order. The first value found is used. |
| `name:"X"` | Long name, for overriding field name. |
| `help:"X"` | Help text. |
| `type:"X"` | Specify [named types](#custom-named-decoders) to use. |
-| `placeholder:"X"` | Placeholder text. |
+| `placeholder:"X"` | Placeholder input, if flag. e.g. `` `placeholder:""` `` will show `--flag-name=` when displaying help. |
| `default:"X"` | Default value. |
| `default:"1"` | On a command, make it the default. |
| `default:"withargs"` | On a command, make it the default and allow args/flags from that command |
| `short:"X"` | Short name, if flag. |
-| `aliases:"X,Y"` | One or more aliases (for cmd). |
+| `aliases:"X,Y"` | One or more aliases (for cmd or flag). |
| `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 |
+| `negatable:"X"` | If present on a `bool` field, supports `--X` 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. An enum field must be `required` or have a valid `default`. |
| `group:"X"` | Logical group for a flag or command. |
| `xor:"X,Y,..."` | Exclusive OR groups for flags. Only one flag in the group can be used which is restricted within the same command. When combined with `required`, at least one of the `xor` group will be required. |
+| `and:"X,Y,..."` | AND groups for flags. All flags in the group must be used in the same command. When combined with `required`, all flags in the group will be required. |
| `prefix:"X"` | Prefix for all sub-flags. |
| `envprefix:"X"` | Envar 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. |
-| `passthrough:""` | If present on a positional argument, it stops flag parsing when encountered, as if `--` was processed before. Useful for external command wrappers, like `exec`. On a command it requires that the command contains only one argument of type `[]string` which is then filled with everything following the command, unparsed. |
+| `passthrough:""`[^1] | If present on a positional argument, it stops flag parsing when encountered, as if `--` was processed before. Useful for external command wrappers, like `exec`. On a command it requires that the command contains only one argument of type `[]string` which is then filled with everything following the command, unparsed. |
| `-` | Ignore the field. Useful for adding non-CLI fields to a configuration struct. e.g `` `kong:"-"` `` |
+[^1]: `` can be `partial` or `all` (the default). `all` will pass through all arguments including flags, including
+flags. `partial` will validate flags until the first positional argument is encountered, then pass through all remaining
+positional arguments.
+
## Plugins
Kong CLI's can be extended by embedding the `kong.Plugin` type and populating it with pointers to Kong annotated structs. For example:
@@ -622,14 +664,19 @@ func main() {
## 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:
+extensible validation. Any node in the tree may implement either of the following interfaces:
```go
type Validatable interface {
Validate() error
}
- ```
+```
+
+```go
+type Validatable interface {
+ Validate(kctx *kong.Context) error
+ }
+```
If one of these nodes is in the active command-line it will be called during
normal validation.
@@ -660,6 +707,13 @@ kong.Parse(&cli, kong.Configuration(kong.JSON, "/etc/myapp.json", "~/.myapp.json
[See the tests](https://github.com/alecthomas/kong/blob/master/resolver_test.go#L206) for an example of how the JSON file is structured.
+#### List of Configuration Loaders
+
+- [YAML](https://github.com/alecthomas/kong-yaml)
+- [HCL](https://github.com/alecthomas/kong-hcl)
+- [TOML](https://github.com/alecthomas/kong-toml)
+- [JSON](https://github.com/alecthomas/kong)
+
### `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.
@@ -694,14 +748,19 @@ All builtin Go types (as well as a bunch of useful stdlib types like `time.Time`
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](https://godoc.org/github.com/alecthomas/kong#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.
+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 `DefaultHelpPrinter` for an example.
3. Use `ValueFormatter(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
+### Injecting values into `Run()` methods
-See the [section on hooks](#hooks-beforeresolve-beforeapply-afterapply-and-the-bind-option) for details.
+There are several ways to inject values into `Run()` methods:
+
+1. Use `Bind()` to bind values directly.
+2. Use `BindTo()` to bind values to an interface type.
+3. Use `BindToProvider()` to bind values to a function that provides the value.
+4. Implement `Provide() error` methods on the command structure.
### Other options
-The full set of options can be found [here](https://godoc.org/github.com/alecthomas/kong#Option).
\ No newline at end of file
+The full set of options can be found [here](https://godoc.org/github.com/alecthomas/kong#Option).
diff --git a/vendor/github.com/alecthomas/kong/build.go b/vendor/github.com/alecthomas/kong/build.go
index e23c11518ec..42d30f08e04 100644
--- a/vendor/github.com/alecthomas/kong/build.go
+++ b/vendor/github.com/alecthomas/kong/build.go
@@ -51,6 +51,9 @@ type flattenedField struct {
func flattenedFields(v reflect.Value, ptag *Tag) (out []flattenedField, err error) {
v = reflect.Indirect(v)
+ if v.Kind() != reflect.Struct {
+ return out, nil
+ }
for i := 0; i < v.NumField(); i++ {
ft := v.Type().Field(i)
fv := v.Field(i)
@@ -170,6 +173,12 @@ MAIN:
if flag.Short != 0 {
delete(seenFlags, "-"+string(flag.Short))
}
+ if negFlag := negatableFlagName(flag.Name, flag.Tag.Negatable); negFlag != "" {
+ delete(seenFlags, negFlag)
+ }
+ for _, aflag := range flag.Aliases {
+ delete(seenFlags, "--"+aflag)
+ }
}
if err := validatePositionalArguments(node); err != nil {
@@ -272,17 +281,18 @@ func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv
}
value := &Value{
- Name: name,
- Help: tag.Help,
- OrigHelp: tag.Help,
- HasDefault: tag.HasDefault,
- Default: tag.Default,
- DefaultValue: reflect.New(fv.Type()).Elem(),
- Mapper: mapper,
- Tag: tag,
- Target: fv,
- Enum: tag.Enum,
- Passthrough: tag.Passthrough,
+ Name: name,
+ Help: tag.Help,
+ OrigHelp: tag.Help,
+ HasDefault: tag.HasDefault,
+ Default: tag.Default,
+ DefaultValue: reflect.New(fv.Type()).Elem(),
+ Mapper: mapper,
+ Tag: tag,
+ Target: fv,
+ Enum: tag.Enum,
+ Passthrough: tag.Passthrough,
+ PassthroughMode: tag.PassthroughMode,
// Flags are optional by default, and args are required by default.
Required: (!tag.Arg && tag.Required) || (tag.Arg && !tag.Optional),
@@ -296,19 +306,35 @@ func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv
return failField(v, ft, "duplicate flag --%s", value.Name)
}
seenFlags["--"+value.Name] = true
+ for _, alias := range tag.Aliases {
+ aliasFlag := "--" + alias
+ if seenFlags[aliasFlag] {
+ return failField(v, ft, "duplicate flag %s", aliasFlag)
+ }
+ seenFlags[aliasFlag] = true
+ }
if tag.Short != 0 {
if seenFlags["-"+string(tag.Short)] {
return failField(v, ft, "duplicate short flag -%c", tag.Short)
}
seenFlags["-"+string(tag.Short)] = true
}
+ if tag.Negatable != "" {
+ negFlag := negatableFlagName(value.Name, tag.Negatable)
+ if seenFlags[negFlag] {
+ return failField(v, ft, "duplicate negation flag %s", negFlag)
+ }
+ seenFlags[negFlag] = true
+ }
flag := &Flag{
Value: value,
+ Aliases: tag.Aliases,
Short: tag.Short,
PlaceHolder: tag.PlaceHolder,
Envs: tag.Envs,
Group: buildGroupForKey(k, tag.Group),
Xor: tag.Xor,
+ And: tag.And,
Hidden: tag.Hidden,
}
value.Flag = flag
diff --git a/vendor/github.com/alecthomas/kong/callbacks.go b/vendor/github.com/alecthomas/kong/callbacks.go
index 3a8a45f2f65..1df975d8236 100644
--- a/vendor/github.com/alecthomas/kong/callbacks.go
+++ b/vendor/github.com/alecthomas/kong/callbacks.go
@@ -6,7 +6,10 @@ import (
"strings"
)
-type bindings map[reflect.Type]func() (reflect.Value, error)
+// A map of type to function that returns a value of that type.
+//
+// The function should have the signature func(...) (T, error). Arguments are recursively resolved.
+type bindings map[reflect.Type]any
func (b bindings) String() string {
out := []string{}
@@ -19,32 +22,23 @@ func (b bindings) String() string {
func (b bindings) add(values ...interface{}) bindings {
for _, v := range values {
v := v
- b[reflect.TypeOf(v)] = func() (reflect.Value, error) { return reflect.ValueOf(v), nil }
+ b[reflect.TypeOf(v)] = func() (any, error) { return v, nil }
}
return b
}
func (b bindings) addTo(impl, iface interface{}) {
- valueOf := reflect.ValueOf(impl)
- b[reflect.TypeOf(iface).Elem()] = func() (reflect.Value, error) { return valueOf, nil }
+ b[reflect.TypeOf(iface).Elem()] = func() (any, error) { return impl, nil }
}
func (b bindings) addProvider(provider interface{}) error {
pv := reflect.ValueOf(provider)
t := pv.Type()
- if t.Kind() != reflect.Func || t.NumIn() != 0 || t.NumOut() != 2 || t.Out(1) != reflect.TypeOf((*error)(nil)).Elem() {
- return fmt.Errorf("%T must be a function with the signature func()(T, error)", provider)
+ if t.Kind() != reflect.Func || t.NumOut() != 2 || t.Out(1) != reflect.TypeOf((*error)(nil)).Elem() {
+ return fmt.Errorf("%T must be a function with the signature func(...)(T, error)", provider)
}
rt := pv.Type().Out(0)
- b[rt] = func() (reflect.Value, error) {
- out := pv.Call(nil)
- errv := out[1]
- var err error
- if !errv.IsNil() {
- err = errv.Interface().(error) // nolint
- }
- return out[0], err
- }
+ b[rt] = provider
return nil
}
@@ -78,28 +72,19 @@ func callFunction(f reflect.Value, bindings bindings) error {
if f.Kind() != reflect.Func {
return fmt.Errorf("expected function, got %s", f.Type())
}
- in := []reflect.Value{}
t := f.Type()
if t.NumOut() != 1 || !t.Out(0).Implements(callbackReturnSignature) {
return fmt.Errorf("return value of %s must implement \"error\"", t)
}
- for i := 0; i < t.NumIn(); i++ {
- pt := t.In(i)
- if argf, ok := bindings[pt]; ok {
- argv, err := argf()
- if err != nil {
- return err
- }
- in = append(in, argv)
- } else {
- return fmt.Errorf("couldn't find binding of type %s for parameter %d of %s(), use kong.Bind(%s)", pt, i, t, pt)
- }
+ out, err := callAnyFunction(f, bindings)
+ if err != nil {
+ return err
}
- out := f.Call(in)
- if out[0].IsNil() {
+ ferr := out[0]
+ if ferrv := reflect.ValueOf(ferr); !ferrv.IsValid() || ((ferrv.Kind() == reflect.Interface || ferrv.Kind() == reflect.Pointer) && ferrv.IsNil()) {
return nil
}
- return out[0].Interface().(error) // nolint
+ return ferr.(error) //nolint:forcetypeassert
}
func callAnyFunction(f reflect.Value, bindings bindings) (out []any, err error) {
@@ -110,15 +95,19 @@ func callAnyFunction(f reflect.Value, bindings bindings) (out []any, err error)
t := f.Type()
for i := 0; i < t.NumIn(); i++ {
pt := t.In(i)
- if argf, ok := bindings[pt]; ok {
- argv, err := argf()
- if err != nil {
- return nil, err
- }
- in = append(in, argv)
- } else {
+ argf, ok := bindings[pt]
+ if !ok {
return nil, fmt.Errorf("couldn't find binding of type %s for parameter %d of %s(), use kong.Bind(%s)", pt, i, t, pt)
}
+ // Recursively resolve binding functions.
+ argv, err := callAnyFunction(reflect.ValueOf(argf), bindings)
+ if err != nil {
+ return nil, fmt.Errorf("%s: %w", pt, err)
+ }
+ if ferrv := reflect.ValueOf(argv[len(argv)-1]); ferrv.IsValid() && !ferrv.IsNil() {
+ return nil, ferrv.Interface().(error) //nolint:forcetypeassert
+ }
+ in = append(in, reflect.ValueOf(argv[0]))
}
outv := f.Call(in)
out = make([]any, len(outv))
@@ -127,11 +116,3 @@ func callAnyFunction(f reflect.Value, bindings bindings) (out []any, err error)
}
return out, nil
}
-
-func callMethod(name string, v, f reflect.Value, bindings bindings) error {
- err := callFunction(f, bindings)
- if err != nil {
- return fmt.Errorf("%s.%s(): %w", v.Type(), name, err)
- }
- return nil
-}
diff --git a/vendor/github.com/alecthomas/kong/camelcase.go b/vendor/github.com/alecthomas/kong/camelcase.go
index acf29f75770..a955b16ba21 100644
--- a/vendor/github.com/alecthomas/kong/camelcase.go
+++ b/vendor/github.com/alecthomas/kong/camelcase.go
@@ -13,37 +13,37 @@ import (
//
// Examples
//
-// "" => [""]
-// "lowercase" => ["lowercase"]
-// "Class" => ["Class"]
-// "MyClass" => ["My", "Class"]
-// "MyC" => ["My", "C"]
-// "HTML" => ["HTML"]
-// "PDFLoader" => ["PDF", "Loader"]
-// "AString" => ["A", "String"]
-// "SimpleXMLParser" => ["Simple", "XML", "Parser"]
-// "vimRPCPlugin" => ["vim", "RPC", "Plugin"]
-// "GL11Version" => ["GL", "11", "Version"]
-// "99Bottles" => ["99", "Bottles"]
-// "May5" => ["May", "5"]
-// "BFG9000" => ["BFG", "9000"]
-// "BöseÜberraschung" => ["Böse", "Überraschung"]
-// "Two spaces" => ["Two", " ", "spaces"]
-// "BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"]
+// "" => [""]
+// "lowercase" => ["lowercase"]
+// "Class" => ["Class"]
+// "MyClass" => ["My", "Class"]
+// "MyC" => ["My", "C"]
+// "HTML" => ["HTML"]
+// "PDFLoader" => ["PDF", "Loader"]
+// "AString" => ["A", "String"]
+// "SimpleXMLParser" => ["Simple", "XML", "Parser"]
+// "vimRPCPlugin" => ["vim", "RPC", "Plugin"]
+// "GL11Version" => ["GL", "11", "Version"]
+// "99Bottles" => ["99", "Bottles"]
+// "May5" => ["May", "5"]
+// "BFG9000" => ["BFG", "9000"]
+// "BöseÜberraschung" => ["Böse", "Überraschung"]
+// "Two spaces" => ["Two", " ", "spaces"]
+// "BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"]
//
// Splitting rules
//
-// 1) If string is not valid UTF-8, return it without splitting as
+// 1. If string is not valid UTF-8, return it without splitting as
// single item array.
-// 2) Assign all unicode characters into one of 4 sets: lower case
+// 2. Assign all unicode characters into one of 4 sets: lower case
// letters, upper case letters, numbers, and all other characters.
-// 3) Iterate through characters of string, introducing splits
+// 3. Iterate through characters of string, introducing splits
// between adjacent characters that belong to different sets.
-// 4) Iterate through array of split strings, and if a given string
+// 4. Iterate through array of split strings, and if a given string
// is upper case:
-// if subsequent string is lower case:
-// move last character of upper case string to beginning of
-// lower case string
+// if subsequent string is lower case:
+// move last character of upper case string to beginning of
+// lower case string
func camelCase(src string) (entries []string) {
// don't split invalid utf8
if !utf8.ValidString(src) {
diff --git a/vendor/github.com/alecthomas/kong/context.go b/vendor/github.com/alecthomas/kong/context.go
index 0ec10f47d17..fd168fa921b 100644
--- a/vendor/github.com/alecthomas/kong/context.go
+++ b/vendor/github.com/alecthomas/kong/context.go
@@ -161,7 +161,7 @@ func (c *Context) Empty() bool {
}
// Validate the current context.
-func (c *Context) Validate() error { // nolint: gocyclo
+func (c *Context) Validate() error { //nolint: gocyclo
err := Visit(c.Model, func(node Visitable, next Next) error {
switch node := node.(type) {
case *Value:
@@ -201,15 +201,18 @@ func (c *Context) Validate() error { // nolint: gocyclo
case *Application:
value = node.Target
- desc = node.Name
+ desc = ""
case *Node:
value = node.Target
desc = node.Path()
}
if validate := isValidatable(value); validate != nil {
- if err := validate.Validate(); err != nil {
- return fmt.Errorf("%s: %w", desc, err)
+ if err := validate.Validate(c); err != nil {
+ if desc != "" {
+ return fmt.Errorf("%s: %w", desc, err)
+ }
+ return err
}
}
}
@@ -256,7 +259,7 @@ func (c *Context) Validate() error { // nolint: gocyclo
if err := checkMissingPositionals(positionals, node.Positional); err != nil {
return err
}
- if err := checkXorDuplicates(c.Path); err != nil {
+ if err := checkXorDuplicatedAndAndMissing(c.Path); err != nil {
return err
}
@@ -344,7 +347,8 @@ func (c *Context) endParsing() {
}
}
-func (c *Context) trace(node *Node) (err error) { // nolint: gocyclo
+//nolint:maintidx
+func (c *Context) trace(node *Node) (err error) { //nolint: gocyclo
positional := 0
node.Active = true
@@ -374,15 +378,19 @@ func (c *Context) trace(node *Node) (err error) { // nolint: gocyclo
switch {
case v == "-":
fallthrough
- default: // nolint
+ default: //nolint
c.scan.Pop()
c.scan.PushTyped(token.Value, PositionalArgumentToken)
// Indicates end of parsing. All remaining arguments are treated as positional arguments only.
case v == "--":
- c.scan.Pop()
c.endParsing()
+ // Pop the -- token unless the next positional argument accepts passthrough arguments.
+ if !(positional < len(node.Positional) && node.Positional[positional].Passthrough) {
+ c.scan.Pop()
+ }
+
// Long flag.
case strings.HasPrefix(v, "--"):
c.scan.Pop()
@@ -417,12 +425,22 @@ func (c *Context) trace(node *Node) (err error) { // nolint: gocyclo
case FlagToken:
if err := c.parseFlag(flags, token.String()); err != nil {
- return err
+ if isUnknownFlagError(err) && positional < len(node.Positional) && node.Positional[positional].PassthroughMode == PassThroughModeAll {
+ c.scan.Pop()
+ c.scan.PushTyped(token.String(), PositionalArgumentToken)
+ } else {
+ return err
+ }
}
case ShortFlagToken:
if err := c.parseFlag(flags, token.String()); err != nil {
- return err
+ if isUnknownFlagError(err) && positional < len(node.Positional) && node.Positional[positional].PassthroughMode == PassThroughModeAll {
+ c.scan.Pop()
+ c.scan.PushTyped(token.String(), PositionalArgumentToken)
+ } else {
+ return err
+ }
}
case FlagValueToken:
@@ -681,20 +699,29 @@ func flipBoolValue(value reflect.Value) error {
func (c *Context) parseFlag(flags []*Flag, match string) (err error) {
candidates := []string{}
+
for _, flag := range flags {
long := "--" + flag.Name
- short := "-" + string(flag.Short)
- neg := "--no-" + flag.Name
+ matched := long == match
candidates = append(candidates, long)
if flag.Short != 0 {
+ short := "-" + string(flag.Short)
+ matched = matched || (short == match)
candidates = append(candidates, short)
}
- if short != match && long != match && !(match == neg && flag.Tag.Negatable) {
+ for _, alias := range flag.Aliases {
+ alias = "--" + alias
+ matched = matched || (alias == match)
+ candidates = append(candidates, alias)
+ }
+
+ neg := negatableFlagName(flag.Name, flag.Tag.Negatable)
+ if !matched && match != neg {
continue
}
// Found a matching flag.
c.scan.Pop()
- if match == neg && flag.Tag.Negatable {
+ if match == neg && flag.Tag.Negatable != "" {
flag.Negated = true
}
err := flag.Parse(c.scan, c.getValue(flag.Value))
@@ -716,13 +743,23 @@ func (c *Context) parseFlag(flags []*Flag, match string) (err error) {
c.Path = append(c.Path, &Path{Flag: flag})
return nil
}
- return findPotentialCandidates(match, candidates, "unknown flag %s", match)
+ return &unknownFlagError{Cause: findPotentialCandidates(match, candidates, "unknown flag %s", match)}
+}
+
+func isUnknownFlagError(err error) bool {
+ var unknown *unknownFlagError
+ return errors.As(err, &unknown)
}
+type unknownFlagError struct{ Cause error }
+
+func (e *unknownFlagError) Unwrap() error { return e.Cause }
+func (e *unknownFlagError) Error() string { return e.Cause.Error() }
+
// Call an arbitrary function filling arguments with bound values.
func (c *Context) Call(fn any, binds ...interface{}) (out []interface{}, err error) {
fv := reflect.ValueOf(fn)
- bindings := c.Kong.bindings.clone().add(binds...).add(c).merge(c.bindings) //nolint:govet
+ bindings := c.Kong.bindings.clone().add(binds...).add(c).merge(c.bindings)
return callAnyFunction(fv, bindings)
}
@@ -745,6 +782,19 @@ func (c *Context) RunNode(node *Node, binds ...interface{}) (err error) {
methodBinds = methodBinds.clone()
for p := node; p != nil; p = p.Parent {
methodBinds = methodBinds.add(p.Target.Addr().Interface())
+ // Try value and pointer to value.
+ for _, p := range []reflect.Value{p.Target, p.Target.Addr()} {
+ t := p.Type()
+ for i := 0; i < p.NumMethod(); i++ {
+ methodt := t.Method(i)
+ if strings.HasPrefix(methodt.Name, "Provide") {
+ method := p.Method(i)
+ if err := methodBinds.addProvider(method.Interface()); err != nil {
+ return fmt.Errorf("%s.%s: %w", t.Name(), methodt.Name, err)
+ }
+ }
+ }
+ }
}
if method.IsValid() {
methods = append(methods, targetMethod{node, method, methodBinds})
@@ -759,7 +809,7 @@ func (c *Context) RunNode(node *Node, binds ...interface{}) (err error) {
}
for _, method := range methods {
- if err = callMethod("Run", method.node.Target, method.method, method.binds); err != nil {
+ if err = callFunction(method.method, method.binds); err != nil {
return err
}
}
@@ -773,18 +823,22 @@ func (c *Context) RunNode(node *Node, binds ...interface{}) (err error) {
func (c *Context) Run(binds ...interface{}) (err error) {
node := c.Selected()
if node == nil {
- if len(c.Path) > 0 {
- selected := c.Path[0].Node()
- if selected.Type == ApplicationNode {
- method := getMethod(selected.Target, "Run")
- if method.IsValid() {
- return c.RunNode(selected, binds...)
- }
+ if len(c.Path) == 0 {
+ return fmt.Errorf("no command selected")
+ }
+ selected := c.Path[0].Node()
+ if selected.Type == ApplicationNode {
+ method := getMethod(selected.Target, "Run")
+ if method.IsValid() {
+ node = selected
}
+ } else {
+ return fmt.Errorf("no command selected")
}
- return fmt.Errorf("no command selected")
}
- return c.RunNode(node, binds...)
+ runErr := c.RunNode(node, binds...)
+ err = c.Kong.applyHook(c, "AfterRun")
+ return errors.Join(runErr, err)
}
// PrintUsage to Kong's stdout.
@@ -799,23 +853,35 @@ func (c *Context) PrintUsage(summary bool) error {
func checkMissingFlags(flags []*Flag) error {
xorGroupSet := map[string]bool{}
xorGroup := map[string][]string{}
+ andGroupSet := map[string]bool{}
+ andGroup := map[string][]string{}
missing := []string{}
+ andGroupRequired := getRequiredAndGroupMap(flags)
for _, flag := range flags {
+ for _, and := range flag.And {
+ flag.Required = andGroupRequired[and]
+ }
if flag.Set {
for _, xor := range flag.Xor {
xorGroupSet[xor] = true
}
+ for _, and := range flag.And {
+ andGroupSet[and] = true
+ }
}
if !flag.Required || flag.Set {
continue
}
- if len(flag.Xor) > 0 {
+ if len(flag.Xor) > 0 || len(flag.And) > 0 {
for _, xor := range flag.Xor {
if xorGroupSet[xor] {
continue
}
xorGroup[xor] = append(xorGroup[xor], flag.Summary())
}
+ for _, and := range flag.And {
+ andGroup[and] = append(andGroup[and], flag.Summary())
+ }
} else {
missing = append(missing, flag.Summary())
}
@@ -825,6 +891,11 @@ func checkMissingFlags(flags []*Flag) error {
missing = append(missing, strings.Join(flags, " or "))
}
}
+ for _, flags := range andGroup {
+ if len(flags) > 1 {
+ missing = append(missing, strings.Join(flags, " and "))
+ }
+ }
if len(missing) == 0 {
return nil
@@ -835,6 +906,18 @@ func checkMissingFlags(flags []*Flag) error {
return fmt.Errorf("missing flags: %s", strings.Join(missing, ", "))
}
+func getRequiredAndGroupMap(flags []*Flag) map[string]bool {
+ andGroupRequired := map[string]bool{}
+ for _, flag := range flags {
+ for _, and := range flag.And {
+ if flag.Required {
+ andGroupRequired[and] = true
+ }
+ }
+ }
+ return andGroupRequired
+}
+
func checkMissingChildren(node *Node) error {
missing := []string{}
@@ -871,7 +954,7 @@ func checkMissingChildren(node *Node) error {
if len(missing) == 1 {
return fmt.Errorf("expected %s", missing[0])
}
- return fmt.Errorf("expected one of %s", strings.Join(missing, ", "))
+ return fmt.Errorf("expected one of %s", strings.Join(missing, ", "))
}
// If we're missing any positionals and they're required, return an error.
@@ -931,7 +1014,7 @@ func checkEnum(value *Value, target reflect.Value) error {
}
enums = append(enums, fmt.Sprintf("%q", enum))
}
- return fmt.Errorf("%s must be one of %s but got %q", value.ShortSummary(), strings.Join(enums, ","), target.Interface())
+ return fmt.Errorf("%s must be one of %s but got %q", value.ShortSummary(), strings.Join(enums, ","), fmt.Sprintf("%v", target.Interface()))
}
}
@@ -945,6 +1028,20 @@ func checkPassthroughArg(target reflect.Value) bool {
}
}
+func checkXorDuplicatedAndAndMissing(paths []*Path) error {
+ errs := []string{}
+ if err := checkXorDuplicates(paths); err != nil {
+ errs = append(errs, err.Error())
+ }
+ if err := checkAndMissing(paths); err != nil {
+ errs = append(errs, err.Error())
+ }
+ if len(errs) > 0 {
+ return errors.New(strings.Join(errs, ", "))
+ }
+ return nil
+}
+
func checkXorDuplicates(paths []*Path) error {
for _, path := range paths {
seen := map[string]*Flag{}
@@ -963,6 +1060,38 @@ func checkXorDuplicates(paths []*Path) error {
return nil
}
+func checkAndMissing(paths []*Path) error {
+ for _, path := range paths {
+ missingMsgs := []string{}
+ andGroups := map[string][]*Flag{}
+ for _, flag := range path.Flags {
+ for _, and := range flag.And {
+ andGroups[and] = append(andGroups[and], flag)
+ }
+ }
+ for _, flags := range andGroups {
+ oneSet := false
+ notSet := []*Flag{}
+ flagNames := []string{}
+ for _, flag := range flags {
+ flagNames = append(flagNames, flag.Name)
+ if flag.Set {
+ oneSet = true
+ } else {
+ notSet = append(notSet, flag)
+ }
+ }
+ if len(notSet) > 0 && oneSet {
+ missingMsgs = append(missingMsgs, fmt.Sprintf("--%s must be used together", strings.Join(flagNames, " and --")))
+ }
+ }
+ if len(missingMsgs) > 0 {
+ return fmt.Errorf("%s", strings.Join(missingMsgs, ", "))
+ }
+ }
+ return nil
+}
+
func findPotentialCandidates(needle string, haystack []string, format string, args ...interface{}) error {
if len(haystack) == 0 {
return fmt.Errorf(format, args...)
@@ -983,12 +1112,23 @@ func findPotentialCandidates(needle string, haystack []string, format string, ar
}
type validatable interface{ Validate() error }
+type extendedValidatable interface {
+ Validate(kctx *Context) error
+}
-func isValidatable(v reflect.Value) validatable {
+// Proxy a validatable function to the extendedValidatable interface
+type validatableFunc func() error
+
+func (f validatableFunc) Validate(kctx *Context) error { return f() }
+
+func isValidatable(v reflect.Value) extendedValidatable {
if !v.IsValid() || (v.Kind() == reflect.Ptr || v.Kind() == reflect.Slice || v.Kind() == reflect.Map) && v.IsNil() {
return nil
}
if validate, ok := v.Interface().(validatable); ok {
+ return validatableFunc(validate.Validate)
+ }
+ if validate, ok := v.Interface().(extendedValidatable); ok {
return validate
}
if v.CanAddr() {
diff --git a/vendor/github.com/alecthomas/kong/doc.go b/vendor/github.com/alecthomas/kong/doc.go
index 78c4d11037b..7e53da7f362 100644
--- a/vendor/github.com/alecthomas/kong/doc.go
+++ b/vendor/github.com/alecthomas/kong/doc.go
@@ -2,31 +2,31 @@
//
// Here's an example:
//
-// shell rm [-f] [-r] ...
-// shell ls [ ...]
+// shell rm [-f] [-r] ...
+// shell ls [ ...]
//
// This can be represented by the following command-line structure:
//
-// package main
+// package main
//
-// import "github.com/alecthomas/kong"
+// import "github.com/alecthomas/kong"
//
-// var CLI struct {
-// Rm struct {
-// Force bool `short:"f" help:"Force removal."`
-// Recursive bool `short:"r" help:"Recursively remove files."`
+// var CLI struct {
+// Rm struct {
+// Force bool `short:"f" help:"Force removal."`
+// Recursive bool `short:"r" help:"Recursively remove files."`
//
-// Paths []string `arg help:"Paths to remove." type:"path"`
-// } `cmd help:"Remove files."`
+// Paths []string `arg help:"Paths to remove." type:"path"`
+// } `cmd help:"Remove files."`
//
-// Ls struct {
-// Paths []string `arg optional help:"Paths to list." type:"path"`
-// } `cmd help:"List paths."`
-// }
+// Ls struct {
+// Paths []string `arg optional help:"Paths to list." type:"path"`
+// } `cmd help:"List paths."`
+// }
//
-// func main() {
-// kong.Parse(&CLI)
-// }
+// func main() {
+// kong.Parse(&CLI)
+// }
//
// See https://github.com/alecthomas/kong for details.
package kong
diff --git a/vendor/github.com/alecthomas/kong/guesswidth.go b/vendor/github.com/alecthomas/kong/guesswidth.go
index 46768e68301..dfdc3f5134b 100644
--- a/vendor/github.com/alecthomas/kong/guesswidth.go
+++ b/vendor/github.com/alecthomas/kong/guesswidth.go
@@ -1,3 +1,4 @@
+//go:build appengine || (!linux && !freebsd && !darwin && !dragonfly && !netbsd && !openbsd)
// +build appengine !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd
package kong
diff --git a/vendor/github.com/alecthomas/kong/guesswidth_unix.go b/vendor/github.com/alecthomas/kong/guesswidth_unix.go
index db52595e248..0170055a79c 100644
--- a/vendor/github.com/alecthomas/kong/guesswidth_unix.go
+++ b/vendor/github.com/alecthomas/kong/guesswidth_unix.go
@@ -27,9 +27,9 @@ func guessWidth(w io.Writer) int {
if _, _, err := syscall.Syscall6(
syscall.SYS_IOCTL,
- uintptr(fd), // nolint: unconvert
+ uintptr(fd), //nolint: unconvert
uintptr(syscall.TIOCGWINSZ),
- uintptr(unsafe.Pointer(&dimensions)), // nolint: gas
+ uintptr(unsafe.Pointer(&dimensions)), //nolint: gas
0, 0, 0,
); err == 0 {
if dimensions[1] == 0 {
diff --git a/vendor/github.com/alecthomas/kong/help.go b/vendor/github.com/alecthomas/kong/help.go
index 4fb77001d4c..26f355df373 100644
--- a/vendor/github.com/alecthomas/kong/help.go
+++ b/vendor/github.com/alecthomas/kong/help.go
@@ -490,28 +490,24 @@ func formatFlag(haveShort bool, flag *Flag) string {
flagString := ""
name := flag.Name
isBool := flag.IsBool()
+ isCounter := flag.IsCounter()
+
+ short := ""
if flag.Short != 0 {
- if isBool && flag.Tag.Negatable {
- flagString += fmt.Sprintf("-%c, --[no-]%s", flag.Short, name)
- } else {
- flagString += fmt.Sprintf("-%c, --%s", flag.Short, name)
- }
- } else {
- if isBool && flag.Tag.Negatable {
- if haveShort {
- flagString = fmt.Sprintf(" --[no-]%s", name)
- } else {
- flagString = fmt.Sprintf("--[no-]%s", name)
- }
- } else {
- if haveShort {
- flagString += fmt.Sprintf(" --%s", name)
- } else {
- flagString += fmt.Sprintf("--%s", name)
- }
- }
+ short = "-" + string(flag.Short) + ", "
+ } else if haveShort {
+ short = " "
+ }
+
+ if isBool && flag.Tag.Negatable == negatableDefault {
+ name = "[no-]" + name
+ } else if isBool && flag.Tag.Negatable != "" {
+ name += "/" + flag.Tag.Negatable
}
- if !isBool {
+
+ flagString += fmt.Sprintf("%s--%s", short, name)
+
+ if !isBool && !isCounter {
flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder())
}
return flagString
diff --git a/vendor/github.com/alecthomas/kong/hooks.go b/vendor/github.com/alecthomas/kong/hooks.go
index d166b08883a..9fdf24c1395 100644
--- a/vendor/github.com/alecthomas/kong/hooks.go
+++ b/vendor/github.com/alecthomas/kong/hooks.go
@@ -3,17 +3,24 @@ package kong
// BeforeResolve is a documentation-only interface describing hooks that run before resolvers are applied.
type BeforeResolve interface {
// This is not the correct signature - see README for details.
- BeforeResolve(args ...interface{}) error
+ BeforeResolve(args ...any) error
}
// BeforeApply is a documentation-only interface describing hooks that run before values are set.
type BeforeApply interface {
// This is not the correct signature - see README for details.
- BeforeApply(args ...interface{}) error
+ BeforeApply(args ...any) error
}
// AfterApply is a documentation-only interface describing hooks that run after values are set.
type AfterApply interface {
// This is not the correct signature - see README for details.
- AfterApply(args ...interface{}) error
+ AfterApply(args ...any) error
+}
+
+// AfterRun is a documentation-only interface describing hooks that run after Run() returns.
+type AfterRun interface {
+ // This is not the correct signature - see README for details.
+ // AfterRun is called after Run() returns.
+ AfterRun(args ...any) error
}
diff --git a/vendor/github.com/alecthomas/kong/kong.go b/vendor/github.com/alecthomas/kong/kong.go
index 255633fbba7..764a994c720 100644
--- a/vendor/github.com/alecthomas/kong/kong.go
+++ b/vendor/github.com/alecthomas/kong/kong.go
@@ -117,7 +117,7 @@ func New(grammar interface{}, options ...Option) (*Kong, error) {
// Embed any embedded structs.
for _, embed := range k.embedded {
- tag, err := parseTagString(strings.Join(embed.tags, " ")) //nolint:govet
+ tag, err := parseTagString(strings.Join(embed.tags, " "))
if err != nil {
return nil, err
}
@@ -167,9 +167,42 @@ func New(grammar interface{}, options ...Option) (*Kong, error) {
k.bindings.add(k.vars)
+ if err = checkOverlappingXorAnd(k); err != nil {
+ return nil, err
+ }
+
return k, nil
}
+func checkOverlappingXorAnd(k *Kong) error {
+ xorGroups := map[string][]string{}
+ andGroups := map[string][]string{}
+ for _, flag := range k.Model.Node.Flags {
+ for _, xor := range flag.Xor {
+ xorGroups[xor] = append(xorGroups[xor], flag.Name)
+ }
+ for _, and := range flag.And {
+ andGroups[and] = append(andGroups[and], flag.Name)
+ }
+ }
+ for xor, xorSet := range xorGroups {
+ for and, andSet := range andGroups {
+ overlappingEntries := []string{}
+ for _, xorTag := range xorSet {
+ for _, andTag := range andSet {
+ if xorTag == andTag {
+ overlappingEntries = append(overlappingEntries, xorTag)
+ }
+ }
+ }
+ if len(overlappingEntries) > 1 {
+ return fmt.Errorf("invalid xor and combination, %s and %s overlap with more than one: %s", xor, and, overlappingEntries)
+ }
+ }
+ }
+ return nil
+}
+
type varStack []Vars
func (v *varStack) head() Vars { return (*v)[len(*v)-1] }
@@ -216,19 +249,19 @@ func (k *Kong) interpolateValue(value *Value, vars Vars) (err error) {
return fmt.Errorf("enum for %s: %s", value.Summary(), err)
}
- updatedVars := map[string]string{
- "default": value.Default,
- "enum": value.Enum,
- }
if value.Default, err = interpolate(value.Default, vars, nil); err != nil {
return fmt.Errorf("default value for %s: %s", value.Summary(), err)
}
if value.Enum, err = interpolate(value.Enum, vars, nil); err != nil {
return fmt.Errorf("enum value for %s: %s", value.Summary(), err)
}
+ updatedVars := map[string]string{
+ "default": value.Default,
+ "enum": value.Enum,
+ }
if value.Flag != nil {
for i, env := range value.Flag.Envs {
- if value.Flag.Envs[i], err = interpolate(env, vars, nil); err != nil {
+ if value.Flag.Envs[i], err = interpolate(env, vars, updatedVars); err != nil {
return fmt.Errorf("env value for %s: %s", value.Summary(), err)
}
}
@@ -336,7 +369,7 @@ func (k *Kong) applyHook(ctx *Context, name string) error {
binds.add(ctx, trace)
binds.add(trace.Node().Vars().CloneWith(k.vars))
binds.merge(ctx.bindings)
- if err := callMethod(name, value, method, binds); err != nil {
+ if err := callFunction(method, binds); err != nil {
return err
}
}
@@ -364,7 +397,7 @@ func (k *Kong) applyHookToDefaultFlags(ctx *Context, node *Node, name string) er
continue
}
path := &Path{Flag: flag}
- if err := callMethod(name, flag.Target, method, binds.clone().add(path)); err != nil {
+ if err := callFunction(method, binds.clone().add(path)); err != nil {
return next(err)
}
}
@@ -373,7 +406,7 @@ func (k *Kong) applyHookToDefaultFlags(ctx *Context, node *Node, name string) er
}
func formatMultilineMessage(w io.Writer, leaders []string, format string, args ...interface{}) {
- lines := strings.Split(fmt.Sprintf(format, args...), "\n")
+ lines := strings.Split(strings.TrimRight(fmt.Sprintf(format, args...), "\n"), "\n")
leader := ""
for _, l := range leaders {
if l == "" {
@@ -412,7 +445,7 @@ func (k *Kong) FatalIfErrorf(err error, args ...interface{}) {
}
msg := err.Error()
if len(args) > 0 {
- msg = fmt.Sprintf(args[0].(string), args[1:]...) + ": " + err.Error() // nolint
+ msg = fmt.Sprintf(args[0].(string), args[1:]...) + ": " + err.Error() //nolint
}
// Maybe display usage information.
var parseErr *ParseError
@@ -439,11 +472,11 @@ func (k *Kong) LoadConfig(path string) (Resolver, error) {
if err != nil {
return nil, err
}
- r, err := os.Open(path) // nolint: gas
+ r, err := os.Open(path) //nolint: gas
if err != nil {
return nil, err
}
- defer r.Close() // nolint: gosec
+ defer r.Close()
return k.loader(r)
}
diff --git a/vendor/github.com/alecthomas/kong/levenshtein.go b/vendor/github.com/alecthomas/kong/levenshtein.go
index 1816f301153..6837d6c3097 100644
--- a/vendor/github.com/alecthomas/kong/levenshtein.go
+++ b/vendor/github.com/alecthomas/kong/levenshtein.go
@@ -31,7 +31,7 @@ func levenshtein(a, b string) int {
return f[len(f)-1]
}
-func min(a, b int) int {
+func min(a, b int) int { //nolint:predeclared
if a <= b {
return a
}
diff --git a/vendor/github.com/alecthomas/kong/mapper.go b/vendor/github.com/alecthomas/kong/mapper.go
index a510e745344..584bb00691d 100644
--- a/vendor/github.com/alecthomas/kong/mapper.go
+++ b/vendor/github.com/alecthomas/kong/mapper.go
@@ -5,7 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
- "io/ioutil"
+ "io"
"math/bits"
"net/url"
"os"
@@ -17,7 +17,7 @@ import (
var (
mapperValueType = reflect.TypeOf((*MapperValue)(nil)).Elem()
- boolMapperType = reflect.TypeOf((*BoolMapper)(nil)).Elem()
+ boolMapperValueType = reflect.TypeOf((*BoolMapperValue)(nil)).Elem()
jsonUnmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
@@ -47,15 +47,21 @@ type MapperValue interface {
Decode(ctx *DecodeContext) error
}
+// BoolMapperValue may be implemented by fields in order to provide custom mappings for boolean values.
+type BoolMapperValue interface {
+ MapperValue
+ IsBool() bool
+}
+
type mapperValueAdapter struct {
isBool bool
}
func (m *mapperValueAdapter) Decode(ctx *DecodeContext, target reflect.Value) error {
if target.Type().Implements(mapperValueType) {
- return target.Interface().(MapperValue).Decode(ctx) // nolint
+ return target.Interface().(MapperValue).Decode(ctx) //nolint
}
- return target.Addr().Interface().(MapperValue).Decode(ctx) // nolint
+ return target.Addr().Interface().(MapperValue).Decode(ctx) //nolint
}
func (m *mapperValueAdapter) IsBool() bool {
@@ -71,9 +77,9 @@ func (m *textUnmarshalerAdapter) Decode(ctx *DecodeContext, target reflect.Value
return err
}
if target.Type().Implements(textUnmarshalerType) {
- return target.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)) // nolint
+ return target.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)) //nolint
}
- return target.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)) // nolint
+ return target.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)) //nolint
}
type binaryUnmarshalerAdapter struct{}
@@ -85,9 +91,9 @@ func (m *binaryUnmarshalerAdapter) Decode(ctx *DecodeContext, target reflect.Val
return err
}
if target.Type().Implements(binaryUnmarshalerType) {
- return target.Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(value)) // nolint
+ return target.Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(value)) //nolint
}
- return target.Addr().Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(value)) // nolint
+ return target.Addr().Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary([]byte(value)) //nolint
}
type jsonUnmarshalerAdapter struct{}
@@ -99,9 +105,9 @@ func (j *jsonUnmarshalerAdapter) Decode(ctx *DecodeContext, target reflect.Value
return err
}
if target.Type().Implements(jsonUnmarshalerType) {
- return target.Interface().(json.Unmarshaler).UnmarshalJSON([]byte(value)) // nolint
+ return target.Interface().(json.Unmarshaler).UnmarshalJSON([]byte(value)) //nolint
}
- return target.Addr().Interface().(json.Unmarshaler).UnmarshalJSON([]byte(value)) // nolint
+ return target.Addr().Interface().(json.Unmarshaler).UnmarshalJSON([]byte(value)) //nolint
}
// A Mapper represents how a field is mapped from command-line values to Go.
@@ -136,7 +142,7 @@ type BoolMapperExt interface {
// A MapperFunc is a single function that complies with the Mapper interface.
type MapperFunc func(ctx *DecodeContext, target reflect.Value) error
-func (m MapperFunc) Decode(ctx *DecodeContext, target reflect.Value) error { // nolint: revive
+func (m MapperFunc) Decode(ctx *DecodeContext, target reflect.Value) error { //nolint: revive
return m(ctx, target)
}
@@ -194,7 +200,7 @@ func (r *Registry) ForType(typ reflect.Type) Mapper {
for _, impl := range []reflect.Type{typ, reflect.PtrTo(typ)} {
if impl.Implements(mapperValueType) {
// FIXME: This should pass in the bool mapper.
- return &mapperValueAdapter{impl.Implements(boolMapperType)}
+ return &mapperValueAdapter{impl.Implements(boolMapperValueType)}
}
}
// Next, try explicitly registered types.
@@ -333,7 +339,7 @@ func durationDecoder() MapperFunc {
return fmt.Errorf("expected duration but got %q: %v", v, err)
}
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
- d = reflect.ValueOf(v).Convert(reflect.TypeOf(time.Duration(0))).Interface().(time.Duration) // nolint: forcetypeassert
+ d = reflect.ValueOf(v).Convert(reflect.TypeOf(time.Duration(0))).Interface().(time.Duration) //nolint: forcetypeassert
default:
return fmt.Errorf("expected duration but got %q", v)
}
@@ -361,7 +367,7 @@ func timeDecoder() MapperFunc {
}
}
-func intDecoder(bits int) MapperFunc { // nolint: dupl
+func intDecoder(bits int) MapperFunc { //nolint: dupl
return func(ctx *DecodeContext, target reflect.Value) error {
t, err := ctx.Scan.PopValue("int")
if err != nil {
@@ -390,7 +396,7 @@ func intDecoder(bits int) MapperFunc { // nolint: dupl
}
}
-func uintDecoder(bits int) MapperFunc { // nolint: dupl
+func uintDecoder(bits int) MapperFunc { //nolint: dupl
return func(ctx *DecodeContext, target reflect.Value) error {
t, err := ctx.Scan.PopValue("uint")
if err != nil {
@@ -534,7 +540,7 @@ func sliceDecoder(r *Registry) MapperFunc {
var childScanner *Scanner
if ctx.Value.Flag != nil {
t := ctx.Scan.Pop()
- // If decoding a flag, we need an value.
+ // If decoding a flag, we need a value.
if t.IsEOL() {
return fmt.Errorf("missing value, expecting \"%c...\"", sep)
}
@@ -574,6 +580,12 @@ func pathMapper(r *Registry) MapperFunc {
if target.Kind() == reflect.Slice {
return sliceDecoder(r)(ctx, target)
}
+ if target.Kind() == reflect.Ptr && target.Elem().Kind() == reflect.String {
+ if target.IsNil() {
+ return nil
+ }
+ target = target.Elem()
+ }
if target.Kind() != reflect.String {
return fmt.Errorf("\"path\" type must be applied to a string not %s", target.Type())
}
@@ -605,7 +617,7 @@ func fileMapper(r *Registry) MapperFunc {
file = os.Stdin
} else {
path = ExpandPath(path)
- file, err = os.Open(path) // nolint: gosec
+ file, err = os.Open(path) //nolint: gosec
if err != nil {
return err
}
@@ -629,7 +641,7 @@ func existingFileMapper(r *Registry) MapperFunc {
return err
}
- if !ctx.Value.Active || ctx.Value.Set {
+ if !ctx.Value.Active || (ctx.Value.Set && ctx.Value.Target.Type() == target.Type()) {
// early return to avoid checking extra files that may not exist;
// this hack only works because the value provided on the cli is
// checked before the default value is checked (if default is set).
@@ -665,7 +677,7 @@ func existingDirMapper(r *Registry) MapperFunc {
return err
}
- if !ctx.Value.Active || ctx.Value.Set {
+ if !ctx.Value.Active || (ctx.Value.Set && ctx.Value.Target.Type() == target.Type()) {
// early return to avoid checking extra dirs that may not exist;
// this hack only works because the value provided on the cli is
// checked before the default value is checked (if default is set).
@@ -706,11 +718,14 @@ func fileContentMapper(r *Registry) MapperFunc {
var data []byte
if path != "-" {
path = ExpandPath(path)
- data, err = ioutil.ReadFile(path) //nolint:gosec
+ data, err = os.ReadFile(path) //nolint:gosec
} else {
- data, err = ioutil.ReadAll(os.Stdin)
+ data, err = io.ReadAll(os.Stdin)
}
if err != nil {
+ if info, statErr := os.Stat(path); statErr == nil && info.IsDir() {
+ return fmt.Errorf("%q exists but is a directory: %w", path, err)
+ }
return err
}
target.SetBytes(data)
@@ -863,7 +878,7 @@ type NamedFileContentFlag struct {
Contents []byte
}
-func (f *NamedFileContentFlag) Decode(ctx *DecodeContext) error { // nolint: revive
+func (f *NamedFileContentFlag) Decode(ctx *DecodeContext) error { //nolint: revive
var filename string
err := ctx.Scan.PopValueInto("filename", &filename)
if err != nil {
@@ -875,7 +890,7 @@ func (f *NamedFileContentFlag) Decode(ctx *DecodeContext) error { // nolint: rev
return nil
}
filename = ExpandPath(filename)
- data, err := ioutil.ReadFile(filename) // nolint: gosec
+ data, err := os.ReadFile(filename) //nolint: gosec
if err != nil {
return fmt.Errorf("failed to open %q: %v", filename, err)
}
@@ -887,7 +902,7 @@ func (f *NamedFileContentFlag) Decode(ctx *DecodeContext) error { // nolint: rev
// FileContentFlag is a flag value that loads a file's contents into its value.
type FileContentFlag []byte
-func (f *FileContentFlag) Decode(ctx *DecodeContext) error { // nolint: revive
+func (f *FileContentFlag) Decode(ctx *DecodeContext) error { //nolint: revive
var filename string
err := ctx.Scan.PopValueInto("filename", &filename)
if err != nil {
@@ -899,7 +914,7 @@ func (f *FileContentFlag) Decode(ctx *DecodeContext) error { // nolint: revive
return nil
}
filename = ExpandPath(filename)
- data, err := ioutil.ReadFile(filename) // nolint: gosec
+ data, err := os.ReadFile(filename) //nolint: gosec
if err != nil {
return fmt.Errorf("failed to open %q: %v", filename, err)
}
diff --git a/vendor/github.com/alecthomas/kong/model.go b/vendor/github.com/alecthomas/kong/model.go
index 793cf97bcce..25ffe96be9f 100644
--- a/vendor/github.com/alecthomas/kong/model.go
+++ b/vendor/github.com/alecthomas/kong/model.go
@@ -162,6 +162,16 @@ func (n *Node) Summary() string {
} else if len(n.Children) > 0 {
summary += " "
}
+ allFlags := n.Flags
+ if n.Parent != nil {
+ allFlags = append(allFlags, n.Parent.Flags...)
+ }
+ for _, flag := range allFlags {
+ if !flag.Required {
+ summary += " [flags]"
+ break
+ }
+ }
return summary
}
@@ -229,23 +239,24 @@ func (n *Node) ClosestGroup() *Group {
// A Value is either a flag or a variable positional argument.
type Value struct {
- Flag *Flag // Nil if positional argument.
- Name string
- Help string
- OrigHelp string // Original help string, without interpolated variables.
- HasDefault bool
- Default string
- DefaultValue reflect.Value
- Enum string
- Mapper Mapper
- Tag *Tag
- Target reflect.Value
- Required bool
- Set bool // Set to true when this value is set through some mechanism.
- Format string // Formatting directive, if applicable.
- Position int // Position (for positional arguments).
- Passthrough bool // Set to true to stop flag parsing when encountered.
- Active bool // Denotes the value is part of an active branch in the CLI.
+ Flag *Flag // Nil if positional argument.
+ Name string
+ Help string
+ OrigHelp string // Original help string, without interpolated variables.
+ HasDefault bool
+ Default string
+ DefaultValue reflect.Value
+ Enum string
+ Mapper Mapper
+ Tag *Tag
+ Target reflect.Value
+ Required bool
+ Set bool // Set to true when this value is set through some mechanism.
+ Format string // Formatting directive, if applicable.
+ Position int // Position (for positional arguments).
+ Passthrough bool // Deprecated: Use PassthroughMode instead. Set to true to stop flag parsing when encountered.
+ PassthroughMode PassthroughMode //
+ Active bool // Denotes the value is part of an active branch in the CLI.
}
// EnumMap returns a map of the enums in this value.
@@ -368,9 +379,9 @@ func (v *Value) Reset() error {
v.Target.Set(reflect.Zero(v.Target.Type()))
if len(v.Tag.Envs) != 0 {
for _, env := range v.Tag.Envs {
- envar := os.Getenv(env)
+ envar, ok := os.LookupEnv(env)
// Parse the first non-empty ENV in the list
- if envar != "" {
+ if ok {
err := v.Parse(ScanFromTokens(Token{Type: FlagValueToken, Value: envar}), v.Target)
if err != nil {
return fmt.Errorf("%s (from envar %s=%q)", err, env, envar)
@@ -395,8 +406,10 @@ type Flag struct {
*Value
Group *Group // Logical grouping when displaying. May also be used by configuration loaders to group options logically.
Xor []string
+ And []string
PlaceHolder string
Envs []string
+ Aliases []string
Short rune
Hidden bool
Negated bool
@@ -490,6 +503,9 @@ func reflectValueIsZero(v reflect.Value) bool {
default:
// This should never happens, but will act as a safeguard for
// later, as a default value doesn't makes sense here.
- panic(&reflect.ValueError{"reflect.Value.IsZero", v.Kind()})
+ panic(&reflect.ValueError{
+ Method: "reflect.Value.IsZero",
+ Kind: v.Kind(),
+ })
}
}
diff --git a/vendor/github.com/alecthomas/kong/negatable.go b/vendor/github.com/alecthomas/kong/negatable.go
new file mode 100644
index 00000000000..7d8902a53d4
--- /dev/null
+++ b/vendor/github.com/alecthomas/kong/negatable.go
@@ -0,0 +1,19 @@
+package kong
+
+// negatableDefault is a placeholder value for the Negatable tag to indicate
+// the negated flag is --no-. This is needed as at the time of
+// parsing a tag, the field's flag name is not yet known.
+const negatableDefault = "_"
+
+// negatableFlagName returns the name of the flag for a negatable field, or
+// an empty string if the field is not negatable.
+func negatableFlagName(name, negation string) string {
+ switch negation {
+ case "":
+ return ""
+ case negatableDefault:
+ return "--no-" + name
+ default:
+ return "--" + negation
+ }
+}
diff --git a/vendor/github.com/alecthomas/kong/options.go b/vendor/github.com/alecthomas/kong/options.go
index 8d2893cf143..3bc991b9511 100644
--- a/vendor/github.com/alecthomas/kong/options.go
+++ b/vendor/github.com/alecthomas/kong/options.go
@@ -20,7 +20,7 @@ type Option interface {
// OptionFunc is function that adheres to the Option interface.
type OptionFunc func(k *Kong) error
-func (o OptionFunc) Apply(k *Kong) error { return o(k) } // nolint: revive
+func (o OptionFunc) Apply(k *Kong) error { return o(k) } //nolint: revive
// Vars sets the variables to use for interpolation into help strings and default values.
//
@@ -89,6 +89,10 @@ type dynamicCommand struct {
// "tags" is a list of extra tag strings to parse, in the form :"".
func DynamicCommand(name, help, group string, cmd interface{}, tags ...string) Option {
return OptionFunc(func(k *Kong) error {
+ if run := getMethod(reflect.Indirect(reflect.ValueOf(cmd)), "Run"); !run.IsValid() {
+ return fmt.Errorf("kong: DynamicCommand %q must be a type with a 'Run' method; got %T", name, cmd)
+ }
+
k.dynamicCommands = append(k.dynamicCommands, &dynamicCommand{
name: name,
help: help,
@@ -204,7 +208,11 @@ func BindTo(impl, iface interface{}) Option {
})
}
-// BindToProvider allows binding of provider functions.
+// BindToProvider binds an injected value to a provider function.
+//
+// The provider function must have the signature:
+//
+// func() (interface{}, error)
//
// This is useful when the Run() function of different commands require different values that may
// not all be initialisable from the main() function.
@@ -287,7 +295,7 @@ func AutoGroup(format func(parent Visitable, flag *Flag) *Group) Option {
// See also ExplicitGroups for a more structured alternative.
type Groups map[string]string
-func (g Groups) Apply(k *Kong) error { // nolint: revive
+func (g Groups) Apply(k *Kong) error { //nolint: revive
for key, info := range g {
lines := strings.Split(info, "\n")
title := strings.TrimSpace(lines[0])
diff --git a/vendor/github.com/alecthomas/kong/renovate.json5 b/vendor/github.com/alecthomas/kong/renovate.json5
new file mode 100644
index 00000000000..561d59fd876
--- /dev/null
+++ b/vendor/github.com/alecthomas/kong/renovate.json5
@@ -0,0 +1,18 @@
+{
+ $schema: "https://docs.renovatebot.com/renovate-schema.json",
+ extends: [
+ "config:recommended",
+ ":semanticCommits",
+ ":semanticCommitTypeAll(chore)",
+ ":semanticCommitScope(deps)",
+ "group:allNonMajor",
+ "schedule:earlyMondays", // Run once a week.
+ ],
+ packageRules: [
+ {
+ "matchPackageNames": ["golangci-lint"],
+ "matchManagers": ["hermit"],
+ "enabled": false
+ },
+ ]
+}
diff --git a/vendor/github.com/alecthomas/kong/resolver.go b/vendor/github.com/alecthomas/kong/resolver.go
index ac1de1fccd1..dca4309f7ff 100644
--- a/vendor/github.com/alecthomas/kong/resolver.go
+++ b/vendor/github.com/alecthomas/kong/resolver.go
@@ -22,10 +22,10 @@ type ResolverFunc func(context *Context, parent *Path, flag *Flag) (interface{},
var _ Resolver = ResolverFunc(nil)
-func (r ResolverFunc) Resolve(context *Context, parent *Path, flag *Flag) (interface{}, error) { // nolint: revive
+func (r ResolverFunc) Resolve(context *Context, parent *Path, flag *Flag) (interface{}, error) { //nolint: revive
return r(context, parent, flag)
}
-func (r ResolverFunc) Validate(app *Application) error { return nil } // nolint: revive
+func (r ResolverFunc) Validate(app *Application) error { return nil } //nolint: revive
// JSON returns a Resolver that retrieves values from a JSON source.
//
@@ -63,6 +63,6 @@ func JSON(r io.Reader) (Resolver, error) {
}
func snakeCase(name string) string {
- name = strings.Join(strings.Split(strings.Title(name), "-"), "") //nolint: staticcheck
+ name = strings.Join(strings.Split(strings.Title(name), "-"), "")
return strings.ToLower(name[:1]) + name[1:]
}
diff --git a/vendor/github.com/alecthomas/kong/scanner.go b/vendor/github.com/alecthomas/kong/scanner.go
index 1766c4b20d2..c8a8bd60dbb 100644
--- a/vendor/github.com/alecthomas/kong/scanner.go
+++ b/vendor/github.com/alecthomas/kong/scanner.go
@@ -82,7 +82,7 @@ func (t Token) InferredType() TokenType {
return t.Type
}
if v, ok := t.Value.(string); ok {
- if strings.HasPrefix(v, "--") { // nolint: gocritic
+ if strings.HasPrefix(v, "--") { //nolint: gocritic
return FlagToken
} else if v == "-" {
return PositionalArgumentToken
@@ -109,7 +109,7 @@ func (t Token) IsValue() bool {
//
// For example, the token "--foo=bar" will be split into the following by the parser:
//
-// [{FlagToken, "foo"}, {FlagValueToken, "bar"}]
+// [{FlagToken, "foo"}, {FlagValueToken, "bar"}]
type Scanner struct {
args []Token
}
diff --git a/vendor/github.com/alecthomas/kong/tag.go b/vendor/github.com/alecthomas/kong/tag.go
index f99059beab9..226171b6b78 100644
--- a/vendor/github.com/alecthomas/kong/tag.go
+++ b/vendor/github.com/alecthomas/kong/tag.go
@@ -9,36 +9,50 @@ import (
"unicode/utf8"
)
+// PassthroughMode indicates how parameters are passed through when "passthrough" is set.
+type PassthroughMode int
+
+const (
+ // PassThroughModeNone indicates passthrough mode is disabled.
+ PassThroughModeNone PassthroughMode = iota
+ // PassThroughModeAll indicates that all parameters, including flags, are passed through. It is the default.
+ PassThroughModeAll
+ // PassThroughModePartial will validate flags until the first positional argument is encountered, then pass through all remaining positional arguments.
+ PassThroughModePartial
+)
+
// Tag represents the parsed state of Kong tags in a struct field tag.
type Tag struct {
- Ignored bool // Field is ignored by Kong. ie. kong:"-"
- Cmd bool
- Arg bool
- Required bool
- Optional bool
- Name string
- Help string
- Type string
- TypeName string
- HasDefault bool
- Default string
- Format string
- PlaceHolder string
- Envs []string
- Short rune
- Hidden bool
- Sep rune
- MapSep rune
- Enum string
- Group string
- Xor []string
- Vars Vars
- Prefix string // Optional prefix on anonymous structs. All sub-flags will have this prefix.
- EnvPrefix string
- Embed bool
- Aliases []string
- Negatable bool
- Passthrough bool
+ Ignored bool // Field is ignored by Kong. ie. kong:"-"
+ Cmd bool
+ Arg bool
+ Required bool
+ Optional bool
+ Name string
+ Help string
+ Type string
+ TypeName string
+ HasDefault bool
+ Default string
+ Format string
+ PlaceHolder string
+ Envs []string
+ Short rune
+ Hidden bool
+ Sep rune
+ MapSep rune
+ Enum string
+ Group string
+ Xor []string
+ And []string
+ Vars Vars
+ Prefix string // Optional prefix on anonymous structs. All sub-flags will have this prefix.
+ EnvPrefix string
+ Embed bool
+ Aliases []string
+ Negatable string
+ Passthrough bool // Deprecated: use PassthroughMode instead.
+ PassthroughMode PassthroughMode
// Storage for all tag keys for arbitrary lookups.
items map[string][]string
@@ -62,7 +76,7 @@ type tagChars struct {
var kongChars = tagChars{sep: ',', quote: '\'', assign: '=', needsUnquote: false}
var bareChars = tagChars{sep: ' ', quote: '"', assign: ':', needsUnquote: true}
-// nolint:gocyclo
+//nolint:gocyclo
func parseTagItems(tagString string, chr tagChars) (map[string][]string, error) {
d := map[string][]string{}
key := []rune{}
@@ -203,7 +217,7 @@ func parseTag(parent reflect.Value, ft reflect.StructField) (*Tag, error) {
return t, nil
}
-func hydrateTag(t *Tag, typ reflect.Type) error { // nolint: gocyclo
+func hydrateTag(t *Tag, typ reflect.Type) error { //nolint: gocyclo
var typeName string
var isBool bool
var isBoolPtr bool
@@ -249,14 +263,22 @@ func hydrateTag(t *Tag, typ reflect.Type) error { // nolint: gocyclo
for _, xor := range t.GetAll("xor") {
t.Xor = append(t.Xor, strings.FieldsFunc(xor, tagSplitFn)...)
}
+ for _, and := range t.GetAll("and") {
+ t.And = append(t.And, strings.FieldsFunc(and, tagSplitFn)...)
+ }
t.Prefix = t.Get("prefix")
t.EnvPrefix = t.Get("envprefix")
t.Embed = t.Has("embed")
- negatable := t.Has("negatable")
- if negatable && !isBool && !isBoolPtr {
- return fmt.Errorf("negatable can only be set on booleans")
+ if t.Has("negatable") {
+ if !isBool && !isBoolPtr {
+ return fmt.Errorf("negatable can only be set on booleans")
+ }
+ negatable := t.Get("negatable")
+ if negatable == "" {
+ negatable = negatableDefault // placeholder for default negation of --no-
+ }
+ t.Negatable = negatable
}
- t.Negatable = negatable
aliases := t.Get("aliases")
if len(aliases) > 0 {
t.Aliases = append(t.Aliases, strings.FieldsFunc(aliases, tagSplitFn)...)
@@ -280,6 +302,17 @@ func hydrateTag(t *Tag, typ reflect.Type) error { // nolint: gocyclo
return fmt.Errorf("passthrough only makes sense for positional arguments or commands")
}
t.Passthrough = passthrough
+ if t.Passthrough {
+ passthroughMode := t.Get("passthrough")
+ switch passthroughMode {
+ case "partial":
+ t.PassthroughMode = PassThroughModePartial
+ case "all", "":
+ t.PassthroughMode = PassThroughModeAll
+ default:
+ return fmt.Errorf("invalid passthrough mode %q, must be one of 'partial' or 'all'", passthroughMode)
+ }
+ }
return nil
}
diff --git a/vendor/github.com/alecthomas/kong/util.go b/vendor/github.com/alecthomas/kong/util.go
index 50b1dfef786..8b7066429b8 100644
--- a/vendor/github.com/alecthomas/kong/util.go
+++ b/vendor/github.com/alecthomas/kong/util.go
@@ -17,7 +17,7 @@ func (c ConfigFlag) BeforeResolve(kong *Kong, ctx *Context, trace *Path) error {
if kong.loader == nil {
return fmt.Errorf("kong must be configured with kong.Configuration(...)")
}
- path := string(ctx.FlagValue(trace.Flag).(ConfigFlag)) // nolint
+ path := string(ctx.FlagValue(trace.Flag).(ConfigFlag)) //nolint
resolver, err := kong.LoadConfig(path)
if err != nil {
return err
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 217e7200ab0..a5922afde58 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -115,8 +115,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go/apps/public
# github.com/IBM/sarama v1.43.3
## explicit; go 1.19
github.com/IBM/sarama
-# github.com/alecthomas/kong v0.8.0
-## explicit; go 1.18
+# github.com/alecthomas/kong v1.6.0
+## explicit; go 1.20
github.com/alecthomas/kong
# github.com/alecthomas/participle/v2 v2.1.1
## explicit; go 1.18