go-flags icon indicating copy to clipboard operation
go-flags copied to clipboard

Manage unset flag/environment variable

Open matteodisabatino opened this issue 1 year ago • 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?

matteodisabatino avatar Feb 15 '24 15:02 matteodisabatino

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.).

nf-brentsaner avatar Jul 12 '24 21:07 nf-brentsaner

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 avatar Jul 12 '24 22:07 nf-brentsaner

@nf-brentsaner thanks for your explanation

matteodisabatino avatar Jul 13 '24 17:07 matteodisabatino