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