Skip to content

Commit e65d956

Browse files
authored
Merge pull request cli#3717 from cli/xdg-state-2
Add support for XDG_STATE_HOME
2 parents 89eff6c + 1d7ffc2 commit e65d956

File tree

5 files changed

+286
-61
lines changed

5 files changed

+286
-61
lines changed

cmd/gh/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ func checkForUpdate(currentVersion string) (*update.ReleaseInfo, error) {
264264
}
265265

266266
repo := updaterEnabled
267-
stateFilePath := filepath.Join(config.ConfigDir(), "state.yml")
267+
stateFilePath := filepath.Join(config.StateDir(), "state.yml")
268268
return update.CheckForUpdate(client, stateFilePath, repo, currentVersion)
269269
}
270270

internal/config/config_file.go

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import (
1616
const (
1717
GH_CONFIG_DIR = "GH_CONFIG_DIR"
1818
XDG_CONFIG_HOME = "XDG_CONFIG_HOME"
19+
XDG_STATE_HOME = "XDG_STATE_HOME"
1920
APP_DATA = "AppData"
21+
LOCAL_APP_DATA = "LocalAppData"
2022
)
2123

2224
// Config path precedence
@@ -38,41 +40,110 @@ func ConfigDir() string {
3840
}
3941

4042
// If the path does not exist try migrating config from default paths
41-
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
42-
autoMigrateConfigDir(path)
43+
if !dirExists(path) {
44+
_ = autoMigrateConfigDir(path)
4345
}
4446

4547
return path
4648
}
4749

50+
// State path precedence
51+
// 1. XDG_CONFIG_HOME
52+
// 2. LocalAppData (windows only)
53+
// 3. HOME
54+
func StateDir() string {
55+
var path string
56+
if a := os.Getenv(XDG_STATE_HOME); a != "" {
57+
path = filepath.Join(a, "gh")
58+
} else if b := os.Getenv(LOCAL_APP_DATA); runtime.GOOS == "windows" && b != "" {
59+
path = filepath.Join(b, "GitHub CLI")
60+
} else {
61+
c, _ := os.UserHomeDir()
62+
path = filepath.Join(c, ".local", "state", "gh")
63+
}
64+
65+
// If the path does not exist try migrating state from default paths
66+
if !dirExists(path) {
67+
_ = autoMigrateStateDir(path)
68+
}
69+
70+
return path
71+
}
72+
73+
var errSamePath = errors.New("same path")
74+
var errNotExist = errors.New("not exist")
75+
4876
// Check default paths (os.UserHomeDir, and homedir.Dir) for existing configs
4977
// If configs exist then move them to newPath
5078
// TODO: Remove support for homedir.Dir location in v2
51-
func autoMigrateConfigDir(newPath string) {
79+
func autoMigrateConfigDir(newPath string) error {
5280
path, err := os.UserHomeDir()
5381
if oldPath := filepath.Join(path, ".config", "gh"); err == nil && dirExists(oldPath) {
54-
migrateConfigDir(oldPath, newPath)
55-
return
82+
return migrateDir(oldPath, newPath)
5683
}
5784

5885
path, err = homedir.Dir()
5986
if oldPath := filepath.Join(path, ".config", "gh"); err == nil && dirExists(oldPath) {
60-
migrateConfigDir(oldPath, newPath)
87+
return migrateDir(oldPath, newPath)
6188
}
89+
90+
return errNotExist
6291
}
6392

64-
func dirExists(path string) bool {
65-
f, err := os.Stat(path)
66-
return err == nil && f.IsDir()
93+
// Check default paths (os.UserHomeDir, and homedir.Dir) for existing state file (state.yml)
94+
// If state file exist then move it to newPath
95+
// TODO: Remove support for homedir.Dir location in v2
96+
func autoMigrateStateDir(newPath string) error {
97+
path, err := os.UserHomeDir()
98+
if oldPath := filepath.Join(path, ".config", "gh"); err == nil && dirExists(oldPath) {
99+
return migrateFile(oldPath, newPath, "state.yml")
100+
}
101+
102+
path, err = homedir.Dir()
103+
if oldPath := filepath.Join(path, ".config", "gh"); err == nil && dirExists(oldPath) {
104+
return migrateFile(oldPath, newPath, "state.yml")
105+
}
106+
107+
return errNotExist
108+
}
109+
110+
func migrateFile(oldPath, newPath, file string) error {
111+
if oldPath == newPath {
112+
return errSamePath
113+
}
114+
115+
oldFile := filepath.Join(oldPath, file)
116+
newFile := filepath.Join(newPath, file)
117+
118+
if !fileExists(oldFile) {
119+
return errNotExist
120+
}
121+
122+
_ = os.MkdirAll(filepath.Dir(newFile), 0755)
123+
return os.Rename(oldFile, newFile)
67124
}
68125

69-
var migrateConfigDir = func(oldPath, newPath string) {
126+
func migrateDir(oldPath, newPath string) error {
70127
if oldPath == newPath {
71-
return
128+
return errSamePath
129+
}
130+
131+
if !dirExists(oldPath) {
132+
return errNotExist
72133
}
73134

74135
_ = os.MkdirAll(filepath.Dir(newPath), 0755)
75-
_ = os.Rename(oldPath, newPath)
136+
return os.Rename(oldPath, newPath)
137+
}
138+
139+
func dirExists(path string) bool {
140+
f, err := os.Stat(path)
141+
return err == nil && f.IsDir()
142+
}
143+
144+
func fileExists(path string) bool {
145+
f, err := os.Stat(path)
146+
return err == nil && !f.IsDir()
76147
}
77148

78149
func ConfigFile() string {

0 commit comments

Comments
 (0)