jessevdk/go-flags

Manage unset flag/environment variable

matteodisabatino opened this issue · 3 comments

Hi, I noticed a "strange behaviour" with flags and environment variables. This is my configuration:

type Config struct {
	MyRequiredField string `env:"MY_REQUIRED_FIELD" long:"my-required-field" required:"true"`
}

func GetConfig(args []string) Config {
	var c Config
	if _, err := flags.ParseArgs(&c, args); err != nil {
		panic(err)
	}

	return c
}

Now, if I launch program both this way: go run my_program.go --my-required-field=, and this other MY_REQUIRED_FIELD= go run my_program.go I receive no errors.

I know that both flag (first case) and environment variable (second case) are passed to the program, but they are unset. Shouldn't this be an error?

Not the author, but from a logic perspective, no- because the arguments were provided and thus satisfy the required constraint, they're just empty strings. Since the zero value of a string is, well, an empty string, this means that that constraint (at the least in theory) will always evaluate to true/pass.

Implement a second round of validation with something with more flexible options that uses a different tag name. I recommend https://pkg.go.dev/github.com/go-playground/validator/v10 and using the tag validate:"required" (this "required" behavior matches your expectations more closely) or validate:"min=1".

Alternatively, just perform the additional validation yourself (if c.MyRequiredField == "", if len(c.MyRequiredField) == 0, if strings.TrimSpace(c.MyRequiredField) == "", etc.).

For a more visual example of why this happens:

# Ensure a clean test environment.
unset TEST_ENVVAR
# Show that it is not set.
env | grep -E '^TEST_ENVVAR='
# Show that it IS set, just to an empty string.
TEST_ENVVAR= env | grep -E '^TEST_ENVVAR='
# Likewise to display it is, in fact, an actual empty string and not *unset*, as these are different.
export TEST_ENVVAR=
echo "|${TEST_ENVVAR}|"

What might be confusing you is there is a strict distinction between an environment variable not being set vs. being set to an empty string. TEST_ENVVAR= sets it to an empty string, which is in fact a value. (Kind of like in Go, testVar := "" IS still a value, it's just an empty string.)

@nf-brentsaner thanks for your explanation