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  }