-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add cli package with envconfig parser
- Loading branch information
1 parent
a33d034
commit d920e5d
Showing
4 changed files
with
218 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package cli | ||
|
||
import ( | ||
"github.com/kelseyhightower/envconfig" | ||
"github.com/spf13/pflag" | ||
) | ||
|
||
// EnvconfigProcessWithPflags can be used to run envconfig.Process without stomping on flags | ||
// passed to the command line, since explicit flags should take precedence over the environment. | ||
func EnvconfigProcessWithPflags(prefix string, flags *pflag.FlagSet, obj any) error { | ||
// discover all non-default flags before processing envconfig | ||
var changedFlags = map[string]string{} | ||
|
||
flags.VisitAll(func(f *pflag.Flag) { | ||
// only include non-default flags | ||
if f.Changed { | ||
changedFlags[f.Name] = f.Value.String() | ||
} | ||
}) | ||
|
||
// apply envconfig values | ||
if err := envconfig.Process(prefix, obj); err != nil { | ||
return err | ||
} | ||
|
||
// re-apply changed flags to override environment | ||
for name, value := range changedFlags { | ||
if err := flags.Lookup(name).Value.Set(value); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
package cli_test | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/kanopy-platform/go-library/cli" | ||
"github.com/spf13/cobra" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
type testCLI struct { | ||
String string | ||
Int int | ||
SubString string `split_words:"true"` | ||
OverrideFlagRoot string `split_words:"true"` | ||
OverrideFlagSub string `split_words:"true"` | ||
} | ||
|
||
func (c *testCLI) RootCmd() *cobra.Command { | ||
emptyRun := func(_ *cobra.Command, _ []string) {} | ||
|
||
root := &cobra.Command{ | ||
Use: "root", | ||
Run: emptyRun, | ||
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { | ||
return cli.EnvconfigProcessWithPflags("APP", cmd.Flags(), c) | ||
}, | ||
} | ||
|
||
root.PersistentFlags().StringVar(&c.String, "string", "default", "") | ||
root.PersistentFlags().IntVar(&c.Int, "int", 1, "") | ||
root.PersistentFlags().StringVar(&c.OverrideFlagRoot, "override", "root", "") | ||
|
||
sub := &cobra.Command{Use: "sub", Run: emptyRun} | ||
sub.Flags().StringVar(&c.SubString, "substring", "default", "") | ||
sub.PersistentFlags().StringVar(&c.OverrideFlagSub, "override", "sub", "") | ||
|
||
root.AddCommand(sub) | ||
|
||
return root | ||
} | ||
|
||
// checks that the correct order of precedence is adhered to: flag > env > default | ||
func TestPrecedence(t *testing.T) { | ||
tests := []struct { | ||
desc string | ||
env map[string]string | ||
args []string | ||
want testCLI | ||
}{ | ||
{ | ||
desc: "test root defaults", | ||
want: testCLI{ | ||
String: "default", | ||
Int: 1, | ||
SubString: "default", | ||
OverrideFlagRoot: "root", | ||
OverrideFlagSub: "sub", | ||
}, | ||
}, | ||
{ | ||
desc: "test root override", | ||
args: []string{"--override=flag"}, | ||
want: testCLI{ | ||
String: "default", | ||
Int: 1, | ||
SubString: "default", | ||
OverrideFlagRoot: "flag", | ||
OverrideFlagSub: "sub", | ||
}, | ||
}, | ||
{ | ||
desc: "test sub defaults", | ||
args: []string{"sub"}, | ||
want: testCLI{ | ||
String: "default", | ||
Int: 1, | ||
SubString: "default", | ||
OverrideFlagRoot: "root", | ||
OverrideFlagSub: "sub", | ||
}, | ||
}, | ||
{ | ||
desc: "test sub env", | ||
args: []string{"sub"}, | ||
env: map[string]string{ | ||
"APP_STRING": "env", | ||
"APP_INT": "2", | ||
"APP_SUB_STRING": "env", | ||
"APP_OVERRIDE_FLAG_SUB": "env", | ||
}, | ||
want: testCLI{ | ||
String: "env", | ||
Int: 2, | ||
SubString: "env", | ||
OverrideFlagRoot: "root", | ||
OverrideFlagSub: "env", | ||
}, | ||
}, | ||
{ | ||
desc: "test sub flags", | ||
args: []string{ | ||
"sub", | ||
"--string=flag", | ||
"--int=3", | ||
"--substring=flag", | ||
"--override=flag", | ||
}, | ||
want: testCLI{ | ||
String: "flag", | ||
Int: 3, | ||
SubString: "flag", | ||
OverrideFlagRoot: "root", | ||
OverrideFlagSub: "flag", | ||
}, | ||
}, | ||
{ | ||
desc: "test sub flags > env", | ||
env: map[string]string{ | ||
"APP_STRING": "env", | ||
"APP_INT": "2", | ||
"APP_SUB_STRING": "env", | ||
"APP_OVERRIDE_FLAG_ROOT": "env", | ||
"APP_OVERRIDE_FLAG_SUB": "env", | ||
}, | ||
args: []string{ | ||
"sub", | ||
"--string=flag", | ||
"--int=3", | ||
"--substring=flag", | ||
"--override=flag", | ||
}, | ||
want: testCLI{ | ||
String: "flag", | ||
Int: 3, | ||
SubString: "flag", | ||
OverrideFlagRoot: "env", | ||
OverrideFlagSub: "flag", | ||
}, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
for k, v := range test.env { | ||
assert.NoError(t, os.Setenv(k, v)) | ||
} | ||
|
||
cli := &testCLI{} | ||
cmd := cli.RootCmd() | ||
cmd.SetArgs(test.args) | ||
assert.NoError(t, cmd.Execute()) | ||
|
||
assert.Equal(t, test.want, *cli, test.desc) | ||
|
||
for k := range test.env { | ||
assert.NoError(t, os.Unsetenv(k)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters