diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2020-10-03 14:54:55 +1000 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2020-10-10 00:23:01 +1100 |
commit | 4912205adb5e8e245b33b8e0d5581bcf0ec37e8a (patch) | |
tree | 90e28657c0af72cd0a880842f88750c01a32834d /pkg | |
parent | 9440dcf9de3214927125693e512cdc941ca4e123 (diff) |
remove viper
WIP
Diffstat (limited to 'pkg')
38 files changed, 706 insertions, 461 deletions
diff --git a/pkg/app/app.go b/pkg/app/app.go index d41f208d7..fd8950ae7 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -61,14 +61,18 @@ func getLogLevel() logrus.Level { } func newDevelopmentLogger(configurer config.AppConfigurer) *logrus.Logger { - log := logrus.New() - log.SetLevel(getLogLevel()) - file, err := os.OpenFile(config.LogPath(), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + logger := logrus.New() + logger.SetLevel(getLogLevel()) + logPath, err := config.LogPath() + if err != nil { + log.Fatal(err) + } + file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { panic("unable to log to file") // TODO: don't panic (also, remove this call to the `panic` function) } - log.SetOutput(file) - return log + logger.SetOutput(file) + return logger } func newLogger(config config.AppConfigurer) *logrus.Entry { @@ -295,11 +299,14 @@ func (app *App) KnownError(err error) (string, bool) { } func TailLogs() { - logFilePath := config.LogPath() + logFilePath, err := config.LogPath() + if err != nil { + log.Fatal(err) + } fmt.Printf("Tailing log file %s\n\n", logFilePath) - _, err := os.Stat(logFilePath) + _, err = os.Stat(logFilePath) if err != nil { if os.IsNotExist(err) { log.Fatal("Log file does not exist. Run `lazygit --debug` first to create the log file") diff --git a/pkg/commands/branches.go b/pkg/commands/branches.go index 2da076e3a..dd910af3b 100644 --- a/pkg/commands/branches.go +++ b/pkg/commands/branches.go @@ -78,7 +78,7 @@ func (c *GitCommand) GetUpstreamForBranch(branchName string) (string, error) { } func (c *GitCommand) GetBranchGraphCmdStr(branchName string) string { - branchLogCmdTemplate := c.Config.GetUserConfig().GetString("git.branchLogCmd") + branchLogCmdTemplate := c.Config.GetUserConfig().Git.BranchLogCmd templateValues := map[string]string{ "branchName": branchName, } @@ -122,7 +122,7 @@ type MergeOpts struct { // Merge merge func (c *GitCommand) Merge(branchName string, opts MergeOpts) error { - mergeArgs := c.Config.GetUserConfig().GetString("git.merging.args") + mergeArgs := c.Config.GetUserConfig().Git.Merging.Args command := fmt.Sprintf("git merge --no-edit %s %s", mergeArgs, branchName) if opts.FastForwardOnly { diff --git a/pkg/commands/config.go b/pkg/commands/config.go index 018de2724..4e747e32c 100644 --- a/pkg/commands/config.go +++ b/pkg/commands/config.go @@ -24,7 +24,7 @@ func (c *GitCommand) ConfiguredPager() string { } func (c *GitCommand) GetPager(width int) string { - useConfig := c.Config.GetUserConfig().GetBool("git.paging.useConfig") + useConfig := c.Config.GetUserConfig().Git.Paging.UseConfig if useConfig { pager := c.ConfiguredPager() return strings.Split(pager, "| less")[0] @@ -34,12 +34,12 @@ func (c *GitCommand) GetPager(width int) string { "columnWidth": strconv.Itoa(width/2 - 6), } - pagerTemplate := c.Config.GetUserConfig().GetString("git.paging.pager") + pagerTemplate := c.Config.GetUserConfig().Git.Paging.Pager return utils.ResolvePlaceholderString(pagerTemplate, templateValues) } func (c *GitCommand) colorArg() string { - return c.Config.GetUserConfig().GetString("git.paging.colorArg") + return c.Config.GetUserConfig().Git.Paging.ColorArg } func (c *GitCommand) GetConfigValue(key string) string { diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 87384930f..e04903e39 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -285,12 +285,12 @@ func TestGitCommandGetStashEntries(t *testing.T) { func(entries []*models.StashEntry) { expected := []*models.StashEntry{ { - 0, - "WIP on add-pkg-commands-test: 55c6af2 increase parallel build", + Index: 0, + Name: "WIP on add-pkg-commands-test: 55c6af2 increase parallel build", }, { - 1, - "WIP on master: bb86a3f update github template", + Index: 1, + Name: "WIP on master: bb86a3f update github template", }, } diff --git a/pkg/commands/oscommands/os.go b/pkg/commands/oscommands/os.go index 8a0e1654e..3f5076dae 100644 --- a/pkg/commands/oscommands/os.go +++ b/pkg/commands/oscommands/os.go @@ -212,7 +212,7 @@ func sanitisedCommandOutput(output []byte, err error) (string, error) { // OpenFile opens a file with the given func (c *OSCommand) OpenFile(filename string) error { - commandTemplate := c.Config.GetUserConfig().GetString("os.openCommand") + commandTemplate := c.Config.GetUserConfig().OS.OpenCommand templateValues := map[string]string{ "filename": c.Quote(filename), } @@ -224,7 +224,7 @@ func (c *OSCommand) OpenFile(filename string) error { // OpenLink opens a file with the given func (c *OSCommand) OpenLink(link string) error { - commandTemplate := c.Config.GetUserConfig().GetString("os.openLinkCommand") + commandTemplate := c.Config.GetUserConfig().OS.OpenLinkCommand templateValues := map[string]string{ "link": c.Quote(link), } diff --git a/pkg/commands/oscommands/os_test.go b/pkg/commands/oscommands/os_test.go index 3383db215..78dfa48ec 100644 --- a/pkg/commands/oscommands/os_test.go +++ b/pkg/commands/oscommands/os_test.go @@ -103,7 +103,7 @@ func TestOSCommandOpenFile(t *testing.T) { for _, s := range scenarios { OSCmd := NewDummyOSCommand() OSCmd.Command = s.command - OSCmd.Config.GetUserConfig().Set("os.openCommand", "open {{filename}}") + OSCmd.Config.GetUserConfig().OS.OpenCommand = "open {{filename}}" s.test(OSCmd.OpenFile(s.filename)) } diff --git a/pkg/commands/pull_request.go b/pkg/commands/pull_request.go index af46318f5..acceced52 100644 --- a/pkg/commands/pull_request.go +++ b/pkg/commands/pull_request.go @@ -60,7 +60,7 @@ func getServices(config config.AppConfigurer) []*Service { NewService("gitlab", "gitlab.com", "gitlab.com"), } - configServices := config.GetUserConfig().GetStringMapString("services") + configServices := config.GetUserConfig().Services for repoDomain, typeAndDomain := range configServices { splitData := strings.Split(typeAndDomain, ":") diff --git a/pkg/commands/pull_request_test.go b/pkg/commands/pull_request_test.go index 075792783..7ed02609a 100644 --- a/pkg/commands/pull_request_test.go +++ b/pkg/commands/pull_request_test.go @@ -147,14 +147,14 @@ func TestCreatePullRequest(t *testing.T) { t.Run(s.testName, func(t *testing.T) { gitCommand := NewDummyGitCommand() gitCommand.OSCommand.Command = s.command - gitCommand.OSCommand.Config.GetUserConfig().Set("os.openLinkCommand", "open {{link}}") - gitCommand.Config.GetUserConfig().Set("services", map[string]string{ + gitCommand.OSCommand.Config.GetUserConfig().OS.OpenLinkCommand = "open {{link}}" + gitCommand.OSCommand.Config.GetUserConfig().Services = map[string]string{ // valid configuration for a custom service URL "git.work.com": "gitlab:code.work.com", // invalid configurations for a custom service URL "invalid.work.com": "noservice:invalid.work.com", "noservice.work.com": "noservice.work.com", - }) + } dummyPullRequest := NewPullRequest(gitCommand) s.test(dummyPullRequest.Create(s.branch)) }) diff --git a/pkg/commands/sync.go b/pkg/commands/sync.go index 1321af2d3..96d0480b4 100644 --- a/pkg/commands/sync.go +++ b/pkg/commands/sync.go @@ -8,7 +8,7 @@ import ( // usingGpg tells us whether the user has gpg enabled so that we can know // whether we need to run a subprocess to allow them to enter their password func (c *GitCommand) usingGpg() bool { - overrideGpg := c.Config.GetUserConfig().GetBool("git.overrideGpg") + overrideGpg := c.Config.GetUserConfig().Git.OverrideGpg if overrideGpg { return false } diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 6558f9d3b..db0d56558 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -1,28 +1,27 @@ package config import ( - "bytes" "io/ioutil" "os" "path/filepath" - "github.com/shibukawa/configdir" - "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" + "github.com/OpenPeeDeeP/xdg" + yaml "github.com/jesseduffield/yaml" ) // AppConfig contains the base configuration fields required for lazygit. type AppConfig struct { - Debug bool `long:"debug" env:"DEBUG" default:"false"` - Version string `long:"version" env:"VERSION" default:"unversioned"` - Commit string `long:"commit" env:"COMMIT"` - BuildDate string `long:"build-date" env:"BUILD_DATE"` - Name string `long:"name" env:"NAME" default:"lazygit"` - BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""` - UserConfig *viper.Viper - UserConfigDir string - AppState *AppState - IsNewRepo bool + Debug bool `long:"debug" env:"DEBUG" default:"false"` + Version string `long:"version" env:"VERSION" default:"unversioned"` + Commit string `long:"commit" env:"COMMIT"` + BuildDate string `long:"build-date" env:"BUILD_DATE"` + Name string `long:"name" env:"NAME" default:"lazygit"` + BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""` + UserConfig *UserConfig + UserConfigDir string + UserConfigPath string + AppState *AppState + IsNewRepo bool } // AppConfigurer interface allows individual app config structs to inherit Fields @@ -34,10 +33,11 @@ type AppConfigurer interface { GetBuildDate() string GetName() string GetBuildSource() string - GetUserConfig() *viper.Viper + GetUserConfig() *UserConfig GetUserConfigDir() string + GetUserConfigPath() string GetAppState() *AppState - WriteToUserConfig(string, interface{}) error + WriteToUserConfig(func(*UserConfig) error) error SaveAppState() error LoadAppState() error SetIsNewRepo(bool) @@ -46,7 +46,12 @@ type AppConfigurer interface { // NewAppConfig makes a new app config func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool) (*AppConfig, error) { - userConfig, userConfigPath, err := LoadConfig("config", true) + configDir, err := findOrCreateConfigDir(name) + if err != nil { + return nil, err + } + + userConfig, err := loadUserConfigWithDefaults(configDir) if err != nil { return nil, err } @@ -56,16 +61,17 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg } appConfig := &AppConfig{ - Name: "lazygit", - Version: version, - Commit: commit, - BuildDate: date, - Debug: debuggingFlag, - BuildSource: buildSource, - UserConfig: userConfig, - UserConfigDir: filepath.Dir(userConfigPath), - AppState: &AppState{}, - IsNewRepo: false, + Name: "lazygit", + Version: version, + Commit: commit, + BuildDate: date, + Debug: debuggingFlag, + BuildSource: buildSource, + UserConfig: userConfig, + UserConfigDir: configDir, + UserConfigPath: filepath.Join(configDir, "config.yml"), + AppState: &AppState{}, + IsNewRepo: false, } if err := appConfig.LoadAppState(); err != nil { @@ -75,6 +81,51 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg return appConfig, nil } +func findOrCreateConfigDir(projectName string) (string, error) { + // chucking my name there is not for vanity purposes, the xdg spec (and that + // function) requires a vendor name. May as well line up with github + configDirs := xdg.New("jesseduffield", projectName) + folder := configDirs.ConfigHome() + + err := os.MkdirAll(folder, 0755) + if err != nil { + return "", err + } + + return folder, nil +} + +func loadUserConfigWithDefaults(configDir string) (*UserConfig, error) { + return loadUserConfig(configDir, GetDefaultConfig()) +} + +func loadUserConfig(configDir string, base *UserConfig) (*UserConfig, error) { + fileName := filepath.Join(configDir, "config.yml") + + if _, err := os.Stat(fileName); err != nil { + if os.IsNotExist(err) { + file, err := os.Create(fileName) + if err != nil { + return nil, err + } + file.Close() + } else { + return nil, err + } + } + + content, err := ioutil.ReadFile(fileName) + if err != nil { + return nil, err + } + + if err := yaml.Unmarshal(content, base); err != nil { + return nil, err + } + + return base, nil +} + // GetIsNewRepo returns known repo boolean func (c *AppConfig) GetIsNewRepo() bool { return c.IsNewRepo @@ -117,10 +168,15 @@ func (c *AppConfig) GetBuildSource() string { } // GetUserConfig returns the user config -func (c *AppConfig) GetUserConfig() *viper.Viper { +func (c *AppConfig) GetUserConfig() *UserConfig { return c.UserConfig } +// GetUserConfig returns the user config +func (c *AppConfig) GetUserConfigPath() string { + return c.UserConfigPath +} + // GetAppState returns the app state func (c *AppConfig) GetAppState() *AppState { return c.AppState @@ -130,78 +186,40 @@ func (c *AppConfig) GetUserConfigDir() string { return c.UserConfigDir } -func newViper(filename string) (*viper.Viper, error) { - v := viper.New() - v.SetConfigType("yaml") - v.SetConfigName(filename) - return v, nil -} - -// LoadConfig gets the user's config -func LoadConfig(filename string, withDefaults bool) (*viper.Viper, string, error) { - v, err := newViper(filename) - if err != nil { - return nil, "", err - } - if withDefaults { - if err = LoadDefaults(v, GetDefaultConfig()); err != nil { - return nil, "", err - } - if err = LoadDefaults(v, GetPlatformDefaultConfig()); err != nil { - return nil, "", err - } - } - configPath, err := LoadAndMergeFile(v, filename+".yml") +func configFilePath(filename string) (string, error) { + folder, err := findOrCreateConfigDir("lazygit") if err != nil { - return nil, "", err + return "", err } - return v, configPath, nil -} - -// LoadDefaults loads in the defaults defined in this file -func LoadDefaults(v *viper.Viper, defaults []byte) error { - return v.MergeConfig(bytes.NewBuffer(defaults)) -} -func prepareConfigFile(filename string) (string, error) { - // chucking my name there is not for vanity purposes, the xdg spec (and that - // function) requires a vendor name. May as well line up with github - configDirs := configdir.New("jesseduffield", "lazygit") - folder := configDirs.QueryFolderContainsFile(filename) - if folder == nil { - // create the file as empty - folders := configDirs.QueryFolders(configdir.Global) - if err := folders[0].WriteFile(filename, []byte{}); err != nil { - return "", err - } - folder = configDirs.QueryFolderContainsFile(filename) - } - return filepath.Join(folder.Path, filename), nil + return filepath.Join(folder, filename), nil } -// LoadAndMergeFile Loads the config/state file, creating -// the file has an empty one if it does not exist -func LoadAndMergeFile(v *viper.Viper, filename string) (string, error) { - configPath, err := prepareConfigFile(filename) +// WriteToUserConfig allows you to set a value on the user config to be saved +// note that if you set a zero-value, it may be ignored e.g. a false or 0 or +// empty string this is because we are using the omitempty yaml directive so +// that we don't write a heap of zero values to the user's config.yml +func (c *AppConfig) WriteToUserConfig(updateConfig func(*UserConfig) error) error { + userConfig, err := loadUserConfig(c.UserConfigDir, &UserConfig{}) if err != nil { - return "", err + return err } - v.AddConfigPath(filepath.Dir(configPath)) - return configPath, v.MergeInConfig() -} + if err := updateConfig(userConfig); err != nil { + return err + } -// WriteToUserConfig adds a key/value pair to the user's config and saves it -func (c *AppConfig) WriteToUserConfig(key string, value interface{}) error { - // reloading the user config directly (without defaults) so that we're not - // writing any defaults back to the user's config - v, _, err := LoadConfig("config", false) + file, err := os.OpenFile(c.ConfigFilename(), os.O_WRONLY|os.O_CREATE, 0666) if err != nil { return err } - v.Set(key, value) - return v.WriteConfig() + return yaml.NewEncoder(file).Encode(userConfig) +} + +// ConfigFilename returns the filename of the current config file +func (c *AppConfig) ConfigFilename() string { + return filepath.Join(c.UserConfigDir, "config.yml") } // SaveAppState marshalls the AppState struct and writes it to the disk @@ -211,7 +229,7 @@ func (c *AppConfig) SaveAppState() error { return err } - filepath, err := prepareConfigFile("state.yml") + filepath, err := configFilePath("state.yml") if err != nil { return err } @@ -221,7 +239,7 @@ func (c *AppConfig) SaveAppState() error { // LoadAppState loads recorded AppState from file func (c *AppConfig) LoadAppState() error { - filepath, err := prepareConfigFile("state.yml") + filepath, err := configFilePath("state.yml") if err != nil { return err } @@ -235,8 +253,20 @@ func (c *AppConfig) LoadAppState() error { return yaml.Unmarshal(appStateBytes, c.AppState) } -// GetDefaultConfig returns the application default configuration -func GetDefaultConfig() []byte { +func GetDefaultConfig() *UserConfig { + userConfig := &UserConfig{} + + if err := yaml.Unmarshal(GetDefaultConfigBytes(), userConfig); err != nil { + panic(err) + } + + userConfig.OS = GetPlatformDefaultConfig() + + return userConfig +} + +// GetDefaultConfigBytes returns the application default configuration +func GetDefaultConfigBytes() []byte { return []byte( `gui: ## stuff relating to the UI @@ -414,12 +444,6 @@ func getDefaultAppState() []byte { `) } -func globalConfigDir() string { - configDirs := configdir.New("jesseduffield", "lazygit") - configDir := configDirs.QueryFolders(configdir.Global)[0] - return configDir.Path -} - -func LogPath() string { - return filepath.Join(globalConfigDir(), "development.log") +func LogPath() (string, error) { + return configFilePath("development.log") } diff --git a/pkg/config/config_default_platform.go b/pkg/config/config_default_platform.go index df205c0d7..e9e2f63ba 100644 --- a/pkg/config/config_default_platform.go +++ b/pkg/config/config_default_platform.go @@ -3,9 +3,9 @@ package config // GetPlatformDefaultConfig gets the defaults for the platform -func GetPlatformDefaultConfig() []byte { - return []byte( - `os: - openCommand: 'open {{filename}}' - openLinkCommand: 'open {{link}}'`) +func GetPlatformDefaultConfig() OSConfig { + return OSConfig{ + OpenCommand: "open {{filename}}", + OpenLinkCommand: "open {{link}}", + } } diff --git a/pkg/config/config_linux.go b/pkg/config/config_linux.go index 2dfbdb1c6..b3d7f42b8 100644 --- a/pkg/config/config_linux.go +++ b/pkg/config/config_linux.go @@ -1,9 +1,9 @@ package config // GetPlatformDefaultConfig gets the defaults for the platform -func GetPlatformDefaultConfig() []byte { - return []byte( - `os: - openCommand: 'sh -c "xdg-open {{filename}} >/dev/null"' - openLinkCommand: 'sh -c "xdg-open {{link}} >/dev/null"'`) +func GetPlatformDefaultConfig() OSConfig { + return OSConfig{ + OpenCommand: `sh -c "xdg-open {{filename}} >/dev/null"`, + OpenLinkCommand: `sh -c "xdg-open {{link}} >/dev/null"`, + } } diff --git a/pkg/config/config_windows.go b/pkg/config/config_windows.go index 6f6560316..cea722424 100644 --- a/pkg/config/config_windows.go +++ b/pkg/config/config_windows.go @@ -1,9 +1,9 @@ package config // GetPlatformDefaultConfig gets the defaults for the platform -func GetPlatformDefaultConfig() []byte { - return []byte( - `os: - openCommand: 'cmd /c "start "" {{filename}}"' - openLinkCommand: 'cmd /c "start "" {{link}}"'`) +func GetPlatformDefaultConfig() OSConfig { + return OSConfig{ + OpenCommand: `cmd /c "start "" {{filename}}"`, + OpenLinkCommand: `cmd /c "start "" {{link}}"`, + } } diff --git a/pkg/config/dummies.go b/pkg/config/dummies.go index 034d9b88b..40548d732 100644 --- a/pkg/config/dummies.go +++ b/pkg/config/dummies.go @@ -1,17 +1,11 @@ package config import ( - "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" + yaml "github.com/jesseduffield/yaml" ) // NewDummyAppConfig creates a new dummy AppConfig for testing func NewDummyAppConfig() *AppConfig { - userConfig := viper.New() - userConfig.SetConfigType("yaml") - if err := LoadDefaults(userConfig, GetDefaultConfig()); err != nil { - panic(err) - } appConfig := &AppConfig{ Name: "lazygit", Version: "unversioned", @@ -19,7 +13,7 @@ func NewDummyAppConfig() *AppConfig { BuildDate: "", Debug: false, BuildSource: "", - UserConfig: userConfig, + UserConfig: GetDefaultConfig(), } _ = yaml.Unmarshal([]byte{}, appConfig.AppState) return appConfig diff --git a/pkg/config/os_config.go b/pkg/config/os_config.go new file mode 100644 index 000000000..b24e0ced3 --- /dev/null +++ b/pkg/config/os_config.go @@ -0,0 +1,10 @@ +package config + +// OSConfig contains config on the level of the os +type OSConfig struct { + // OpenCommand is the comm |