From b4e5b3d4524abfd46352ebb55ebc5ac977612904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efe=20G=C3=B6kdemir?= Date: Wed, 23 Sep 2026 08:31:00 +0300 Subject: [PATCH] fix(config): use standard user directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Efe Gökdemir --- pkg/config/config.go | 52 +++++++++++++++++++++------------------ pkg/config/config_test.go | 16 ++++++++++-- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 9789a06f..7805954f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -246,20 +246,17 @@ func mapFromString(str string) (*yamlmap.Map, error) { // ConfigDir returns the path to the configuration directory. // -// Config path precedence: GH_CONFIG_DIR, XDG_CONFIG_HOME, AppData (windows only), HOME. +// Config path precedence: GH_CONFIG_DIR, then the platform's standard user +// config directory. func ConfigDir() string { - var path string if a := os.Getenv(ghConfigDir); a != "" { - path = a - } else if b := os.Getenv(xdgConfigHome); b != "" { - path = filepath.Join(b, "gh") - } else if c := os.Getenv(appData); runtime.GOOS == "windows" && c != "" { - path = filepath.Join(c, "GitHub CLI") - } else { - d, _ := os.UserHomeDir() - path = filepath.Join(d, ".config", "gh") + return a } - return path + base, err := os.UserConfigDir() + if err != nil { + return filepath.Join(".config", "gh") + } + return filepath.Join(base, configSubdir()) } // StateDir returns the path to the state directory. @@ -296,21 +293,28 @@ func DataDir() string { // CacheDir returns the path to the cache directory. // -// Cache path precedence: XDG_CACHE_HOME, LocalAppData (windows only), HOME, legacy gh-cli-cache. +// Cache path precedence: the platform's standard user cache directory, then +// the legacy temporary directory when the standard directory is unavailable. func CacheDir() string { - if a := os.Getenv(xdgCacheHome); a != "" { - return filepath.Join(a, "gh") - } else if b := os.Getenv(localAppData); runtime.GOOS == "windows" && b != "" { - return filepath.Join(b, "GitHub CLI") - } else if c, err := os.UserHomeDir(); err == nil { - return filepath.Join(c, ".cache", "gh") - } else { - // Note that this has a minor security issue because /tmp is world-writeable. - // As such, it is possible for other users on a shared system to overwrite cached data. - // The practical risk of this is low, but it's worth calling out as a risk. - // I've included this here for backwards compatibility but we should consider removing it. - return filepath.Join(os.TempDir(), "gh-cli-cache") + if base, err := os.UserCacheDir(); err == nil { + return filepath.Join(base, cacheSubdir()) + } + // Note that this has a minor security issue because /tmp is world-writeable. + // As such, it is possible for other users on a shared system to overwrite cached data. + // The practical risk of this is low, but it's worth calling out as a risk. + // I've included this here for backwards compatibility but we should consider removing it. + return filepath.Join(os.TempDir(), "gh-cli-cache") +} + +func configSubdir() string { + if runtime.GOOS == "windows" { + return "GitHub CLI" } + return "gh" +} + +func cacheSubdir() string { + return configSubdir() } func readFile(filename string) ([]byte, error) { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 01cb61ba..e4940687 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -91,7 +91,13 @@ func TestConfigDir(t *testing.T) { t.Setenv(k, v) } } - assert.Equal(t, tt.output, ConfigDir()) + expected := tt.output + if tt.name == "HOME/USERPROFILE specified" { + base, err := os.UserConfigDir() + require.NoError(t, err) + expected = filepath.Join(base, configSubdir()) + } + assert.Equal(t, expected, ConfigDir()) }) } } @@ -284,7 +290,13 @@ func TestCacheDir(t *testing.T) { t.Setenv(k, v) } } - assert.Equal(t, tt.output, CacheDir()) + expected := tt.output + if tt.name == "tries to use the home dir cache directory" { + base, err := os.UserCacheDir() + require.NoError(t, err) + expected = filepath.Join(base, cacheSubdir()) + } + assert.Equal(t, expected, CacheDir()) }) }