1
1
package config
2
2
3
3
import (
4
- "bytes"
5
- "fmt"
6
4
"os"
7
- "os/exec"
8
5
"path/filepath"
9
- "runtime"
10
- "strings"
11
6
"time"
12
7
13
- "github.com/BurntSushi/toml"
14
- "github.com/mitchellh/go-homedir"
15
8
log "github.com/sirupsen/logrus"
16
9
"github.com/spf13/viper"
17
10
prefixed "github.com/x-cray/logrus-prefixed-formatter"
@@ -52,30 +45,6 @@ type Config struct {
52
45
fs ConfigFS
53
46
}
54
47
55
- // getConfigFolder retrieves the folder where the profiles file is stored
56
- // It searches for the xdg environment path first and will secondarily
57
- // place it in the home directory
58
- func getConfigFolder (xdgPath string ) string {
59
- configPath := xdgPath
60
-
61
- if configPath == "" {
62
- home , err := homedir .Dir ()
63
- if err != nil {
64
- fmt .Println (err )
65
- os .Exit (1 )
66
- }
67
-
68
- configPath = filepath .Join (home , ".config" )
69
- }
70
-
71
- log .WithFields (log.Fields {
72
- "prefix" : "config.Config.GetProfilesFolder" ,
73
- "path" : configPath ,
74
- }).Debug ("Using profiles folder" )
75
-
76
- return filepath .Join (configPath , "hookdeck" )
77
- }
78
-
79
48
// InitConfig reads in profiles file and ENV variables if set.
80
49
func (c * Config ) InitConfig () {
81
50
if c .fs == nil {
@@ -155,37 +124,6 @@ func (c *Config) InitConfig() {
155
124
log .SetFormatter (logFormatter )
156
125
}
157
126
158
- // EditConfig opens the configuration file in the default editor.
159
- func (c * Config ) EditConfig () error {
160
- var err error
161
-
162
- fmt .Println ("Opening config file:" , c .configFile )
163
-
164
- switch runtime .GOOS {
165
- case "darwin" , "linux" :
166
- editor := os .Getenv ("EDITOR" )
167
- if editor == "" {
168
- editor = "vi"
169
- }
170
-
171
- cmd := exec .Command (editor , c .configFile )
172
- // Some editors detect whether they have control of stdin/out and will
173
- // fail if they do not.
174
- cmd .Stdin = os .Stdin
175
- cmd .Stdout = os .Stdout
176
-
177
- return cmd .Run ()
178
- case "windows" :
179
- // As far as I can tell, Windows doesn't have an easily accesible or
180
- // comparable option to $EDITOR, so default to notepad for now
181
- err = exec .Command ("notepad" , c .configFile ).Run ()
182
- default :
183
- err = fmt .Errorf ("unsupported platform" )
184
- }
185
-
186
- return err
187
- }
188
-
189
127
// UseProject selects the active project to be used
190
128
func (c * Config ) UseProject (teamId string , teamMode string ) error {
191
129
c .Profile .TeamID = teamId
@@ -228,10 +166,10 @@ func (c *Config) RemoveAllProfiles() error {
228
166
runtimeViper .SetConfigType ("toml" )
229
167
runtimeViper .SetConfigFile (c .viper .ConfigFileUsed ())
230
168
c .viper = runtimeViper
231
- return c .WriteConfig ()
169
+ return c .writeConfig ()
232
170
}
233
171
234
- func (c * Config ) WriteConfig () error {
172
+ func (c * Config ) writeConfig () error {
235
173
if err := c .fs .makePath (c .viper .ConfigFileUsed ()); err != nil {
236
174
return err
237
175
}
@@ -246,13 +184,13 @@ func (c *Config) WriteConfig() error {
246
184
247
185
// Construct the config struct from flags > local config > global config
248
186
func (c * Config ) constructConfig () {
249
- c .Color = getStringConfig ([] string { c .Color , c .viper .GetString (("color" )), "auto" } )
250
- c .LogLevel = getStringConfig ([] string { c .LogLevel , c .viper .GetString (("log" )), "info" } )
251
- c .APIBaseURL = getStringConfig ([] string { c .APIBaseURL , c .viper .GetString (("api_base" )), hookdeck .DefaultAPIBaseURL } )
252
- c .DashboardBaseURL = getStringConfig ([] string { c .DashboardBaseURL , c .viper .GetString (("dashboard_base" )), hookdeck .DefaultDashboardBaseURL } )
253
- c .ConsoleBaseURL = getStringConfig ([] string { c .ConsoleBaseURL , c .viper .GetString (("console_base" )), hookdeck .DefaultConsoleBaseURL } )
254
- c .WSBaseURL = getStringConfig ([] string { c .WSBaseURL , c .viper .GetString (("ws_base" )), hookdeck .DefaultWebsocektURL } )
255
- c .Profile .Name = getStringConfig ([] string { c .Profile .Name , c .viper .GetString (("profile" )), hookdeck .DefaultProfileName } )
187
+ c .Color = stringCoalesce ( c .Color , c .viper .GetString (("color" )), "auto" )
188
+ c .LogLevel = stringCoalesce ( c .LogLevel , c .viper .GetString (("log" )), "info" )
189
+ c .APIBaseURL = stringCoalesce ( c .APIBaseURL , c .viper .GetString (("api_base" )), hookdeck .DefaultAPIBaseURL )
190
+ c .DashboardBaseURL = stringCoalesce ( c .DashboardBaseURL , c .viper .GetString (("dashboard_base" )), hookdeck .DefaultDashboardBaseURL )
191
+ c .ConsoleBaseURL = stringCoalesce ( c .ConsoleBaseURL , c .viper .GetString (("console_base" )), hookdeck .DefaultConsoleBaseURL )
192
+ c .WSBaseURL = stringCoalesce ( c .WSBaseURL , c .viper .GetString (("ws_base" )), hookdeck .DefaultWebsocektURL )
193
+ c .Profile .Name = stringCoalesce ( c .Profile .Name , c .viper .GetString (("profile" )), hookdeck .DefaultProfileName )
256
194
// Needs to support both profile-based config
257
195
// and top-level config for backward compat. For example:
258
196
// ````
@@ -267,19 +205,9 @@ func (c *Config) constructConfig() {
267
205
// "workspace" > "team"
268
206
// TODO: use "project" instead of "workspace"
269
207
// TODO: use "cli_key" instead of "api_key"
270
- c .Profile .APIKey = getStringConfig ([]string {c .Profile .APIKey , c .viper .GetString (c .Profile .GetConfigField ("api_key" )), c .viper .GetString ("api_key" ), "" })
271
- c .Profile .TeamID = getStringConfig ([]string {c .Profile .TeamID , c .viper .GetString (c .Profile .GetConfigField ("workspace_id" )), c .viper .GetString (c .Profile .GetConfigField ("team_id" )), c .viper .GetString ("workspace_id" ), "" })
272
- c .Profile .TeamMode = getStringConfig ([]string {c .Profile .TeamMode , c .viper .GetString (c .Profile .GetConfigField ("workspace_mode" )), c .viper .GetString (c .Profile .GetConfigField ("team_mode" )), c .viper .GetString ("workspace_mode" ), "" })
273
- }
274
-
275
- func getStringConfig (values []string ) string {
276
- for _ , str := range values {
277
- if str != "" {
278
- return str
279
- }
280
- }
281
-
282
- return values [len (values )- 1 ]
208
+ c .Profile .APIKey = stringCoalesce (c .Profile .APIKey , c .viper .GetString (c .Profile .getConfigField ("api_key" )), c .viper .GetString ("api_key" ), "" )
209
+ c .Profile .TeamID = stringCoalesce (c .Profile .TeamID , c .viper .GetString (c .Profile .getConfigField ("workspace_id" )), c .viper .GetString (c .Profile .getConfigField ("team_id" )), c .viper .GetString ("workspace_id" ), "" )
210
+ c .Profile .TeamMode = stringCoalesce (c .Profile .TeamMode , c .viper .GetString (c .Profile .getConfigField ("workspace_mode" )), c .viper .GetString (c .Profile .getConfigField ("team_mode" )), c .viper .GetString ("workspace_mode" ), "" )
283
211
}
284
212
285
213
// getConfigPath returns the path for the config file.
@@ -310,69 +238,6 @@ func (c *Config) getConfigPath(path string) (string, bool) {
310
238
return localConfigPath , false
311
239
}
312
240
313
- globalConfigFolder := getConfigFolder (os .Getenv ("XDG_CONFIG_HOME" ))
241
+ globalConfigFolder := getSystemConfigFolder (os .Getenv ("XDG_CONFIG_HOME" ))
314
242
return filepath .Join (globalConfigFolder , "config.toml" ), true
315
243
}
316
-
317
- // isProfile identifies whether a value in the config pertains to a profile.
318
- func isProfile (value interface {}) bool {
319
- // TODO: ianjabour - ideally find a better way to identify projects in config
320
- _ , ok := value .(map [string ]interface {})
321
- return ok
322
- }
323
-
324
- // Temporary workaround until https://github.com/spf13/viper/pull/519 can remove a key from viper
325
- func removeKey (v * viper.Viper , key string ) (* viper.Viper , error ) {
326
- configMap := v .AllSettings ()
327
- path := strings .Split (key , "." )
328
- lastKey := strings .ToLower (path [len (path )- 1 ])
329
- deepestMap := deepSearch (configMap , path [0 :len (path )- 1 ])
330
- delete (deepestMap , lastKey )
331
-
332
- buf := new (bytes.Buffer )
333
-
334
- encodeErr := toml .NewEncoder (buf ).Encode (configMap )
335
- if encodeErr != nil {
336
- return nil , encodeErr
337
- }
338
-
339
- nv := viper .New ()
340
- nv .SetConfigType ("toml" ) // hint to viper that we've encoded the data as toml
341
-
342
- err := nv .ReadConfig (buf )
343
- if err != nil {
344
- return nil , err
345
- }
346
-
347
- return nv , nil
348
- }
349
-
350
- // taken from https://github.com/spf13/viper/blob/master/util.go#L199,
351
- // we need this to delete configs, remove when viper supprts unset natively
352
- func deepSearch (m map [string ]interface {}, path []string ) map [string ]interface {} {
353
- for _ , k := range path {
354
- m2 , ok := m [k ]
355
- if ! ok {
356
- // intermediate key does not exist
357
- // => create it and continue from there
358
- m3 := make (map [string ]interface {})
359
- m [k ] = m3
360
- m = m3
361
-
362
- continue
363
- }
364
-
365
- m3 , ok := m2 .(map [string ]interface {})
366
- if ! ok {
367
- // intermediate key is a value
368
- // => replace with a new map
369
- m3 = make (map [string ]interface {})
370
- m [k ] = m3
371
- }
372
-
373
- // continue search from here
374
- m = m3
375
- }
376
-
377
- return m
378
- }
0 commit comments