Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion kong.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func New(grammar any, options ...Option) (*Kong, error) {
},
}

options = append(options, Bind(k))
options = append(options, Bind(k), Resolvers(EnvResolver()))

for _, option := range options {
if err := option.Apply(k); err != nil {
Expand Down
14 changes: 0 additions & 14 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package kong
import (
"fmt"
"math"
"os"
"reflect"
"strconv"
"strings"
Expand Down Expand Up @@ -377,19 +376,6 @@ func (v *Value) ApplyDefault() error {
// Does not include resolvers.
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, ok := os.LookupEnv(env)
// Parse the first non-empty ENV in the list
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)
}
return nil
}
}
}
if v.HasDefault {
return v.Parse(ScanFromTokens(Token{Type: FlagValueToken, Value: v.Default}), v.Target)
}
Expand Down
68 changes: 68 additions & 0 deletions resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package kong

import (
"encoding/json"
"fmt"
"io"
"os"
"strings"
)

Expand Down Expand Up @@ -66,3 +68,69 @@ func snakeCase(name string) string {
name = strings.Join(strings.Split(strings.Title(name), "-"), "")
return strings.ToLower(name[:1]) + name[1:]
}

// EnvResolver provides a resolver for environment variables tags
func EnvResolver() Resolver {
// Resolvers are typically only invoked for flags, as shown here:
// https://github.com/alecthomas/kong/blob/v1.6.0/context.go#L567
// However, environment variable annotations can also apply to arguments,
// as demonstrated in this test:
// https://github.com/alecthomas/kong/blob/v1.6.0/kong_test.go#L1226-L1244
// To handle this, we ensure that arguments are resolved as well.
// Since the resolution only needs to happen once, we use this boolean
// to track whether the resolution process has already been performed.
argsResolved := false
return ResolverFunc(func(context *Context, parent *Path, flag *Flag) (interface{}, error) {
if !argsResolved {
if err := resolveArgs(context.Path); err != nil {
return nil, err
}
// once resolved we do not want to run this anymore
argsResolved = true
}
for _, env := range flag.Tag.Envs {
envar, ok := os.LookupEnv(env)
// Parse the first non-empty ENV in the list
if ok {
return envar, nil
}
}
return nil, nil
})
}

func resolveArgs(paths []*Path) error {
for _, path := range paths {
if path.Command == nil {
continue
}
for _, positional := range path.Command.Positional {
if positional.Tag == nil {
continue
}
if err := visitValue(positional); err != nil {
return err
}
}
if path.Command.Argument != nil {
if err := visitValue(path.Command.Argument); err != nil {
return err
}
}
}
return nil
}

func visitValue(value *Value) error {
for _, env := range value.Tag.Envs {
envar, ok := os.LookupEnv(env)
if !ok {
continue
}
token := Token{Type: FlagValueToken, Value: envar}
if err := value.Parse(ScanFromTokens(token), value.Target); err != nil {
return fmt.Errorf("%s (from envar %s=%q)", err, env, envar)
}
}
return nil
}