diff --git a/config/config.go b/config/config.go index 40b77d1339cf425df810c4314135c7cde80e5621..1928bdbf513429008e38468b929dc28e7327d8d0 100644 --- a/config/config.go +++ b/config/config.go @@ -27,6 +27,11 @@ import ( "github.com/go-git/go-git/v5/storage/memory" ) +var ( + // ErrNoConfig is returned when a repo has no config file. + ErrNoConfig = errors.New("no config file found") +) + // Config is the Soft Serve configuration. type Config struct { Name string `yaml:"name" json:"name"` @@ -124,31 +129,40 @@ func NewConfig(cfg *config.Config) (*Config, error) { return c, nil } +// readConfig reads the config file for the repo. All config files are stored in +// the config repo. func (cfg *Config) readConfig(repo string, v interface{}) error { - cr, err := cfg.Source.GetRepo(repo) + cr, err := cfg.Source.GetRepo("config") if err != nil { return err } - cy, _, err := cr.LatestFile(repo + ".yaml") - if err != nil && !errors.Is(err, git.ErrFileNotFound) { - return fmt.Errorf("error reading %s.yaml: %w", repo, err) + // Parse YAML files + var cy string + for _, ext := range []string{".yaml", ".yml"} { + cy, _, err = cr.LatestFile(repo + ext) + if err != nil && !errors.Is(err, git.ErrFileNotFound) { + return err + } else if err == nil { + break + } } + // Parse JSON files cj, _, err := cr.LatestFile(repo + ".json") if err != nil && !errors.Is(err, git.ErrFileNotFound) { - return fmt.Errorf("error reading %s.json: %w", repo, err) + return err } if cy != "" { err = yaml.Unmarshal([]byte(cy), v) if err != nil { - return fmt.Errorf("bad yaml in %s.yaml: %s", repo, err) + return err } } else if cj != "" { err = json.Unmarshal([]byte(cj), v) if err != nil { - return fmt.Errorf("bad json in %s.json: %s", repo, err) + return err } } else { - return fmt.Errorf("no config file found for %q", repo) + return ErrNoConfig } return nil } @@ -164,16 +178,41 @@ func (cfg *Config) Reload() error { if err := cfg.readConfig("config", cfg); err != nil { return fmt.Errorf("error reading config: %w", err) } + // sanitize repo configs + repos := make(map[string]RepoConfig, 0) + for _, r := range cfg.Repos { + repos[r.Repo] = r + } + for _, r := range cfg.Source.AllRepos() { + var rc RepoConfig + repo := r.Repo() + if repo == "config" { + continue + } + if err := cfg.readConfig(repo, &rc); err != nil { + if !errors.Is(err, ErrNoConfig) { + log.Printf("error reading config: %v", err) + } + continue + } + repos[r.Repo()] = rc + } + cfg.Repos = make([]RepoConfig, 0, len(repos)) + for n, r := range repos { + r.Repo = n + cfg.Repos = append(cfg.Repos, r) + } + // Populate readmes and descriptions for _, r := range cfg.Source.AllRepos() { - name := r.Name() + repo := r.Repo() err = r.UpdateServerInfo() if err != nil { - log.Printf("error updating server info for %s: %s", name, err) + log.Printf("error updating server info for %s: %s", repo, err) } pat := "README*" rp := "" for _, rr := range cfg.Repos { - if name == rr.Repo { + if repo == rr.Repo { rp = rr.Readme r.name = rr.Name r.description = rr.Note @@ -187,7 +226,7 @@ func (cfg *Config) Reload() error { rm := "" fc, fp, _ := r.LatestFile(pat) rm = fc - if name == "config" { + if repo == "config" { md, err := templatize(rm, cfg) if err != nil { return err