all repos

mugit @ 8703591

🐮 git server that your cow will love
3 files changed, 55 insertions(+), 98 deletions(-)
config: file path validating strategy
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-02-13 00:07:29 +0200
Change ID: lwxymwsrprsnylporrqztkzquyxktwpv
Parent: c9e6e1a
M internal/cli/cli.go

@@ -35,7 +35,8 @@ Usage: "path to config file",

}, }, Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) { - loadedCfg, err := config.Load(cmd.String("config")) + loadedCfg, err := config.Load( + config.PathOrDefault(cmd.String("config"))) if err != nil { return ctx, err }
M internal/config/config.go

@@ -49,18 +49,8 @@ SSH SSHConfig `yaml:"ssh"`

Mirror MirrorConfig `yaml:"mirror"` } -// Load loads configuration with the following priority: -// 1. User provided fpath (if provided and exists) -// 2. /var/lib/mugit/config.yaml -// 3. $XDG_CONFIG_HOME/mugit/config.yaml or $HOME/.config/mugit/config.yaml func Load(fpath string) (*Config, error) { - // 4. /etc/mugit/config.yaml - configPath, err := findConfigFile(fpath) - if err != nil { - return nil, err - } - - configBytes, err := os.ReadFile(configPath) + configBytes, err := os.ReadFile(fpath) if err != nil { return nil, err }

@@ -83,6 +73,32 @@

return &config, nil } +// PathOrDefault uses userPath, if it's "", or invalid path, will default to one of those(in priority order) +// 1. ./config.yaml +// 2. /etc/mugit.yaml +// 3. /var/lib/mugit/config.yaml +func PathOrDefault(userPath string) string { + return pathOrDefaultWithCandidates(userPath, []string{ + "./config.yaml", + "/etc/mugit.yaml", + "/var/lib/mugit/config.yaml", + }) +} + +func pathOrDefaultWithCandidates(path string, candidates []string) string { + if isFileExists(path) { + return path + } + + for _, fpath := range candidates { + if isFileExists(fpath) { + return fpath + } + } + + return "" +} + func (c *Config) ensureDefaults() { // ports if c.Server.Port == 0 {

@@ -116,33 +132,6 @@ // mirroring

if c.Mirror.Interval == "" { c.Mirror.Interval = "8h" } -} - -func findConfigFile(userPath string) (string, error) { - if userPath != "" { - if _, err := os.Stat(userPath); err == nil { - return userPath, nil - } - } - - path := "/var/lib/mugit/config.yaml" - if _, err := os.Stat(path); err == nil { - return path, nil - } - - if configDir, err := os.UserConfigDir(); err == nil { - p := filepath.Join(configDir, "mugit", "config.yaml") - if _, err := os.Stat(p); err == nil { - return p, nil - } - } - - path = "/etc/mugit/config.yaml" - if _, err := os.Stat(path); err == nil { - return path, nil - } - - return "", ErrConfigNotFound } func isFileExists(path string) bool {
M internal/config/config_test.go

@@ -8,71 +8,38 @@

"olexsmir.xyz/x/is" ) -func TestFindConfigFile(t *testing.T) { - t.Run("returns user provided path when it exists", func(t *testing.T) { - path, err := findConfigFile("testdata/hostkey") - is.Err(t, err, nil) - is.Equal(t, path, "testdata/hostkey") - }) +func TestPathOrDefaultWithCandidates(t *testing.T) { + first := candidateFile(t, "first.yaml") + second := candidateFile(t, "second.yaml") + third := candidateFile(t, "third.yaml") - t.Run("falls back when user path doesn't exist", func(t *testing.T) { - path, err := findConfigFile("/nonexistent/user/config.yaml") - if err != nil { - is.Err(t, err, ErrConfigNotFound) - } else { - _, statErr := os.Stat(path) - is.Err(t, statErr, nil) + t.Run("returns user path when exists", func(t *testing.T) { + userPath := candidateFile(t, "user.yaml") + candidates := []string{first, second, third} + got := pathOrDefaultWithCandidates(userPath, candidates) + if got != userPath { + t.Errorf("got %q, want %q", got, userPath) } }) - t.Run("finds config in user config directory", func(t *testing.T) { - tmpDir := t.TempDir() - configDir := filepath.Join(tmpDir, "mugit") - if err := os.MkdirAll(configDir, 0o755); err != nil { - t.Fatal(err) - } - configFile := filepath.Join(configDir, "config.yaml") - if err := os.WriteFile(configFile, []byte("test"), 0o644); err != nil { - t.Fatal(err) - } - - t.Setenv("XDG_CONFIG_HOME", tmpDir) - - path, err := findConfigFile("") - is.Err(t, err, nil) - is.Equal(t, path, configFile) + t.Run("returns first existing candidate", func(t *testing.T) { + candidates := []string{first, second, third} + got := pathOrDefaultWithCandidates("", candidates) + is.Equal(t, got, first) }) - t.Run("returns error when no config found anywhere", func(t *testing.T) { - t.Setenv("XDG_CONFIG_HOME", "/nonexistent") - t.Setenv("HOME", "/nonexistent") - - path, err := findConfigFile("/nonexistent/config.yaml") - is.Err(t, err, ErrConfigNotFound) - is.Equal(t, path, "") - }) - - t.Run("prefers data directory over user config", func(t *testing.T) { - tmpDir := t.TempDir() - configDir := filepath.Join(tmpDir, "mugit") - if err := os.MkdirAll(configDir, 0o755); err != nil { - t.Fatal(err) - } - userConfigFile := filepath.Join(configDir, "config.yaml") - if err := os.WriteFile(userConfigFile, []byte("user config"), 0o644); err != nil { - t.Fatal(err) - } - - t.Setenv("XDG_CONFIG_HOME", tmpDir) - - path, err := findConfigFile("") - is.Err(t, err, nil) - - if path == "/var/lib/mugit/config.yaml" { - _, statErr := os.Stat(path) - is.Err(t, statErr, nil) - } else { - is.Equal(t, path, userConfigFile) + t.Run("returns empty when nothing exists", func(t *testing.T) { + candidates := []string{} + got := pathOrDefaultWithCandidates("", candidates) + if got != "" { + t.Errorf("got %q, want empty", got) } }) } + +func candidateFile(t *testing.T, name string) string { + t.Helper() + out := filepath.Join(t.TempDir(), name) + os.WriteFile(out, []byte("test"), 0o644) + return out +}