Skip to content

Commit

Permalink
config: add config helper methods (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
craftamap committed Sep 8, 2021
1 parent e83e8e0 commit e5ea1a5
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 105 deletions.
28 changes: 9 additions & 19 deletions cmd/commands/auth/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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{
Expand All @@ -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
Expand Down
66 changes: 8 additions & 58 deletions cmd/commands/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package config

import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
Expand All @@ -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 (
Expand All @@ -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 {
Expand Down Expand Up @@ -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))
Expand All @@ -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))
}
Expand All @@ -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
}
12 changes: 1 addition & 11 deletions cmd/commands/repo/clone/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
135 changes: 118 additions & 17 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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(),
Expand All @@ -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
}

0 comments on commit e5ea1a5

Please sign in to comment.