Skip to content

Commit e5ea1a5

Browse files
committed
config: add config helper methods (#18)
1 parent e83e8e0 commit e5ea1a5

File tree

4 files changed

+136
-105
lines changed

4 files changed

+136
-105
lines changed

cmd/commands/auth/login/login.go

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,19 @@ import (
1111
"github.com/craftamap/bb/cmd/options"
1212
"github.com/logrusorgru/aurora"
1313
"github.com/spf13/cobra"
14-
"github.com/spf13/viper"
1514
)
1615

17-
func Add(authCmd *cobra.Command, globalOpts *options.GlobalOptions) {
16+
func Add(authCmd *cobra.Command, _ *options.GlobalOptions) {
1817
loginCmd := &cobra.Command{
1918
Use: "login",
2019
Run: func(_ *cobra.Command, _ []string) {
2120
configDirectory, filename := config.GetGlobalConfigurationPath()
2221
path := filepath.Join(configDirectory, filename)
23-
// TODO: extract tmpVp stuff to a seperate file
24-
tmpVp := viper.New()
25-
tmpVp.SetConfigType("toml")
26-
tmpVp.SetConfigFile(path)
27-
tmpVp.ReadInConfig()
22+
tmpVp, err := config.GetViperForPath(path)
23+
if err != nil {
24+
logging.Error(err)
25+
return
26+
}
2827

2928
oldPw := tmpVp.GetString(config.CONFIG_KEY_AUTH_PASSWORD)
3029

@@ -51,7 +50,7 @@ func Add(authCmd *cobra.Command, globalOpts *options.GlobalOptions) {
5150
Password string
5251
}{}
5352

54-
err := survey.Ask([]*survey.Question{
53+
err = survey.Ask([]*survey.Question{
5554
{
5655
Name: "username",
5756
Prompt: &survey.Input{
@@ -70,21 +69,12 @@ func Add(authCmd *cobra.Command, globalOpts *options.GlobalOptions) {
7069
logging.Error(err)
7170
return
7271
}
73-
username, err := config.BbConfigurationValidation.ValidateEntry(config.CONFIG_KEY_AUTH_USERNAME, answers.Username)
74-
if err != nil {
75-
logging.Error(err)
76-
return
77-
}
78-
password, err := config.BbConfigurationValidation.ValidateEntry(config.CONFIG_KEY_AUTH_PASSWORD, answers.Password)
72+
_, err = config.ValidateAndUpdateEntryWithViper(tmpVp, config.CONFIG_KEY_AUTH_USERNAME, answers.Username)
7973
if err != nil {
8074
logging.Error(err)
8175
return
8276
}
83-
84-
tmpVp.Set(config.CONFIG_KEY_AUTH_USERNAME, username)
85-
tmpVp.Set(config.CONFIG_KEY_AUTH_PASSWORD, password)
86-
87-
err = tmpVp.WriteConfig()
77+
_, err = config.ValidateAndUpdateEntryWithViper(tmpVp, config.CONFIG_KEY_AUTH_PASSWORD, answers.Password)
8878
if err != nil {
8979
logging.Error(err)
9080
return

cmd/commands/config/config.go

Lines changed: 8 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package config
22

33
import (
44
"fmt"
5-
"io"
6-
"io/ioutil"
75
"os"
86
"path/filepath"
97
"strings"
@@ -12,7 +10,6 @@ import (
1210
"github.com/craftamap/bb/config"
1311
"github.com/craftamap/bb/util/logging"
1412
"github.com/spf13/cobra"
15-
"github.com/spf13/viper"
1613
)
1714

1815
var (
@@ -38,7 +35,7 @@ func Add(rootCmd *cobra.Command, _ *options.GlobalOptions) {
3835
} else {
3936
key := args[0]
4037
inputValue := args[1]
41-
38+
4239
newValue, err := config.BbConfigurationValidation.ValidateEntry(key, inputValue)
4340

4441
if err != nil {
@@ -79,17 +76,18 @@ func Add(rootCmd *cobra.Command, _ *options.GlobalOptions) {
7976

8077
logging.Debugf("Config file path: %s", path)
8178

82-
tmpVp := viper.New()
83-
tmpVp.SetConfigType("toml")
84-
tmpVp.SetConfigFile(path)
85-
tmpVp.ReadInConfig()
79+
tmpVp, err := config.GetViperForPath(path)
80+
if err != nil {
81+
logging.Error(err)
82+
return
83+
}
8684

8785
isSetAlready := tmpVp.IsSet(key)
8886
oldValue := tmpVp.Get(key)
8987

9088
if isSetAlready {
9189
// Don't print old password values
92-
if strings.ToLower(key) == "password" {
90+
if strings.ToLower(key) == config.CONFIG_KEY_AUTH_PASSWORD {
9391
oldValue = "(truncated)"
9492
}
9593
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) {
103101
tmpVp.Set(key, newValue)
104102
logging.Debugf("%+v", tmpVp.AllSettings())
105103

106-
// WORKAROUND: currently, WriteConfig does not support writing to `.bb`-files despite setting SetConfigType.
107-
// Therefore, we create a temporary file, write there, and try to copy the file over.
108-
tmpFh, err := ioutil.TempFile(os.TempDir(), "bb-tmpconfig.*.toml")
109-
if err != nil {
110-
logging.Error("Failed to create temporary configuration file")
111-
return
112-
}
113-
tmpFilename := tmpFh.Name()
114-
logging.Debugf("tmpFilename: %s", tmpFilename)
115-
err = tmpFh.Close()
116-
if err != nil {
117-
logging.Error("Failed to create temporary configuration file")
118-
return
119-
}
120-
err = tmpVp.WriteConfigAs(tmpFilename)
121-
if err != nil {
122-
logging.Error(fmt.Sprintf("Failed to write temporary config %s: %s", path, err))
123-
return
124-
}
125-
err = copyFileContent(tmpFilename, path)
126-
if err != nil {
127-
logging.Error(fmt.Sprintf("Failed to write config %s -> %s: %s", tmpFilename, path, err))
128-
return
129-
}
104+
config.WriteViper(tmpVp, path)
130105

131106
logging.SuccessExclamation(fmt.Sprintf("Successfully updated configuration %s", path))
132107
}
@@ -138,28 +113,3 @@ func Add(rootCmd *cobra.Command, _ *options.GlobalOptions) {
138113

139114
rootCmd.AddCommand(&configCommand)
140115
}
141-
142-
func copyFileContent(src string, dst string) error {
143-
sourceFileStat, err := os.Stat(src)
144-
if err != nil {
145-
return err
146-
}
147-
148-
if !sourceFileStat.Mode().IsRegular() {
149-
return fmt.Errorf("%s is not a regular file", src)
150-
}
151-
152-
source, err := os.Open(src)
153-
if err != nil {
154-
return err
155-
}
156-
defer source.Close()
157-
158-
destination, err := os.Create(dst) // Create or trunicate
159-
if err != nil {
160-
return err
161-
}
162-
defer destination.Close()
163-
_, err = io.Copy(destination, source)
164-
return err
165-
}

cmd/commands/repo/clone/clone.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,11 @@ func Add(repoCmd *cobra.Command, globalOpts *options.GlobalOptions) {
3939

4040
configDirectory, filename := config.GetGlobalConfigurationPath()
4141
path := filepath.Join(configDirectory, filename)
42-
// TODO: extract tmpVp stuff to a seperate file
43-
tmpVp := viper.New()
44-
tmpVp.SetConfigType("toml")
45-
tmpVp.SetConfigFile(path)
46-
tmpVp.ReadInConfig()
47-
48-
gitProtocolI, err := config.BbConfigurationValidation.ValidateEntry(config.CONFIG_KEY_REPO_CLONE_GIT_PROTOCOL, gitProtocol)
42+
_, err = config.ValidateAndUpdateEntry(path, config.CONFIG_KEY_REPO_CLONE_GIT_PROTOCOL, gitProtocol)
4943
if err != nil {
5044
logging.Error(err)
5145
return
5246
}
53-
gitProtocol = gitProtocolI.(string)
54-
55-
tmpVp.Set(config.CONFIG_KEY_REPO_CLONE_GIT_PROTOCOL, gitProtocol)
56-
tmpVp.WriteConfig()
5747
}
5848

5949
if len(args) == 0 {

config/config.go

Lines changed: 118 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,21 @@ package config
22

33
import (
44
"fmt"
5+
"io"
6+
"io/ioutil"
7+
"os"
58
"strings"
9+
10+
"github.com/craftamap/bb/util/logging"
11+
"github.com/spf13/viper"
12+
)
13+
14+
const (
15+
CONFIG_KEY_AUTH_USERNAME = "auth.username"
16+
CONFIG_KEY_AUTH_PASSWORD = "auth.password"
17+
CONFIG_KEY_GIT_REMOTE = "git.remote"
18+
CONFIG_KEY_REPO_CLONE_GIT_PROTOCOL = "repo.clone.git_protocol"
19+
CONFIG_KEY_PR_SYNC_SYNC_METHOD = "pr.sync.sync_method"
620
)
721

822
type Validator func(interface{}) (interface{}, error)
@@ -52,23 +66,6 @@ type Entry struct {
5266

5367
type Configuration map[string]Entry
5468

55-
func (c Configuration) ValidateEntry(key string, value interface{}) (interface{}, error) {
56-
e, ok := c[key]
57-
if !ok {
58-
return "", fmt.Errorf("key \"%s\" is not a valid key", key)
59-
}
60-
return e.Validator(value)
61-
}
62-
63-
const (
64-
CONFIG_KEY_AUTH_USERNAME = "auth.username"
65-
CONFIG_KEY_AUTH_PASSWORD = "auth.password"
66-
CONFIG_KEY_GIT_REMOTE = "git.remote"
67-
CONFIG_KEY_REPO_CLONE_GIT_PROTOCOL = "repo.clone.git_protocol"
68-
CONFIG_KEY_PR_SYNC_SYNC_METHOD = "pr.sync.sync_method"
69-
)
70-
71-
7269
var BbConfigurationValidation Configuration = map[string]Entry{
7370
CONFIG_KEY_AUTH_USERNAME: {
7471
Validator: SimpleStringValidator(),
@@ -86,3 +83,107 @@ var BbConfigurationValidation Configuration = map[string]Entry{
8683
Validator: EnumValidator("merge", "rebase"),
8784
},
8885
}
86+
87+
func (c Configuration) ValidateEntry(key string, value interface{}) (interface{}, error) {
88+
e, ok := c[key]
89+
if !ok {
90+
return "", fmt.Errorf("key \"%s\" is not a valid key", key)
91+
}
92+
return e.Validator(value)
93+
}
94+
95+
func ValidateEntry(key string, value interface{}) (interface{}, error) {
96+
return BbConfigurationValidation.ValidateEntry(key, value)
97+
}
98+
99+
func ValidateAndUpdateEntry(filepath string, key string, value interface{}) (interface{}, error) {
100+
sanitizedValue, err := ValidateEntry(key, value)
101+
if err != nil {
102+
return "", err
103+
}
104+
105+
// TODO: Add a filename-to-tmpVp cache - this way, we can prevent creating a new viper every time we want to set a value
106+
vp, err := GetViperForPath(filepath)
107+
if err != nil {
108+
return sanitizedValue, err
109+
}
110+
111+
vp.Set(key, sanitizedValue)
112+
err = WriteViper(vp, filepath)
113+
114+
return sanitizedValue, err
115+
}
116+
117+
func ValidateAndUpdateEntryWithViper(vp viper.Viper, key string, value interface{}) (interface{}, error) {
118+
sanitizedValue, err := ValidateEntry(key, value)
119+
if err != nil {
120+
return "", err
121+
}
122+
123+
vp.Set(key, sanitizedValue)
124+
err = WriteViper(vp, vp.ConfigFileUsed())
125+
126+
return sanitizedValue, err
127+
}
128+
129+
func WriteViper(vp viper.Viper, path string) error {
130+
// WORKAROUND: currently, WriteConfig does not support writing to `.bb`-files despite setting SetConfigType.
131+
// Therefore, we create a temporary file, write there, and try to copy the file over.
132+
tmpFh, err := ioutil.TempFile(os.TempDir(), "bb-tmpconfig.*.toml")
133+
if err != nil {
134+
logging.Error("Failed to create temporary configuration file")
135+
return err
136+
}
137+
tmpFilename := tmpFh.Name()
138+
logging.Debugf("tmpFilename: %s", tmpFilename)
139+
err = tmpFh.Close()
140+
if err != nil {
141+
logging.Error("Failed to create temporary configuration file")
142+
return err
143+
}
144+
err = vp.WriteConfigAs(tmpFilename)
145+
if err != nil {
146+
logging.Error(fmt.Sprintf("Failed to write temporary config %s: %s", path, err))
147+
return err
148+
}
149+
err = copyFileContent(tmpFilename, path)
150+
if err != nil {
151+
logging.Error(fmt.Sprintf("Failed to write config %s -> %s: %s", tmpFilename, path, err))
152+
return err
153+
}
154+
return nil
155+
}
156+
157+
func copyFileContent(src string, dst string) error {
158+
sourceFileStat, err := os.Stat(src)
159+
if err != nil {
160+
return err
161+
}
162+
163+
if !sourceFileStat.Mode().IsRegular() {
164+
return fmt.Errorf("%s is not a regular file", src)
165+
}
166+
167+
source, err := os.Open(src)
168+
if err != nil {
169+
return err
170+
}
171+
defer source.Close()
172+
173+
destination, err := os.Create(dst) // Create or trunicate
174+
if err != nil {
175+
return err
176+
}
177+
defer destination.Close()
178+
_, err = io.Copy(destination, source)
179+
return err
180+
}
181+
182+
func GetViperForPath(path string) (viper.Viper, error) {
183+
tmpVp := viper.New()
184+
tmpVp.SetConfigType("toml")
185+
tmpVp.SetConfigFile(path)
186+
err := tmpVp.ReadInConfig()
187+
188+
return *tmpVp, err
189+
}

0 commit comments

Comments
 (0)