diff --git a/matchers.go b/matchers.go index 6c585c9..2607102 100644 --- a/matchers.go +++ b/matchers.go @@ -53,7 +53,7 @@ type optMatcher struct { func (o *optMatcher) match(args []string, c *parseContext) (bool, []string) { if len(args) == 0 || c.rejectOptions { - return false, args + return o.theOne.valueSetFromEnv, args } idx := 0 @@ -63,7 +63,7 @@ func (o *optMatcher) match(args []string, c *parseContext) (bool, []string) { case arg == "-": idx++ case arg == "--": - return false, nil + return o.theOne.valueSetFromEnv, nil case strings.HasPrefix(arg, "--"): matched, consumed, nargs := o.matchLongOpt(args, idx, c) @@ -71,7 +71,7 @@ func (o *optMatcher) match(args []string, c *parseContext) (bool, []string) { return true, nargs } if consumed == 0 { - return false, args + return o.theOne.valueSetFromEnv, args } idx += consumed @@ -81,15 +81,15 @@ func (o *optMatcher) match(args []string, c *parseContext) (bool, []string) { return true, nargs } if consumed == 0 { - return false, args + return o.theOne.valueSetFromEnv, args } idx += consumed default: - return false, args + return o.theOne.valueSetFromEnv, args } } - return false, args + return o.theOne.valueSetFromEnv, args } func (o *optMatcher) matchLongOpt(args []string, idx int, c *parseContext) (bool, int, []string) { diff --git a/spec_n_parse_test.go b/spec_n_parse_test.go index 0909e72..d33b2ee 100644 --- a/spec_n_parse_test.go +++ b/spec_n_parse_test.go @@ -2,7 +2,10 @@ package cli import ( "flag" + "os" + "strings" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "testing" @@ -1194,3 +1197,92 @@ func TestWardDoesntRunTooSlowly(t *testing.T) { okCmd(t, spec, init, []string{"--min-length", "10", "--no-symbol", "--no-lower", "--length", "42", "--gen"}) } + +func TestEnvOverrideOk(t *testing.T) { + defer os.Unsetenv("envopt") + + cases := []struct { + setenv bool + spec string + args []string + envval string + }{ + // pickup the value from the environment variable + {true, "--envopt --other", []string{"--other", "otheropt"}, "fromenv"}, + {true, "[--envopt] --other", []string{"--other", "otheropt"}, "fromenv"}, + {true, "--envopt", []string{}, "fromenv"}, + {true, "--envopt", []string{"--"}, "fromenv"}, + + // override on command line + {true, "--envopt", []string{"-e", "fromopt"}, "fromopt"}, + {true, "--envopt", []string{"--envopt", "fromopt"}, "fromopt"}, + + // no env set + {false, "--envopt", []string{"--envopt", "fromopt"}, "fromopt"}, + {false, "--envopt", []string{"-e", "fromopt"}, "fromopt"}, + + // no env var, fallback to default + {false, "[--envopt]", []string{}, "envdefault"}, + {false, "[--envopt] --other", []string{"--other", "otheropt"}, "envdefault"}, + } + + for _, cas := range cases { + var envopt *string + var otheropt *string + + init := func(c *Cmd) { + os.Unsetenv("envopt") + if cas.setenv { + os.Setenv("envopt", "fromenv") + } + envopt = c.String(StringOpt{ + Name: "e envopt", + Value: "envdefault", + EnvVar: "envopt", + }) + if strings.Contains(cas.spec, "other") { + otheropt = c.StringOpt("o other", "", "") + } + } + okCmd(t, cas.spec, init, cas.args) + if strings.Contains(cas.spec, "other") { + // if the test spec defined --other, make sure it was actually set + assert.Equal(t, "otheropt", *otheropt) + } + // ensure --envopt was actually set to the test's expectations + assert.Equal(t, cas.envval, *envopt) + } +} + +// Test that not setting an environment variable correctly causes +// required options to fail if no value is supplied in args. +func TestEnvOverrideFail(t *testing.T) { + os.Unsetenv("envopt") + + cases := []struct { + spec string + args []string + envval string + }{ + // no env var, not optional; should fail + {"--envopt", []string{}, ""}, + {"--envopt --other", []string{"--other", "otheropt"}, ""}, + } + + for _, cas := range cases { + var envopt *string + var otheropt *string + + init := func(c *Cmd) { + envopt = c.String(StringOpt{ + Name: "e envopt", + Value: "envdefault", + EnvVar: "envopt", + }) + if strings.Contains(cas.spec, "other") { + otheropt = c.StringOpt("o other", "", "") + } + } + failCmd(t, cas.spec, init, cas.args) + } +}