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

environment variable overrides are processed after the config struct is already initialized #34

Open
chrispickard opened this issue Jul 11, 2024 · 0 comments

Comments

@chrispickard
Copy link

chrispickard commented Jul 11, 2024

consider a config struct like

type Config struct {
	Foo *CustomFoo `fig:"foo_config_file"`
}

where CustomFoo:

type CustomFoo struct {
	Bar string `json:"bar"`
	Baz string `json:"baz"`
}

func (c *CustomFoo) UnmarshalString(s string) error {
	fmt.Println(s)
	file, err := os.ReadFile(s)
	if err != nil {
		return err
	}
	err = json.Unmarshal(file, &c)
	if err != nil {
		return err
	}
	return nil
}

if you attempt to fill CustomFoo by passing a filename to UnmarshalString, it should read that file, then unmarshal into the CustomFoo struct. However, if you attempt to override the config with environment variables, the struct will attempt to be built twice, the first time with the value from config.yaml and the second time with the environment variable. if UnmarshalString returns an error it fails the config loading process. I'm attaching a runnable reproducer at the bottom, but IMO fig should wait until it has merged the different sources of config before it attempts to actually construct the final struct it will return to the caller.

expected result:

the Config struct is generated with Foo.Bar and Foo.Baz populated

actual result:

the foo.json file referenced in config.yaml is opened (and doesn't exist), rather than the foo_custom.json file referenced in the environment variables

error: 1 error(s) decoding:

* error decoding 'foo_config_file': open foo.json: no such file or directory
exit status 1
full example
package main

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/kkyr/fig"
)

type CustomFoo struct {
	Bar string `json:"bar"`
	Baz string `json:"baz"`
}

func (c *CustomFoo) UnmarshalString(s string) error {
	fmt.Println(s)
	file, err := os.ReadFile(s)
	if err != nil {
		return err
	}
	err = json.Unmarshal(file, &c)
	if err != nil {
		return err
	}
	return nil
}

type Config struct {
	Foo *CustomFoo `fig:"foo_config_file"`
}

func main() {
	err := os.Setenv("MYAPP_FOO_CONFIG_FILE", "custom_foo.json")
	if err != nil {
		fmt.Printf("error: %v\n", err)
		os.Exit(1)
	}
	var cfg Config
	err = fig.Load(&cfg, fig.UseEnv("MYAPP"))
	if err != nil {
		fmt.Printf("error: %v\n", err)
		os.Exit(1)
	}
	fmt.Printf("%v\n", cfg)
}

config.yaml:

foo_config_file: "foo.json"

foo.json: non-existent

custom_foo.json:

{
  "bar": "hello",
  "baz": "world"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant