Consider empty environment variables as set
When assigning values to flags (also when interogatting via `context.(Global)IsSet`. For boolean flags, consider empty as `false`. Using `syscall.Getenv` rather than `os.LookupEnv` in order to support older Golang versions.
This commit is contained in:
parent
7a5dacbc41
commit
a00c3f5872
@ -2,9 +2,9 @@ package altsrc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
@ -237,13 +237,11 @@ func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourc
|
||||
func isEnvVarSet(envVars string) bool {
|
||||
for _, envVar := range strings.Split(envVars, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if _, ok := syscall.Getenv(envVar); ok {
|
||||
// TODO: Can't use this for bools as
|
||||
// set means that it was true or false based on
|
||||
// Bool flag type, should work for other types
|
||||
if len(envVal) > 0 {
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,9 @@ package cli
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Context is a type that is passed through to
|
||||
@ -91,7 +91,7 @@ func (c *Context) IsSet(name string) bool {
|
||||
|
||||
eachName(envVarValue.String(), func(envVar string) {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if _, ok := syscall.Getenv(envVar); ok {
|
||||
c.setFlags[name] = true
|
||||
return
|
||||
}
|
||||
|
@ -184,18 +184,22 @@ func TestContext_IsSet(t *testing.T) {
|
||||
// XXX Corresponds to hack in context.IsSet for flags with EnvVar field
|
||||
// Should be moved to `flag_test` in v2
|
||||
func TestContext_IsSet_fromEnv(t *testing.T) {
|
||||
var timeoutIsSet, tIsSet, noEnvVarIsSet, nIsSet bool
|
||||
var timeoutIsSet, tIsSet, noEnvVarIsSet, nIsSet, passwordIsSet, pIsSet bool
|
||||
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||
os.Setenv("APP_PASSWORD", "")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
||||
Float64Flag{Name: "password, p", EnvVar: "APP_PASSWORD"},
|
||||
Float64Flag{Name: "no-env-var, n"},
|
||||
},
|
||||
Action: func(ctx *Context) error {
|
||||
timeoutIsSet = ctx.IsSet("timeout")
|
||||
tIsSet = ctx.IsSet("t")
|
||||
passwordIsSet = ctx.IsSet("password")
|
||||
pIsSet = ctx.IsSet("p")
|
||||
noEnvVarIsSet = ctx.IsSet("no-env-var")
|
||||
nIsSet = ctx.IsSet("n")
|
||||
return nil
|
||||
@ -204,6 +208,8 @@ func TestContext_IsSet_fromEnv(t *testing.T) {
|
||||
a.Run([]string{"run"})
|
||||
expect(t, timeoutIsSet, true)
|
||||
expect(t, tIsSet, true)
|
||||
expect(t, passwordIsSet, true)
|
||||
expect(t, pIsSet, true)
|
||||
expect(t, noEnvVarIsSet, false)
|
||||
expect(t, nIsSet, false)
|
||||
}
|
||||
|
38
flag.go
38
flag.go
@ -3,11 +3,11 @@ package cli
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -92,7 +92,7 @@ func (f GenericFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
val.Set(envVal)
|
||||
break
|
||||
}
|
||||
@ -128,7 +128,7 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
newVal := &StringSlice{}
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
@ -176,7 +176,7 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
newVal := &IntSlice{}
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
@ -227,7 +227,7 @@ func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
newVal := &Int64Slice{}
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
@ -256,7 +256,12 @@ func (f BoolFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
if envVal == "" {
|
||||
val = false
|
||||
break
|
||||
}
|
||||
|
||||
envValBool, err := strconv.ParseBool(envVal)
|
||||
if err == nil {
|
||||
val = envValBool
|
||||
@ -281,7 +286,12 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
if envVal == "" {
|
||||
val = false
|
||||
break
|
||||
}
|
||||
|
||||
envValBool, err := strconv.ParseBool(envVal)
|
||||
if err == nil {
|
||||
val = envValBool
|
||||
@ -305,7 +315,7 @@ func (f StringFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
f.Value = envVal
|
||||
break
|
||||
}
|
||||
@ -326,7 +336,7 @@ func (f IntFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
||||
if err == nil {
|
||||
f.Value = int(envValInt)
|
||||
@ -350,7 +360,7 @@ func (f Int64Flag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
||||
if err == nil {
|
||||
f.Value = envValInt
|
||||
@ -374,7 +384,7 @@ func (f UintFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
||||
if err == nil {
|
||||
f.Value = uint(envValInt)
|
||||
@ -398,7 +408,7 @@ func (f Uint64Flag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
||||
if err == nil {
|
||||
f.Value = uint64(envValInt)
|
||||
@ -422,7 +432,7 @@ func (f DurationFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValDuration, err := time.ParseDuration(envVal)
|
||||
if err == nil {
|
||||
f.Value = envValDuration
|
||||
@ -446,7 +456,7 @@ func (f Float64Flag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
||||
if err == nil {
|
||||
f.Value = float64(envValFloat)
|
||||
|
64
flag_test.go
64
flag_test.go
@ -29,6 +29,38 @@ func TestBoolFlagHelpOutput(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBoolFromEnv(t *testing.T) {
|
||||
var boolFlagTests = []struct {
|
||||
input string
|
||||
output bool
|
||||
}{
|
||||
{"", false},
|
||||
{"1", true},
|
||||
{"false", false},
|
||||
{"true", true},
|
||||
}
|
||||
|
||||
for _, test := range boolFlagTests {
|
||||
os.Clearenv()
|
||||
os.Setenv("DEBUG", test.input)
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
BoolFlag{Name: "debug, d", EnvVar: "DEBUG"},
|
||||
},
|
||||
Action: func(ctx *Context) error {
|
||||
if ctx.Bool("debug") != test.output {
|
||||
t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("debug"))
|
||||
}
|
||||
if ctx.Bool("d") != test.output {
|
||||
t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("d"))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
}
|
||||
|
||||
var stringFlagTests = []struct {
|
||||
name string
|
||||
usage string
|
||||
@ -941,6 +973,38 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) {
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseBoolTFromEnv(t *testing.T) {
|
||||
var boolTFlagTests = []struct {
|
||||
input string
|
||||
output bool
|
||||
}{
|
||||
{"", false},
|
||||
{"1", true},
|
||||
{"false", false},
|
||||
{"true", true},
|
||||
}
|
||||
|
||||
for _, test := range boolTFlagTests {
|
||||
os.Clearenv()
|
||||
os.Setenv("DEBUG", test.input)
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
BoolTFlag{Name: "debug, d", EnvVar: "DEBUG"},
|
||||
},
|
||||
Action: func(ctx *Context) error {
|
||||
if ctx.Bool("debug") != test.output {
|
||||
t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("debug"))
|
||||
}
|
||||
if ctx.Bool("d") != test.output {
|
||||
t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("d"))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMultiBoolT(t *testing.T) {
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
|
Loading…
Reference in New Issue
Block a user