From 913a2fd0656c393e743686cf6fe823a963916463 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Tue, 27 Jul 2021 22:03:37 +0200 Subject: Allow having multiple config files --- docs/Config.md | 5 ++ main.go | 7 +++ pkg/config/app_config.go | 140 ++++++++++++++++++++++++------------------ pkg/gui/gui.go | 6 +- pkg/gui/recent_repos_panel.go | 6 +- pkg/gui/status_panel.go | 32 +++++++++- pkg/updates/updates.go | 6 +- 7 files changed, 137 insertions(+), 65 deletions(-) diff --git a/docs/Config.md b/docs/Config.md index 51cf02ce5..92e63db58 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -265,6 +265,11 @@ os: `{{editor}}` in `editCommandTemplate` is replaced with the value of `editCommand`. +### Change config file used + +- Use `--config-file=~/.base_lg_conf,~/.light_theme_lg_conf` or `$LG_CONFIG_FILE="~/.base_lg_conf,~/.light_theme_lg_conf"` +- Change the default config directory `$CONFIG_DIR="~/.config/lazygit"` + ### Recommended Config Values for users of VSCode diff --git a/main.go b/main.go index 67d6e8287..02088b5bd 100644 --- a/main.go +++ b/main.go @@ -61,6 +61,9 @@ func main() { gitDir := "" flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument") + customConfig := "" + flaggy.String(&customConfig, "cf", "config-file", "Comma seperated list to custom config file(s)") + flaggy.Parse() if repoPath != "" { @@ -72,6 +75,10 @@ func main() { gitDir = filepath.Join(repoPath, ".git") } + if customConfig != "" { + os.Setenv("LG_CONFIG_FILE", customConfig) + } + if useConfigDir != "" { os.Setenv("CONFIG_DIR", useConfigDir) } diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 6adfb9919..dc866e253 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -12,18 +12,20 @@ import ( // 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 *UserConfig - UserConfigDir string - UserConfigPath string - TempDir 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 + UserConfigFiles []string + DeafultConfFiles bool + UserConfigPath string + UserConfigDir string + TempDir string + AppState *AppState + IsNewRepo bool } // AppConfigurer interface allows individual app config structs to inherit Fields @@ -36,6 +38,7 @@ type AppConfigurer interface { GetName() string GetBuildSource() string GetUserConfig() *UserConfig + GetUserConfigFiles() []string GetUserConfigDir() string GetUserConfigPath() string GetTempDir() string @@ -49,11 +52,24 @@ type AppConfigurer interface { // NewAppConfig makes a new app config func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool) (*AppConfig, error) { configDir, err := findOrCreateConfigDir() - if err != nil { + if err != nil && !os.IsPermission(err) { return nil, err } - userConfig, err := loadUserConfigWithDefaults(configDir) + var userConfigFiles []string + + userConfigFilesOverwrite := os.Getenv("LG_CONFIG_FILE") + deafultConfFiles := true + if userConfigFilesOverwrite != "" { + // Load user defined config files + userConfigFiles = strings.Split(userConfigFilesOverwrite, ",") + deafultConfFiles = false + } else { + // Load default config files + userConfigFiles = []string{filepath.Join(configDir, ConfigFilename)} + } + + userConfig, err := loadUserConfigWithDefaults(userConfigFiles, deafultConfFiles) if err != nil { return nil, err } @@ -70,18 +86,20 @@ 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: configDir, - UserConfigPath: filepath.Join(configDir, "config.yml"), - TempDir: tempDir, - AppState: appState, - IsNewRepo: false, + Name: "lazygit", + Version: version, + Commit: commit, + BuildDate: date, + Debug: debuggingFlag, + BuildSource: buildSource, + UserConfig: userConfig, + UserConfigFiles: userConfigFiles, + UserConfigDir: configDir, + DeafultConfFiles: deafultConfFiles, + UserConfigPath: filepath.Join(configDir, "config.yml"), + TempDir: tempDir, + AppState: appState, + IsNewRepo: false, } return appConfig, nil @@ -107,43 +125,43 @@ func configDirForVendor(vendor string) string { func findOrCreateConfigDir() (string, error) { folder := ConfigDir() - err := os.MkdirAll(folder, 0755) - if err != nil { - return "", err - } - - return folder, nil + return folder, os.MkdirAll(folder, 0755) } -func loadUserConfigWithDefaults(configDir string) (*UserConfig, error) { - return loadUserConfig(configDir, GetDefaultConfig()) +func loadUserConfigWithDefaults(configFiles []string, deafultConfFiles bool) (*UserConfig, error) { + return loadUserConfig(configFiles, GetDefaultConfig(), deafultConfFiles) } -func loadUserConfig(configDir string, base *UserConfig) (*UserConfig, error) { - fileName := filepath.Join(configDir, "config.yml") +func loadUserConfig(configFiles []string, base *UserConfig, deafultConfFiles bool) (*UserConfig, error) { + for _, fileName := range configFiles { + content, readConfFileErr := ioutil.ReadFile(fileName) + if readConfFileErr != nil { + if !deafultConfFiles { + return nil, readConfFileErr + } + + _, err := os.Stat(fileName) + if err == nil { + return nil, readConfFileErr + } + if !os.IsNotExist(err) { + return nil, readConfFileErr + } - if _, err := os.Stat(fileName); err != nil { - if os.IsNotExist(err) { file, err := os.Create(fileName) if err != nil { - if strings.Contains(err.Error(), "read-only file system") { - return base, nil + if os.IsPermission(err) { + continue + } else { + return nil, readConfFileErr } - 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 + if err := yaml.Unmarshal(content, base); err != nil { + return nil, err + } } return base, nil @@ -195,16 +213,15 @@ 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 } +func (c *AppConfig) GetUserConfigFiles() []string { + return c.UserConfigFiles +} + func (c *AppConfig) GetUserConfigDir() string { return c.UserConfigDir } @@ -214,7 +231,7 @@ func (c *AppConfig) GetTempDir() string { } func (c *AppConfig) ReloadUserConfig() error { - userConfig, err := loadUserConfigWithDefaults(c.UserConfigDir) + userConfig, err := loadUserConfigWithDefaults(c.UserConfigFiles, c.DeafultConfFiles) if err != nil { return err } @@ -232,9 +249,11 @@ func configFilePath(filename string) (string, error) { return filepath.Join(folder, filename), nil } -// ConfigFilename returns the filename of the current config file +var ConfigFilename = "config.yml" + +// ConfigFilename returns the filename of the deafult config file func (c *AppConfig) ConfigFilename() string { - return filepath.Join(c.UserConfigDir, "config.yml") + return filepath.Join(c.UserConfigDir, ConfigFilename) } // SaveAppState marshalls the AppState struct and writes it to the disk @@ -256,6 +275,9 @@ func (c *AppConfig) SaveAppState() error { func loadAppState() (*AppState, error) { filepath, err := configFilePath("state.yml") if err != nil { + if os.IsPermission(err) { + return getDefaultAppState(), nil + } return nil, err } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 95c165a71..80131f00d 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -656,7 +656,11 @@ func (gui *Gui) showIntroPopupMessage(done chan struct{}) error { onConfirm := func() error { done <- struct{}{} gui.Config.GetAppState().StartupPopupVersion = StartupPopupVersion - return gui.Config.SaveAppState() + err := gui.Config.SaveAppState() + if err != nil && os.IsPermission(err) { + return nil + } + return err } return gui.ask(askOpts{ diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go index d1ac4ea30..f82b2838a 100644 --- a/pkg/gui/recent_repos_panel.go +++ b/pkg/gui/recent_repos_panel.go @@ -114,7 +114,11 @@ func (gui *Gui) updateRecentRepoList() error { known, recentRepos := newRecentReposList(recentRepos, currentRepo) gui.Config.SetIsNewRepo(known) gui.Config.GetAppState().RecentRepos = recentRepos - return gui.Config.SaveAppState() + err = gui.Config.SaveAppState() + if err != nil && os.IsPermission(err) { + return nil + } + return err } // newRecentReposList returns a new repo list with a new entry but only when it doesn't exist yet diff --git a/pkg/gui/status_panel.go b/pkg/gui/status_panel.go index a3f8deb10..2800a6bf2 100644 --- a/pkg/gui/status_panel.go +++ b/pkg/gui/status_panel.go @@ -1,6 +1,7 @@ package gui import ( + "errors" "fmt" "strings" @@ -114,13 +115,38 @@ func (gui *Gui) handleStatusSelect() error { }) } +func (gui *Gui) askForConfigFile(action func(file string) error) error { + confFiles := gui.Config.GetUserConfigFiles() + switch len(confFiles) { + case 0: + return errors.New("no config file found") + case 1: + return action(confFiles[0]) + default: + menuItems := make([]*menuItem, len(confFiles)) + for i, file := range confFiles { + i := i + menuItems[i] = &menuItem{ + displayString: file, + onPress: func() error { + return action(confFiles[i]) + }, + } + } + return gui.createMenu("select config file", menuItems, createMenuOptions{}) + } +} + func (gui *Gui) handleOpenConfig() error { - return gui.openFile(gui.Config.GetUserConfigPath()) + return gui.askForConfigFile(func(file string) error { + return gui.openFile(file) + }) } func (gui *Gui) handleEditConfig() error { - filename := gui.Config.GetUserConfigPath() - return gui.editFile(filename) + return gui.askForConfigFile(func(file string) error { + return gui.editFile(file) + }) } func lazygitTitle() string { diff --git a/pkg/updates/updates.go b/pkg/updates/updates.go index cb5a64788..a190fdb2c 100644 --- a/pkg/updates/updates.go +++ b/pkg/updates/updates.go @@ -76,7 +76,11 @@ func (u *Updater) getLatestVersionNumber() (string, error) { // RecordLastUpdateCheck records last time an update check was performed func (u *Updater) RecordLastUpdateCheck() error { u.Config.GetAppState().LastUpdateCheck = time.Now().Unix() - return u.Config.SaveAppState() + err := u.Config.SaveAppState() + if err != nil && os.IsPermission(err) { + return nil + } + return err } // expecting version to be of the form `v12.34.56` -- cgit v1.2.3