Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Viper does not unmarshal values passed from env or pflag #1365

Closed
3 tasks done
pantelis-karamolegkos opened this issue May 28, 2022 · 3 comments · Fixed by #1429
Closed
3 tasks done

Viper does not unmarshal values passed from env or pflag #1365

pantelis-karamolegkos opened this issue May 28, 2022 · 3 comments · Fixed by #1429
Labels
kind/bug Something isn't working

Comments

@pantelis-karamolegkos
Copy link

Preflight Checklist

  • I have searched the issue tracker for an issue that matches the one I want to file, without success.
  • I am not looking for support or already pursued the available support channels without success.
  • I have checked the troubleshooting guide for my problem, without success.

Viper Version

1.11.0

Go Version

1.17

Config Source

Flags, Environment variables

Format

No response

Repl.it link

No response

Code reproducing the issue

package main

import (
	"fmt"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

type Config struct {
	AFlag string `mapstructure:"A_FLAG"`
}

// rootCmd is the root cobra command of ormos-web
var rootCmd = &cobra.Command{
	Use:   "myapp",
	Short: "My app",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("running")
	},
}

func InitConfig(cmd *cobra.Command) {
	var conf Config
	v := viper.New()
	v.AddConfigPath(".")
	v.SetConfigName(".env")
	v.SetConfigType("env")
	v.SetEnvPrefix("FOO")
	v.AllowEmptyEnv(true)
	v.AutomaticEnv()
	v.BindPFlags(cmd.Flags())
	v.Unmarshal(&conf)
	fmt.Printf("%+v\n", conf)
}

func main() {
	rootCmd.PersistentFlags().StringP("a-flag", "", "", "a flag")
	InitConfig(rootCmd)
	rootCmd.Execute()
}

Expected Behavior

after

export FOO_A_FLAG=test

or either running as

go run main.go --a-flag=test

the program should be printing

{AFlag:test}
running

Actual Behavior

the program just prints

{AFlag:}
running

Steps To Reproduce

No response

Additional Information

No response

@pantelis-karamolegkos pantelis-karamolegkos added the kind/bug Something isn't working label May 28, 2022
@github-actions
Copy link

👋 Thanks for reporting!

A maintainer will take a look at your issue shortly. 👀

In the meantime: We are working on Viper v2 and we would love to hear your thoughts about what you like or don't like about Viper, so we can improve or fix those issues.

⏰ If you have a couple minutes, please take some time and share your thoughts: https://forms.gle/R6faU74qPRPAzchZ9

📣 If you've already given us your feedback, you can still help by spreading the news,
either by sharing the above link or telling people about this on Twitter:

https://twitter.com/sagikazarmark/status/1306904078967074816

Thank you! ❤️

@gucki
Copy link

gucki commented Nov 12, 2022

@pantelis-karamolegkos I think I hit the same bug. AutomaticEnv only really seems to work for me when the key has already a value, for example, from another source (ex. YAML) or a default. Otherwise, it's just ignored. So my solution/ workaround is to use define a default for all keys.

@ezdac
Copy link

ezdac commented Apr 12, 2023

I also encountered this issue, and there are several more open issues related to this.

AutomaticEnv is using viper.AllSettings() which then gets passed to a custom wrapper of mapstructure for unmarshaling all known keys into a struct, if those keys are present in the struct.
If you do an explicit viper.Get() or viper.IsSet() then the environment variables will be checked and recognized for the option, but AllSettings only iterates over keys viper already knows about (e.g. via viper.SetDefault()).

This can be made more explicit if the ErrorUnset mapstructure decoder config is active:

// pass this function to the viper.Unmarshal() call 
func DecoderErrorUnset(c *mapstructure.DecoderConfig) {
	c.ErrorUnset = true
}
func InitConfig(cmd *cobra.Command) {
	var conf Config
	v := viper.New()
         // ... other setup, see original post
	v.Unmarshal(&conf, DecoderErrorUnset)
}

In my opinion when unmarshaling into well defined structs, the struct should be introspected, and then viper should be queried if it knows about the configuration options. Currently it is the other way around - if viper doesn't know about a field in the first place, it will be simply ignored during unmarshaling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working
Projects
None yet
3 participants