I've written a simple CLI tool using cobra and viper. I've recently been refactoring it to avoid package global variables, largely because it turned out to be difficult to test using the layout suggested by e.g. cobra init
.
Instead of...
var rootCmd = &cobra.Command{
...
}
func main() {
rootCmd.Execute()
}
I've got something more like:
func NewCmdRoot() *cobra.Command {
cmd := &cobra.Command{
...
}
return cmd
}
func main() {
rootCmd := NewCmdRoot()
rootCmd.Execute()
}
This has actually worked out great, and makes it much easier for tests
to start with a clean set of cli options. I'm running into some
difficulties integrating Viper into the new scheme. If I only care
about the root command, I can set things up in a PersistentPreRun
command, like this:
func initConfig(cmd *cobra.Command) {
config := viper.New()
rootCmd := cmd.Root()
config.BindPFlag("my-nifty-option", rootCmd.Flag("my-nifty-option")); err != nil {
// ...stuff happens here...
config.ReadInConfig()
// What happens next?
}
func NewCmdRoot() *cobra.Command {
cmd := &cobra.Command{
PersistentPreRun: func(cmd *cobra.Command, args []string) {
initConfig(cmd)
},
}
This sort of works: as long as I'm only interested in config options
that correspond to Cobra command line options, things works as
expected. But what if I want access to the config
variable itself?
I'm unsure how to expose the config
variable outside of the
initConfig
method without turning it into a package global. I'd like
the possibility of instantiating multiple command trees, each with
it's own isolated Viper config object, but I'm unclear where to put
it.
The cobra team did that recently, see https://github.com/spf13/cobra/pull/1551