github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/env/config.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package env 16 17 import ( 18 "errors" 19 "path/filepath" 20 "strings" 21 22 "github.com/dolthub/dolt/go/libraries/doltcore/dbfactory" 23 "github.com/dolthub/dolt/go/libraries/utils/config" 24 "github.com/dolthub/dolt/go/libraries/utils/filesys" 25 "github.com/dolthub/dolt/go/store/datas" 26 ) 27 28 const ( 29 localConfigName = "local" 30 globalConfigName = "global" 31 32 // should be able to have remote specific creds? 33 34 ) 35 36 // ConfigScope is an enum representing the elements that make up the ConfigHierarchy 37 type ConfigScope int 38 39 const ( 40 // LocalConfig is the repository's local config portion of the ConfigHierarchy 41 LocalConfig ConfigScope = iota 42 43 // GlobalConfig is the user's global config portion of the ConfigHierarchy 44 GlobalConfig 45 ) 46 47 const ( 48 // SqlServerGlobalsPrefix is config namespace accessible by the SQL engine (ex: sqlserver.global.key) 49 SqlServerGlobalsPrefix = "sqlserver.global" 50 ) 51 52 // String gives the string name of an element that was used when it was added to the ConfigHierarchy, which is the 53 // same name that is used to retrieve that element of the string hierarchy. 54 func (ce ConfigScope) String() string { 55 switch ce { 56 case LocalConfig: 57 return localConfigName 58 case GlobalConfig: 59 return globalConfigName 60 } 61 62 return "" 63 } 64 65 // DoltCliConfig is the config for the cli 66 type DoltCliConfig struct { 67 config.ReadableConfig 68 69 ch *config.ConfigHierarchy 70 fs filesys.ReadWriteFS 71 } 72 73 var _ config.ReadableConfig = &DoltCliConfig{} 74 75 func LoadDoltCliConfig(hdp HomeDirProvider, fs filesys.ReadWriteFS) (*DoltCliConfig, error) { 76 ch := config.NewConfigHierarchy() 77 78 lPath := getLocalConfigPath() 79 if exists, _ := fs.Exists(lPath); exists { 80 lCfg, err := config.FromFile(lPath, fs) 81 82 if err == nil { 83 ch.AddConfig(localConfigName, lCfg) 84 } 85 } 86 87 gPath, err := getGlobalCfgPath(hdp) 88 if err != nil { 89 return nil, err 90 } 91 92 gCfg, err := ensureGlobalConfig(gPath, fs) 93 if err != nil { 94 return nil, err 95 } 96 97 ch.AddConfig(globalConfigName, gCfg) 98 99 return &DoltCliConfig{ch, ch, fs}, nil 100 } 101 102 func ensureGlobalConfig(path string, fs filesys.ReadWriteFS) (config.ReadWriteConfig, error) { 103 if exists, isDir := fs.Exists(path); exists { 104 if isDir { 105 return nil, errors.New("A directory exists where this file should be. path: " + path) 106 } 107 108 return config.FromFile(path, fs) 109 } 110 111 return config.NewFileConfig(path, fs, map[string]string{}) 112 } 113 114 // CreateLocalConfig creates a new repository local config file with the values from |val| 115 // at the directory |dir|. The |dir| directory must have already been initialized 116 // as a data repository before a local config can be created. 117 func (dcc *DoltCliConfig) CreateLocalConfig(dir string, vals map[string]string) error { 118 return dcc.createLocalConfigAt(dir, vals) 119 } 120 121 func (dcc *DoltCliConfig) createLocalConfigAt(dir string, vals map[string]string) error { 122 doltDir := filepath.Join(dir, dbfactory.DoltDir) 123 if exists, isDir := dcc.fs.Exists(doltDir); !exists { 124 return errors.New(dbfactory.DoltDir + " directory not found. Is the current directory a repository directory?") 125 } else if !isDir { 126 return errors.New("A file exists with the name \"" + dbfactory.DoltDir + "\". This is not a valid file within a data repository directory.") 127 } 128 129 path := filepath.Join(dir, getLocalConfigPath()) 130 cfg, err := config.NewFileConfig(path, dcc.fs, vals) 131 132 if err != nil { 133 return err 134 } 135 136 dcc.ch.AddConfig(localConfigName, cfg) 137 138 return nil 139 } 140 141 // GetConfig retrieves a specific element of the config hierarchy. 142 func (dcc *DoltCliConfig) GetConfig(element ConfigScope) (config.ReadWriteConfig, bool) { 143 switch element { 144 case LocalConfig, GlobalConfig: 145 return dcc.ch.GetConfig(element.String()) 146 default: 147 return nil, false 148 } 149 } 150 151 // GetStringOrDefault retrieves a string from the config hierarchy and returns it if available. Otherwise it returns 152 // the default string value 153 func (dcc *DoltCliConfig) GetStringOrDefault(key, defStr string) string { 154 return GetStringOrDefault(dcc.ch, key, defStr) 155 } 156 157 // IfEmptyUseConfig looks at a strings value and if it is an empty string will try to return a value from the config 158 // hierarchy. If it is missing in the config a pointer to an empty string will be returned. 159 func (dcc *DoltCliConfig) IfEmptyUseConfig(val, key string) string { 160 if len(strings.TrimSpace(val)) > 0 { 161 return val 162 } 163 164 cfgVal, err := dcc.ch.GetString(key) 165 166 if err != nil { 167 s := "" 168 return s 169 } 170 171 return cfgVal 172 } 173 174 func GetStringOrDefault(cfg config.ReadableConfig, key, defStr string) string { 175 if cfg == nil { 176 return defStr 177 } 178 val, err := cfg.GetString(key) 179 if err != nil { 180 return defStr 181 } 182 return val 183 } 184 185 // GetNameAndEmail returns the name and email from the supplied config 186 func GetNameAndEmail(cfg config.ReadableConfig) (string, string, error) { 187 name, err := cfg.GetString(config.UserNameKey) 188 189 if err == config.ErrConfigParamNotFound { 190 return "", "", datas.ErrNameNotConfigured 191 } else if err != nil { 192 return "", "", err 193 } 194 195 email, err := cfg.GetString(config.UserEmailKey) 196 197 if err == config.ErrConfigParamNotFound { 198 return "", "", datas.ErrEmailNotConfigured 199 } else if err != nil { 200 return "", "", err 201 } 202 203 return name, email, nil 204 } 205 206 // writeableLocalDoltCliConfig is an extension to DoltCliConfig that reads values from the hierarchy but writes to 207 // local config. 208 type writeableLocalDoltCliConfig struct { 209 *DoltCliConfig 210 } 211 212 // WriteableConfig returns a ReadWriteConfig reading from this config hierarchy. The config will read from the hierarchy 213 // and write to the local config if it's available, or the global config otherwise. 214 func (dcc *DoltCliConfig) WriteableConfig() config.ReadWriteConfig { 215 return writeableLocalDoltCliConfig{dcc} 216 } 217 218 // SetFailsafes sets the config values given as failsafes, i.e. values that will be returned as a last resort if they 219 // are not found elsewhere in the config hierarchy. The "failsafe" config can be written to in order to conform to the 220 // interface of ConfigHierarchy, but values will not persist beyond this session. 221 // Calling SetFailsafes more than once will overwrite any previous values. 222 // Should only be called after primary configuration of the config hierarchy has been completed. 223 func (dcc DoltCliConfig) SetFailsafes(cfg map[string]string) { 224 existing, ok := dcc.ch.GetConfig("failsafe") 225 if !ok { 226 existing = config.NewEmptyMapConfig() 227 dcc.ch.AddConfig("failsafe", existing) 228 } 229 230 _ = existing.SetStrings(cfg) 231 } 232 233 const ( 234 DefaultEmail = "doltuser@dolthub.com" 235 DefaultName = "Dolt System Account" 236 ) 237 238 var DefaultFailsafeConfig = map[string]string{ 239 config.UserEmailKey: DefaultEmail, 240 config.UserNameKey: DefaultName, 241 } 242 243 func (w writeableLocalDoltCliConfig) SetStrings(updates map[string]string) error { 244 cfg, ok := w.GetConfig(LocalConfig) 245 if !ok { 246 cfg, ok = w.GetConfig(GlobalConfig) 247 if !ok { 248 return errors.New("no local or global config found") 249 } 250 } 251 252 return cfg.SetStrings(updates) 253 } 254 255 func (w writeableLocalDoltCliConfig) Unset(params []string) error { 256 cfg, ok := w.GetConfig(LocalConfig) 257 if !ok { 258 cfg, ok = w.GetConfig(GlobalConfig) 259 if !ok { 260 return errors.New("no local or global config found") 261 } 262 } 263 264 return cfg.Unset(params) 265 }