From 1d7ffc20133c49bcd8cba6ba68c77a637d7364fd Mon Sep 17 00:00:00 2001 From: Sam Coe Date: Tue, 1 Jun 2021 15:40:08 -0400 Subject: [PATCH] Add support for LocalAppData and .local/state/ fallback --- internal/config/config_file.go | 29 +++++++++++++++-------- internal/config/config_file_test.go | 36 ++++++++++++++++++++--------- internal/update/update.go | 11 +++++++-- 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/internal/config/config_file.go b/internal/config/config_file.go index 74fe77502..e90d40ab0 100644 --- a/internal/config/config_file.go +++ b/internal/config/config_file.go @@ -18,6 +18,7 @@ const ( XDG_CONFIG_HOME = "XDG_CONFIG_HOME" XDG_STATE_HOME = "XDG_STATE_HOME" APP_DATA = "AppData" + LOCAL_APP_DATA = "LocalAppData" ) // Config path precedence @@ -39,24 +40,34 @@ func ConfigDir() string { } // If the path does not exist try migrating config from default paths - if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { + if !dirExists(path) { _ = autoMigrateConfigDir(path) } return path } +// State path precedence +// 1. XDG_CONFIG_HOME +// 2. LocalAppData (windows only) +// 3. HOME func StateDir() string { - if path := os.Getenv(XDG_STATE_HOME); path != "" { - path = filepath.Join(path, "gh") - if !dirExists(path) { - _ = os.MkdirAll(path, 0755) - _ = autoMigrateStateDir(path) - } - return path + var path string + if a := os.Getenv(XDG_STATE_HOME); a != "" { + path = filepath.Join(a, "gh") + } else if b := os.Getenv(LOCAL_APP_DATA); runtime.GOOS == "windows" && b != "" { + path = filepath.Join(b, "GitHub CLI") + } else { + c, _ := os.UserHomeDir() + path = filepath.Join(c, ".local", "state", "gh") } - return ConfigDir() + // If the path does not exist try migrating state from default paths + if !dirExists(path) { + _ = autoMigrateStateDir(path) + } + + return path } var errSamePath = errors.New("same path") diff --git a/internal/config/config_file_test.go b/internal/config/config_file_test.go index ff4cd1b04..e6150aae3 100644 --- a/internal/config/config_file_test.go +++ b/internal/config/config_file_test.go @@ -346,9 +346,10 @@ func Test_StateDir(t *testing.T) { tempDir := t.TempDir() tests := []struct { - name string - env map[string]string - output string + name string + onlyWindows bool + env map[string]string + output string }{ { name: "HOME/USERPROFILE specified", @@ -356,11 +357,11 @@ func Test_StateDir(t *testing.T) { "XDG_STATE_HOME": "", "GH_CONFIG_DIR": "", "XDG_CONFIG_HOME": "", - "AppData": "", + "LocalAppData": "", "USERPROFILE": tempDir, "HOME": tempDir, }, - output: filepath.Join(tempDir, ".config", "gh"), + output: filepath.Join(tempDir, ".local", "state", "gh"), }, { name: "XDG_STATE_HOME specified", @@ -370,15 +371,28 @@ func Test_StateDir(t *testing.T) { output: filepath.Join(tempDir, "gh"), }, { - name: "GH_CONFIG_DIR specified", + name: "LocalAppData specified", + onlyWindows: true, env: map[string]string{ - "GH_CONFIG_DIR": filepath.Join(tempDir, "gh_config_dir"), + "LocalAppData": tempDir, }, - output: filepath.Join(tempDir, "gh_config_dir"), + output: filepath.Join(tempDir, "GitHub CLI"), + }, + { + name: "XDG_STATE_HOME and LocalAppData specified", + onlyWindows: true, + env: map[string]string{ + "XDG_STATE_HOME": tempDir, + "LocalAppData": tempDir, + }, + output: filepath.Join(tempDir, "gh"), }, } for _, tt := range tests { + if tt.onlyWindows && runtime.GOOS != "windows" { + continue + } t.Run(tt.name, func(t *testing.T) { if tt.env != nil { for k, v := range tt.env { @@ -443,7 +457,7 @@ func Test_autoMigrateStateDir_migration(t *testing.T) { homeDir := t.TempDir() migrateDir := t.TempDir() homeConfigDir := filepath.Join(homeDir, ".config", "gh") - migrateConfigDir := filepath.Join(migrateDir, ".config", "gh") + migrateStateDir := filepath.Join(migrateDir, ".local", "state", "gh") homeEnvVar := "HOME" if runtime.GOOS == "windows" { @@ -458,14 +472,14 @@ func Test_autoMigrateStateDir_migration(t *testing.T) { err = ioutil.WriteFile(filepath.Join(homeConfigDir, "state.yml"), nil, 0755) assert.NoError(t, err) - err = autoMigrateStateDir(migrateConfigDir) + err = autoMigrateStateDir(migrateStateDir) assert.NoError(t, err) files, err := ioutil.ReadDir(homeConfigDir) assert.NoError(t, err) assert.Equal(t, 0, len(files)) - files, err = ioutil.ReadDir(migrateConfigDir) + files, err = ioutil.ReadDir(migrateStateDir) assert.NoError(t, err) assert.Equal(t, 1, len(files)) assert.Equal(t, "state.yml", files[0].Name()) diff --git a/internal/update/update.go b/internal/update/update.go index 024fd2377..f525544e9 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -3,6 +3,8 @@ package update import ( "fmt" "io/ioutil" + "os" + "path/filepath" "regexp" "strconv" "strings" @@ -83,9 +85,14 @@ func setStateEntry(stateFilePath string, t time.Time, r ReleaseInfo) error { if err != nil { return err } - _ = ioutil.WriteFile(stateFilePath, content, 0600) - return nil + err = os.MkdirAll(filepath.Dir(stateFilePath), 0755) + if err != nil { + return err + } + + err = ioutil.WriteFile(stateFilePath, content, 0600) + return err } func versionGreaterThan(v, w string) bool {