github.com/andrewrech/lazygit@v0.8.1/pkg/config/app_config.go (about) 1 package config 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 9 "github.com/shibukawa/configdir" 10 "github.com/spf13/viper" 11 yaml "gopkg.in/yaml.v2" 12 ) 13 14 // AppConfig contains the base configuration fields required for lazygit. 15 type AppConfig struct { 16 Debug bool `long:"debug" env:"DEBUG" default:"false"` 17 Version string `long:"version" env:"VERSION" default:"unversioned"` 18 Commit string `long:"commit" env:"COMMIT"` 19 BuildDate string `long:"build-date" env:"BUILD_DATE"` 20 Name string `long:"name" env:"NAME" default:"lazygit"` 21 BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""` 22 UserConfig *viper.Viper 23 AppState *AppState 24 IsNewRepo bool 25 } 26 27 // AppConfigurer interface allows individual app config structs to inherit Fields 28 // from AppConfig and still be used by lazygit. 29 type AppConfigurer interface { 30 GetDebug() bool 31 GetVersion() string 32 GetCommit() string 33 GetBuildDate() string 34 GetName() string 35 GetBuildSource() string 36 GetUserConfig() *viper.Viper 37 GetAppState() *AppState 38 WriteToUserConfig(string, string) error 39 SaveAppState() error 40 LoadAppState() error 41 SetIsNewRepo(bool) 42 GetIsNewRepo() bool 43 } 44 45 // NewAppConfig makes a new app config 46 func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool) (*AppConfig, error) { 47 userConfig, err := LoadConfig("config", true) 48 if err != nil { 49 return nil, err 50 } 51 52 if os.Getenv("DEBUG") == "TRUE" { 53 debuggingFlag = true 54 } 55 56 appConfig := &AppConfig{ 57 Name: "lazygit", 58 Version: version, 59 Commit: commit, 60 BuildDate: date, 61 Debug: debuggingFlag, 62 BuildSource: buildSource, 63 UserConfig: userConfig, 64 AppState: &AppState{}, 65 IsNewRepo: false, 66 } 67 68 if err := appConfig.LoadAppState(); err != nil { 69 return nil, err 70 } 71 72 return appConfig, nil 73 } 74 75 // GetIsNewRepo returns known repo boolean 76 func (c *AppConfig) GetIsNewRepo() bool { 77 return c.IsNewRepo 78 } 79 80 // SetIsNewRepo set if the current repo is known 81 func (c *AppConfig) SetIsNewRepo(toSet bool) { 82 c.IsNewRepo = toSet 83 } 84 85 // GetDebug returns debug flag 86 func (c *AppConfig) GetDebug() bool { 87 return c.Debug 88 } 89 90 // GetVersion returns debug flag 91 func (c *AppConfig) GetVersion() string { 92 return c.Version 93 } 94 95 // GetCommit returns debug flag 96 func (c *AppConfig) GetCommit() string { 97 return c.Commit 98 } 99 100 // GetBuildDate returns debug flag 101 func (c *AppConfig) GetBuildDate() string { 102 return c.BuildDate 103 } 104 105 // GetName returns debug flag 106 func (c *AppConfig) GetName() string { 107 return c.Name 108 } 109 110 // GetBuildSource returns the source of the build. For builds from goreleaser 111 // this will be binaryBuild 112 func (c *AppConfig) GetBuildSource() string { 113 return c.BuildSource 114 } 115 116 // GetUserConfig returns the user config 117 func (c *AppConfig) GetUserConfig() *viper.Viper { 118 return c.UserConfig 119 } 120 121 // GetAppState returns the app state 122 func (c *AppConfig) GetAppState() *AppState { 123 return c.AppState 124 } 125 126 func newViper(filename string) (*viper.Viper, error) { 127 v := viper.New() 128 v.SetConfigType("yaml") 129 v.SetConfigName(filename) 130 return v, nil 131 } 132 133 // LoadConfig gets the user's config 134 func LoadConfig(filename string, withDefaults bool) (*viper.Viper, error) { 135 v, err := newViper(filename) 136 if err != nil { 137 return nil, err 138 } 139 if withDefaults { 140 if err = LoadDefaults(v, GetDefaultConfig()); err != nil { 141 return nil, err 142 } 143 if err = LoadDefaults(v, GetPlatformDefaultConfig()); err != nil { 144 return nil, err 145 } 146 } 147 if err = LoadAndMergeFile(v, filename+".yml"); err != nil { 148 return nil, err 149 } 150 return v, nil 151 } 152 153 // LoadDefaults loads in the defaults defined in this file 154 func LoadDefaults(v *viper.Viper, defaults []byte) error { 155 return v.MergeConfig(bytes.NewBuffer(defaults)) 156 } 157 158 func prepareConfigFile(filename string) (string, error) { 159 // chucking my name there is not for vanity purposes, the xdg spec (and that 160 // function) requires a vendor name. May as well line up with github 161 configDirs := configdir.New("jesseduffield", "lazygit") 162 folder := configDirs.QueryFolderContainsFile(filename) 163 if folder == nil { 164 // create the file as empty 165 folders := configDirs.QueryFolders(configdir.Global) 166 if err := folders[0].WriteFile(filename, []byte{}); err != nil { 167 return "", err 168 } 169 folder = configDirs.QueryFolderContainsFile(filename) 170 } 171 return filepath.Join(folder.Path, filename), nil 172 } 173 174 // LoadAndMergeFile Loads the config/state file, creating 175 // the file has an empty one if it does not exist 176 func LoadAndMergeFile(v *viper.Viper, filename string) error { 177 configPath, err := prepareConfigFile(filename) 178 if err != nil { 179 return err 180 } 181 182 v.AddConfigPath(filepath.Dir(configPath)) 183 return v.MergeInConfig() 184 } 185 186 // WriteToUserConfig adds a key/value pair to the user's config and saves it 187 func (c *AppConfig) WriteToUserConfig(key, value string) error { 188 // reloading the user config directly (without defaults) so that we're not 189 // writing any defaults back to the user's config 190 v, err := LoadConfig("config", false) 191 if err != nil { 192 return err 193 } 194 195 v.Set(key, value) 196 return v.WriteConfig() 197 } 198 199 // SaveAppState marhsalls the AppState struct and writes it to the disk 200 func (c *AppConfig) SaveAppState() error { 201 marshalledAppState, err := yaml.Marshal(c.AppState) 202 if err != nil { 203 return err 204 } 205 206 filepath, err := prepareConfigFile("state.yml") 207 if err != nil { 208 return err 209 } 210 211 return ioutil.WriteFile(filepath, marshalledAppState, 0644) 212 } 213 214 // LoadAppState loads recorded AppState from file 215 func (c *AppConfig) LoadAppState() error { 216 filepath, err := prepareConfigFile("state.yml") 217 if err != nil { 218 return err 219 } 220 appStateBytes, err := ioutil.ReadFile(filepath) 221 if err != nil { 222 return err 223 } 224 if len(appStateBytes) == 0 { 225 return yaml.Unmarshal(getDefaultAppState(), c.AppState) 226 } 227 return yaml.Unmarshal(appStateBytes, c.AppState) 228 } 229 230 // GetDefaultConfig returns the application default configuration 231 func GetDefaultConfig() []byte { 232 return []byte( 233 `gui: 234 ## stuff relating to the UI 235 scrollHeight: 2 236 scrollPastBottom: true 237 mouseEvents: false # will default to true when the feature is complete 238 theme: 239 activeBorderColor: 240 - white 241 - bold 242 inactiveBorderColor: 243 - white 244 optionsTextColor: 245 - blue 246 commitLength: 247 show: true 248 git: 249 merging: 250 manualCommit: false 251 skipHookPrefix: 'WIP' 252 update: 253 method: prompt # can be: prompt | background | never 254 days: 14 # how often a update is checked for 255 reporting: 'undetermined' # one of: 'on' | 'off' | 'undetermined' 256 confirmOnQuit: false 257 `) 258 } 259 260 // AppState stores data between runs of the app like when the last update check 261 // was performed and which other repos have been checked out 262 type AppState struct { 263 LastUpdateCheck int64 264 RecentRepos []string 265 } 266 267 func getDefaultAppState() []byte { 268 return []byte(` 269 lastUpdateCheck: 0 270 recentRepos: [] 271 `) 272 } 273 274 // // commenting this out until we use it again 275 // func homeDirectory() string { 276 // usr, err := user.Current() 277 // if err != nil { 278 // log.Fatal(err) 279 // } 280 // return usr.HomeDir 281 // }