Skip to content

Commit 82bab17

Browse files
committed
bug(flags): required flags with default value still failed validation
When a required flag had a default value, execution still failed, saying that the flag has not been provided. This change fixes that behavior by ensuring that default values are taken under consideration when checking whether a flag is missing or not. NOTE: environment variables will still override default values.
1 parent 53de84a commit 82bab17

2 files changed

Lines changed: 47 additions & 7 deletions

File tree

execute_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,35 @@ Flags:
221221
With(t).Verify(rootPostRunHook.providedExitCode).Will(EqualTo(exitCode)).OrFail()
222222
})
223223

224+
t.Run("missing required flags fail execution", func(t *testing.T) {
225+
type ActionWithRequiredFlag struct {
226+
TrackingAction
227+
MyFlag string `required:"true"`
228+
}
229+
action := &ActionWithRequiredFlag{}
230+
ctx := context.Background()
231+
root := MustNew("cmd", "desc", "long desc", action, nil, nil)
232+
233+
b := &bytes.Buffer{}
234+
With(t).Verify(Execute(ctx, b, root, nil, nil)).Will(EqualTo(ExitCodeMisconfiguration)).OrFail()
235+
With(t).Verify(action.TrackingAction.callTime).Will(BeNil()).OrFail()
236+
With(t).Verify(b.String()).Will(EqualTo("required flag is missing: --my-flag\nUsage: cmd [--help] --my-flag=VALUE\n")).OrFail()
237+
})
238+
239+
t.Run("required flags with default value do not fail execution", func(t *testing.T) {
240+
type ActionWithRequiredFlag struct {
241+
TrackingAction
242+
MyFlag string `required:"true"`
243+
}
244+
action := &ActionWithRequiredFlag{
245+
MyFlag: "abc",
246+
}
247+
ctx := context.Background()
248+
root := MustNew("cmd", "desc", "long desc", action, nil, nil)
249+
250+
b := &bytes.Buffer{}
251+
With(t).Verify(Execute(ctx, b, root, nil, nil)).Will(EqualTo(ExitCodeSuccess)).OrFail()
252+
With(t).Verify(action.TrackingAction.callTime).Will(Not(BeNil())).OrFail()
253+
With(t).Verify(b.String()).Will(BeEmpty()).OrFail()
254+
})
224255
}

flag_set.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -363,20 +363,29 @@ func (fs *flagSet) apply(envVars map[string]string, args []string) error {
363363
// Iterate flags and define them in the stdlib FlagSet
364364
for _, mfd := range mergedFlagDefs {
365365

366-
// Set the value to the flag's corresponding environment variable, if one was given
367-
if v, found := envVars[*mfd.EnvVarName]; found {
368-
if err := mfd.setValue(v); err != nil {
369-
return err
370-
}
371-
}
372-
373366
// By definition, for the same name - all flags have the same "HasValue" value, so it should be safe to just
374367
// take it from the first one
375368
if mfd.HasValue {
369+
370+
// Set the field's default value so it's marked as "applied" (and thus the "required" validation will ignore it)
371+
if mfd.DefaultValue != "" {
372+
if err := mfd.setValue(mfd.DefaultValue); err != nil {
373+
return fmt.Errorf("failed applying default value for flag '%s': %w", mfd.Name, err)
374+
}
375+
}
376376
stdFs.Func(mfd.Name, "", func(v string) error { return mfd.setValue(v) })
377+
377378
} else {
378379
stdFs.BoolFunc(mfd.Name, "", func(string) error { return mfd.setValue("true") })
379380
}
381+
382+
// Set the value to the flag's corresponding environment variable, if one was given
383+
// Important this is done here, so it overrides the default value set earlier
384+
if v, found := envVars[*mfd.EnvVarName]; found {
385+
if err := mfd.setValue(v); err != nil {
386+
return err
387+
}
388+
}
380389
}
381390

382391
// Parse the given arguments, which will result in all CLI flags being set

0 commit comments

Comments
 (0)