Skip to content

Commit 56aaa9a

Browse files
committed
fix(config): deterministic home-dir fallback in Path()
os.UserHomeDir()'s error was discarded; when it fails (home env var unset) home became "", yielding a cwd-relative config path silently read/written in an unexpected location. New homeDir() helper falls back to the platform home env var (USERPROFILE/HOME), then os.TempDir(), so Path() is always absolute. No signature change — callers untouched. Regression tests added.
1 parent 2e926c9 commit 56aaa9a

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

internal/config/path.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func Path() string {
1515
if v := os.Getenv("XDG_CONFIG_HOME"); v != "" {
1616
return filepath.Join(v, "siphon", "config.yaml")
1717
}
18-
home, _ := os.UserHomeDir()
18+
home := homeDir()
1919
switch runtime.GOOS {
2020
case "windows":
2121
if v := os.Getenv("APPDATA"); v != "" {
@@ -26,3 +26,22 @@ func Path() string {
2626
return filepath.Join(home, ".config", "siphon", "config.yaml")
2727
}
2828
}
29+
30+
// homeDir resolves the user's home directory deterministically. os.UserHomeDir
31+
// only fails when the platform's home env var is unset; if that happens we fall
32+
// back to that env var directly and finally to os.TempDir(), so the result is
33+
// always an absolute path — never "" (which would yield a cwd-relative config
34+
// path silently written/read in an unexpected location).
35+
func homeDir() string {
36+
if h, err := os.UserHomeDir(); err == nil && h != "" {
37+
return h
38+
}
39+
envVar := "HOME"
40+
if runtime.GOOS == "windows" {
41+
envVar = "USERPROFILE"
42+
}
43+
if h := os.Getenv(envVar); h != "" {
44+
return h
45+
}
46+
return os.TempDir()
47+
}

internal/config/path_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package config
2+
3+
import (
4+
"path/filepath"
5+
"testing"
6+
)
7+
8+
// TestPath_AlwaysAbsolute is the regression for the ignored os.UserHomeDir
9+
// error: even when SIPHON_CONFIG_HOME / XDG_CONFIG_HOME are unset and home
10+
// resolution must fall back, Path() must return an absolute path — never a
11+
// cwd-relative one like ".config/siphon/config.yaml".
12+
func TestPath_AlwaysAbsolute(t *testing.T) {
13+
t.Setenv("SIPHON_CONFIG_HOME", "")
14+
t.Setenv("XDG_CONFIG_HOME", "")
15+
16+
got := Path()
17+
if !filepath.IsAbs(got) {
18+
t.Fatalf("Path() = %q; want an absolute path", got)
19+
}
20+
}
21+
22+
// TestHomeDir_FallsBackWhenHomeUnset verifies homeDir never returns "" even
23+
// when the platform home env var is cleared — it falls back to os.TempDir().
24+
func TestHomeDir_FallsBackWhenHomeUnset(t *testing.T) {
25+
t.Setenv("HOME", "")
26+
t.Setenv("USERPROFILE", "")
27+
28+
got := homeDir()
29+
if got == "" {
30+
t.Fatal("homeDir() = \"\"; want a non-empty absolute path")
31+
}
32+
if !filepath.IsAbs(got) {
33+
t.Fatalf("homeDir() = %q; want an absolute path", got)
34+
}
35+
}

0 commit comments

Comments
 (0)