Repository: spf13/viper
Pull request #2004 (2025-03-25 18:52:21)

Update update guide with `mapstructure` package replacement.
relates to https://github.com/spf13/viper/issues/1999

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/spf13/viper?pullRequest=2004) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/spf13/viper?pullRequest=2004) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/spf13/viper?pullRequest=2004) it.</sub>
aldas: @sagikazarmark please review
sagikazarmark: Thanks @aldas 

Patch:
diff --git a/UPGRADE.md b/UPGRADE.md
index ccf413e..a33c965 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -83,6 +83,27 @@ v := viper.NewWithOptions(
 )
 ```
 
+### BREAKING: "github.com/mitchellh/mapstructure" depedency replaced
+
+The original [mapstructure](https://github.com/mitchellh/mapstructure) has been [archived](https://github.com/mitchellh/mapstructure/issues/349) and was replaced with a [fork](https://github.com/go-viper/mapstructure) maintained by Viper ([#1723](https://github.com/spf13/viper/pull/1723)).
+
+As a result, the package import path needs to be changed in cases where `mapstructure` is directly referenced in your code.
+
+For example, when providing a custom decoder config:
+
+```go
+err := viper.Unmarshal(&appConfig, func(config *mapstructure.DecoderConfig) {
+	config.TagName = "yaml"
+})
+```
+
+The change is fairly straightforward, just replace all occurrences of the import path `github.com/mitchellh/mapstructure` with `github.com/go-viper/mapstructure/v2`:
+
+```diff
+- import "github.com/mitchellh/mapstructure"
++ import "github.com/go-viper/mapstructure/v2"
+```
+
 ### BREAKING: HCL, Java properties, INI removed from core
 
 In order to reduce third-party dependencies, Viper dropped support for the following formats from the core:
diff --git a/viper.go b/viper.go
index f900e58..374fe74 100644
--- a/viper.go
+++ b/viper.go
@@ -1535,8 +1535,8 @@ func (v *Viper) MergeInConfig() error {
 func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
 
 func (v *Viper) ReadConfig(in io.Reader) error {
-	if v.configType == "" {
-		return errors.New("cannot decode configuration: config type is not set")
+	if v.getConfigType() == "" {
+		return errors.New("cannot decode configuration: unable to get config type from configType or file extension")
 	}
 
 	v.config = make(map[string]any)
@@ -1547,8 +1547,8 @@ func (v *Viper) ReadConfig(in io.Reader) error {
 func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
 
 func (v *Viper) MergeConfig(in io.Reader) error {
-	if v.configType == "" {
-		return errors.New("cannot decode configuration: config type is not set")
+	if v.getConfigType() == "" {
+		return errors.New("cannot decode configuration: unable to get config type from configType or file extension")
 	}
 
 	cfg := make(map[string]any)


Test patch:
diff --git a/viper_test.go b/viper_test.go
index c0c0e7c..f7e0787 100644
--- a/viper_test.go
+++ b/viper_test.go
@@ -1542,6 +1542,14 @@ func TestReadConfig(t *testing.T) {
 	})
 }
 
+func TestReadConfigWithSetConfigFile(t *testing.T) {
+	v := New()
+	v.SetConfigFile("config.yaml") // Dummy value to infer config type from file extension
+	err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc))
+	require.NoError(t, err)
+	assert.Equal(t, 45000, v.GetInt("hello.pop"))
+}
+
 func TestIsSet(t *testing.T) {
 	v := New()
 	v.SetConfigType("yaml")
@@ -2059,6 +2067,14 @@ func TestMergeConfig(t *testing.T) {
 	assert.Equal(t, "bar", v.GetString("fu"))
 }
 
+func TestMergeConfigWithSetConfigFile(t *testing.T) {
+	v := New()
+	v.SetConfigFile("config.yaml") // Dummy value to infer config type from file extension
+	err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc))
+	require.NoError(t, err)
+	assert.Equal(t, 45000, v.GetInt("hello.pop"))
+}
+
 func TestMergeConfigOverrideType(t *testing.T) {
 	v := New()
 	v.SetConfigType("json")