Viper: Default value of Cobra flag overrides the viper env variable

Created on 20 Mar 2019  路  5Comments  路  Source: spf13/viper

Hi,

I maybe mistaken here but i'm currently under the assumption that viper takes care of the order of precedence when it comes to where the value comes from: config file -> env var -> flag.

I'm finding that the default value that is used in the flag always overrides the env var. To overcome this i have to set the default value to be empty and then i have to check to see if the value is empty before using the env var value. Is this correct?

Here's the test code:

package cmd

import (
    "fmt"
    "os"

    "github.com/mitchellh/go-homedir"
    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

var defaultConfigFilename = "abc-config"
var defaultConfigExt = "json"
var defaultLogPath = "/var/logs/abc/"

func NewRootCommand() *cobra.Command {
    configFile := ""

    rootCmd := &cobra.Command{
        Use:   "test",
        Short: "Test",
        Long:  `Test`,
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println(configFile)
        },
    }

    initConfig := func() {

        if configFile == "" {
            // Try and get from env
            configFile = viper.GetString("configFile")
        }

        // Don't forget to read config either from configFile or from home directory!
        if configFile != "" {
            // Use config file from the flag.
            viper.SetConfigFile(configFile)
        } else {
            // Find home directory.
            home, err := homedir.Dir()
            if err != nil {
                fmt.Println(err)
                os.Exit(1)
            }

            // Search config in home directory with name "abc-config" (without extension).
            viper.AddConfigPath(home)
            viper.SetConfigName(defaultConfigFilename)
        }

        if err := viper.ReadInConfig(); err != nil {
            fmt.Println("Can't read config:", err)
            os.Exit(1)
        }
    }

    cobra.OnInitialize(initConfig)

    viper.SetEnvPrefix("ABC")
    viper.AutomaticEnv()

    rootCmd.Flags().StringVarP(&configFile, "configFile", "c", "", fmt.Sprintf("config file (default is ~/%s.%s)", defaultConfigFilename, defaultConfigExt))
    viper.BindPFlag("configFile", rootCmd.Flags().Lookup("configFile"))

    return rootCmd
}

Thanks,
Ankur

Most helpful comment

Why is this a known accepted behavior?!
Cobra integration with Viper is broken and can't be used with environment variable - it should be in big bold letters on the README

All 5 comments

Yup, currently you can't really do anything else, but not define a default value for flags.

Thank you for the quick response. I'll close this ticket since it's known. It might be worth mentioning in the readme... i guess i could change it and open a pull request too :)

Why is this a known accepted behavior?!
Cobra integration with Viper is broken and can't be used with environment variable - it should be in big bold letters on the README

I've been using viper and cobra together on multiple CLIs for a couple years now and with a bit of custom code you can get them to integrate such that the config stacks:

flags > environment variables > configuration files > flag defaults

I wrote a sample working app with tests that demonstrates how to integrate them and a blog post that explains it in a bit more detail. Apologies if I am misunderstanding the OP (if I did, let me know and I'll remove/edit this), but hopefully the sample helps them out.

@carolynvs Great works! Thanks!

Was this page helpful?
0 / 5 - 0 ratings