package config import ( "strings" "time" "github.com/spf13/viper" "go.bigb.es/auxilia/culpa" ) // Load reads YAML config from path, overlays APP_* env vars, and validates. // // Strict mode is enabled — unknown keys cause a load failure rather than // being silently ignored, which catches typos in production config. func Load(path string) (*Config, error) { v := viper.New() v.SetConfigFile(path) v.SetConfigType("yaml") v.SetEnvPrefix("APP") v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) v.AutomaticEnv() // Defaults — keep these in sync with config.example.yaml. v.SetDefault("server.host", "0.0.0.0") v.SetDefault("server.port", 8080) v.SetDefault("server.read_timeout", 15*time.Second) v.SetDefault("server.write_timeout", 15*time.Second) v.SetDefault("log.level", "info") v.SetDefault("log.format", "human") v.SetDefault("search.default_provider", "gh") if err := v.ReadInConfig(); err != nil { return nil, culpa.Wrap(err, "read config") } var cfg Config if err := v.UnmarshalExact(&cfg); err != nil { return nil, culpa.Wrap(err, "unmarshal config") } if err := cfg.Validate(); err != nil { return nil, culpa.Wrap(err, "validate config") } return &cfg, nil }