package cmd import ( "bytes" "os" "path/filepath" "github.com/sirupsen/logrus" "github.com/urfave/cli/v3" "go.yaml.in/yaml/v3" ) const ( FlagConfig = "config" ) func ConfigFlags(defaultValue string) []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Name: FlagConfig, Aliases: []string{"c"}, Usage: "Configuration file path", Value: defaultValue, }, } } type ConfigWithIncludes interface { Includes() []string } func Load(logger *logrus.Logger, name string, config ConfigWithIncludes, parsed ...string) (err error) { if !filepath.IsAbs(name) { var abs string if abs, err = filepath.Abs(name); err != nil { return } logger.Tracef("config: canonicallize %s => %s", name, abs) name = abs } logger.Tracef("config: parsed %s", parsed) logger.Debugf("config: parse %s", name) var data []byte if data, err = os.ReadFile(name); err != nil { return } decoder := yaml.NewDecoder(bytes.NewBuffer(data)) decoder.KnownFields(true) if err = decoder.Decode(config); err != nil { return err } for _, include := range config.Includes() { if contains(parsed, include) { continue } if !filepath.IsAbs(include) { abs := filepath.Clean(filepath.Join(filepath.Dir(name), include)) logger.Tracef("config: canonicallize %s => %s", include, abs) include = abs } parsed = append(parsed, include) if err = Load(logger, include, config, parsed...); err != nil { return } } return nil } func contains(ss []string, s string) bool { for _, v := range ss { if v == s { return true } } return false }