From 76f9f7243d7a3f93223cce6990b0dea0279f52c7 Mon Sep 17 00:00:00 2001 From: Fabian Siegel Date: Thu, 9 Sep 2021 01:37:32 +0200 Subject: [PATCH] config: add config helper methods (#18) --- cmd/commands/auth/login/login.go | 28 +++---- cmd/commands/config/config.go | 66 ++------------- cmd/commands/repo/clone/clone.go | 12 +-- config/config.go | 135 +++++++++++++++++++++++++++---- 4 files changed, 136 insertions(+), 105 deletions(-) diff --git a/cmd/commands/auth/login/login.go b/cmd/commands/auth/login/login.go index 460f314..204bd78 100644 --- a/cmd/commands/auth/login/login.go +++ b/cmd/commands/auth/login/login.go @@ -11,20 +11,19 @@ import ( "github.com/craftamap/bb/cmd/options" "github.com/logrusorgru/aurora" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -func Add(authCmd *cobra.Command, globalOpts *options.GlobalOptions) { +func Add(authCmd *cobra.Command, _ *options.GlobalOptions) { loginCmd := &cobra.Command{ Use: "login", Run: func(_ *cobra.Command, _ []string) { configDirectory, filename := config.GetGlobalConfigurationPath() path := filepath.Join(configDirectory, filename) - // TODO: extract tmpVp stuff to a seperate file - tmpVp := viper.New() - tmpVp.SetConfigType("toml") - tmpVp.SetConfigFile(path) - tmpVp.ReadInConfig() + tmpVp, err := config.GetViperForPath(path) + if err != nil { + logging.Error(err) + return + } oldPw := tmpVp.GetString(config.CONFIG_KEY_AUTH_PASSWORD) @@ -51,7 +50,7 @@ func Add(authCmd *cobra.Command, globalOpts *options.GlobalOptions) { Password string }{} - err := survey.Ask([]*survey.Question{ + err = survey.Ask([]*survey.Question{ { Name: "username", Prompt: &survey.Input{ @@ -70,21 +69,12 @@ func Add(authCmd *cobra.Command, globalOpts *options.GlobalOptions) { logging.Error(err) return } - username, err := config.BbConfigurationValidation.ValidateEntry(config.CONFIG_KEY_AUTH_USERNAME, answers.Username) - if err != nil { - logging.Error(err) - return - } - password, err := config.BbConfigurationValidation.ValidateEntry(config.CONFIG_KEY_AUTH_PASSWORD, answers.Password) + _, err = config.ValidateAndUpdateEntryWithViper(tmpVp, config.CONFIG_KEY_AUTH_USERNAME, answers.Username) if err != nil { logging.Error(err) return } - - tmpVp.Set(config.CONFIG_KEY_AUTH_USERNAME, username) - tmpVp.Set(config.CONFIG_KEY_AUTH_PASSWORD, password) - - err = tmpVp.WriteConfig() + _, err = config.ValidateAndUpdateEntryWithViper(tmpVp, config.CONFIG_KEY_AUTH_PASSWORD, answers.Password) if err != nil { logging.Error(err) return diff --git a/cmd/commands/config/config.go b/cmd/commands/config/config.go index 3e67782..c80c1d3 100644 --- a/cmd/commands/config/config.go +++ b/cmd/commands/config/config.go @@ -2,8 +2,6 @@ package config import ( "fmt" - "io" - "io/ioutil" "os" "path/filepath" "strings" @@ -12,7 +10,6 @@ import ( "github.com/craftamap/bb/config" "github.com/craftamap/bb/util/logging" "github.com/spf13/cobra" - "github.com/spf13/viper" ) var ( @@ -38,7 +35,7 @@ func Add(rootCmd *cobra.Command, _ *options.GlobalOptions) { } else { key := args[0] inputValue := args[1] - + newValue, err := config.BbConfigurationValidation.ValidateEntry(key, inputValue) if err != nil { @@ -79,17 +76,18 @@ func Add(rootCmd *cobra.Command, _ *options.GlobalOptions) { logging.Debugf("Config file path: %s", path) - tmpVp := viper.New() - tmpVp.SetConfigType("toml") - tmpVp.SetConfigFile(path) - tmpVp.ReadInConfig() + tmpVp, err := config.GetViperForPath(path) + if err != nil { + logging.Error(err) + return + } isSetAlready := tmpVp.IsSet(key) oldValue := tmpVp.Get(key) if isSetAlready { // Don't print old password values - if strings.ToLower(key) == "password" { + if strings.ToLower(key) == config.CONFIG_KEY_AUTH_PASSWORD { oldValue = "(truncated)" } logging.Warning(fmt.Sprintf("\"%s\" is already set. This will overwrite the value of \"%s\" from \"%s\" to \"%s\".", key, key, oldValue, newValue)) @@ -103,30 +101,7 @@ func Add(rootCmd *cobra.Command, _ *options.GlobalOptions) { tmpVp.Set(key, newValue) logging.Debugf("%+v", tmpVp.AllSettings()) - // WORKAROUND: currently, WriteConfig does not support writing to `.bb`-files despite setting SetConfigType. - // Therefore, we create a temporary file, write there, and try to copy the file over. - tmpFh, err := ioutil.TempFile(os.TempDir(), "bb-tmpconfig.*.toml") - if err != nil { - logging.Error("Failed to create temporary configuration file") - return - } - tmpFilename := tmpFh.Name() - logging.Debugf("tmpFilename: %s", tmpFilename) - err = tmpFh.Close() - if err != nil { - logging.Error("Failed to create temporary configuration file") - return - } - err = tmpVp.WriteConfigAs(tmpFilename) - if err != nil { - logging.Error(fmt.Sprintf("Failed to write temporary config %s: %s", path, err)) - return - } - err = copyFileContent(tmpFilename, path) - if err != nil { - logging.Error(fmt.Sprintf("Failed to write config %s -> %s: %s", tmpFilename, path, err)) - return - } + config.WriteViper(tmpVp, path) logging.SuccessExclamation(fmt.Sprintf("Successfully updated configuration %s", path)) } @@ -138,28 +113,3 @@ func Add(rootCmd *cobra.Command, _ *options.GlobalOptions) { rootCmd.AddCommand(&configCommand) } - -func copyFileContent(src string, dst string) error { - sourceFileStat, err := os.Stat(src) - if err != nil { - return err - } - - if !sourceFileStat.Mode().IsRegular() { - return fmt.Errorf("%s is not a regular file", src) - } - - source, err := os.Open(src) - if err != nil { - return err - } - defer source.Close() - - destination, err := os.Create(dst) // Create or trunicate - if err != nil { - return err - } - defer destination.Close() - _, err = io.Copy(destination, source) - return err -} diff --git a/cmd/commands/repo/clone/clone.go b/cmd/commands/repo/clone/clone.go index 769cc4c..bf989ad 100644 --- a/cmd/commands/repo/clone/clone.go +++ b/cmd/commands/repo/clone/clone.go @@ -39,21 +39,11 @@ func Add(repoCmd *cobra.Command, globalOpts *options.GlobalOptions) { configDirectory, filename := config.GetGlobalConfigurationPath() path := filepath.Join(configDirectory, filename) - // TODO: extract tmpVp stuff to a seperate file - tmpVp := viper.New() - tmpVp.SetConfigType("toml") - tmpVp.SetConfigFile(path) - tmpVp.ReadInConfig() - - gitProtocolI, err := config.BbConfigurationValidation.ValidateEntry(config.CONFIG_KEY_REPO_CLONE_GIT_PROTOCOL, gitProtocol) + _, err = config.ValidateAndUpdateEntry(path, config.CONFIG_KEY_REPO_CLONE_GIT_PROTOCOL, gitProtocol) if err != nil { logging.Error(err) return } - gitProtocol = gitProtocolI.(string) - - tmpVp.Set(config.CONFIG_KEY_REPO_CLONE_GIT_PROTOCOL, gitProtocol) - tmpVp.WriteConfig() } if len(args) == 0 { diff --git a/config/config.go b/config/config.go index d668517..9c2d190 100644 --- a/config/config.go +++ b/config/config.go @@ -2,7 +2,21 @@ package config import ( "fmt" + "io" + "io/ioutil" + "os" "strings" + + "github.com/craftamap/bb/util/logging" + "github.com/spf13/viper" +) + +const ( + CONFIG_KEY_AUTH_USERNAME = "auth.username" + CONFIG_KEY_AUTH_PASSWORD = "auth.password" + CONFIG_KEY_GIT_REMOTE = "git.remote" + CONFIG_KEY_REPO_CLONE_GIT_PROTOCOL = "repo.clone.git_protocol" + CONFIG_KEY_PR_SYNC_SYNC_METHOD = "pr.sync.sync_method" ) type Validator func(interface{}) (interface{}, error) @@ -52,23 +66,6 @@ type Entry struct { type Configuration map[string]Entry -func (c Configuration) ValidateEntry(key string, value interface{}) (interface{}, error) { - e, ok := c[key] - if !ok { - return "", fmt.Errorf("key \"%s\" is not a valid key", key) - } - return e.Validator(value) -} - -const ( - CONFIG_KEY_AUTH_USERNAME = "auth.username" - CONFIG_KEY_AUTH_PASSWORD = "auth.password" - CONFIG_KEY_GIT_REMOTE = "git.remote" - CONFIG_KEY_REPO_CLONE_GIT_PROTOCOL = "repo.clone.git_protocol" - CONFIG_KEY_PR_SYNC_SYNC_METHOD = "pr.sync.sync_method" -) - - var BbConfigurationValidation Configuration = map[string]Entry{ CONFIG_KEY_AUTH_USERNAME: { Validator: SimpleStringValidator(), @@ -86,3 +83,107 @@ var BbConfigurationValidation Configuration = map[string]Entry{ Validator: EnumValidator("merge", "rebase"), }, } + +func (c Configuration) ValidateEntry(key string, value interface{}) (interface{}, error) { + e, ok := c[key] + if !ok { + return "", fmt.Errorf("key \"%s\" is not a valid key", key) + } + return e.Validator(value) +} + +func ValidateEntry(key string, value interface{}) (interface{}, error) { + return BbConfigurationValidation.ValidateEntry(key, value) +} + +func ValidateAndUpdateEntry(filepath string, key string, value interface{}) (interface{}, error) { + sanitizedValue, err := ValidateEntry(key, value) + if err != nil { + return "", err + } + + // TODO: Add a filename-to-tmpVp cache - this way, we can prevent creating a new viper every time we want to set a value + vp, err := GetViperForPath(filepath) + if err != nil { + return sanitizedValue, err + } + + vp.Set(key, sanitizedValue) + err = WriteViper(vp, filepath) + + return sanitizedValue, err +} + +func ValidateAndUpdateEntryWithViper(vp viper.Viper, key string, value interface{}) (interface{}, error) { + sanitizedValue, err := ValidateEntry(key, value) + if err != nil { + return "", err + } + + vp.Set(key, sanitizedValue) + err = WriteViper(vp, vp.ConfigFileUsed()) + + return sanitizedValue, err +} + +func WriteViper(vp viper.Viper, path string) error { + // WORKAROUND: currently, WriteConfig does not support writing to `.bb`-files despite setting SetConfigType. + // Therefore, we create a temporary file, write there, and try to copy the file over. + tmpFh, err := ioutil.TempFile(os.TempDir(), "bb-tmpconfig.*.toml") + if err != nil { + logging.Error("Failed to create temporary configuration file") + return err + } + tmpFilename := tmpFh.Name() + logging.Debugf("tmpFilename: %s", tmpFilename) + err = tmpFh.Close() + if err != nil { + logging.Error("Failed to create temporary configuration file") + return err + } + err = vp.WriteConfigAs(tmpFilename) + if err != nil { + logging.Error(fmt.Sprintf("Failed to write temporary config %s: %s", path, err)) + return err + } + err = copyFileContent(tmpFilename, path) + if err != nil { + logging.Error(fmt.Sprintf("Failed to write config %s -> %s: %s", tmpFilename, path, err)) + return err + } + return nil +} + +func copyFileContent(src string, dst string) error { + sourceFileStat, err := os.Stat(src) + if err != nil { + return err + } + + if !sourceFileStat.Mode().IsRegular() { + return fmt.Errorf("%s is not a regular file", src) + } + + source, err := os.Open(src) + if err != nil { + return err + } + defer source.Close() + + destination, err := os.Create(dst) // Create or trunicate + if err != nil { + return err + } + defer destination.Close() + _, err = io.Copy(destination, source) + return err +} + +func GetViperForPath(path string) (viper.Viper, error) { + tmpVp := viper.New() + tmpVp.SetConfigType("toml") + tmpVp.SetConfigFile(path) + err := tmpVp.ReadInConfig() + + return *tmpVp, err +}