github.com/henvic/wedeploycli@v1.7.6-0.20200319005353-3630f582f284/config/config.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"regexp"
     7  	"runtime"
     8  	"strings"
     9  	"sync"
    10  
    11  	"github.com/hashicorp/errwrap"
    12  	version "github.com/hashicorp/go-version"
    13  	"github.com/henvic/wedeploycli/color"
    14  	"github.com/henvic/wedeploycli/defaults"
    15  	"github.com/henvic/wedeploycli/remotes"
    16  	"github.com/henvic/wedeploycli/verbose"
    17  	"gopkg.in/ini.v1"
    18  )
    19  
    20  // Config of the application
    21  type Config struct {
    22  	Path string
    23  
    24  	Params Params
    25  
    26  	m    sync.Mutex
    27  	file *ini.File
    28  }
    29  
    30  // GetParams gets the configuration parameters in a concurrent safe way.
    31  func (c *Config) GetParams() Params {
    32  	c.m.Lock()
    33  	defer c.m.Unlock()
    34  	return c.Params
    35  }
    36  
    37  // SetParams updates the set of params.
    38  func (c *Config) SetParams(p Params) {
    39  	c.m.Lock()
    40  	defer c.m.Unlock()
    41  	c.Params = p
    42  }
    43  
    44  // Params for the configuration
    45  type Params struct {
    46  	DefaultRemote   string        `ini:"default_remote"`
    47  	NoAutocomplete  bool          `ini:"disable_autocomplete_autoinstall"`
    48  	NoColor         bool          `ini:"disable_colors"`
    49  	NotifyUpdates   bool          `ini:"notify_updates"`
    50  	ReleaseChannel  string        `ini:"release_channel"`
    51  	LastUpdateCheck string        `ini:"last_update_check"`
    52  	PastVersion     string        `ini:"past_version"`
    53  	NextVersion     string        `ini:"next_version"`
    54  	EnableAnalytics bool          `ini:"enable_analytics"`
    55  	AnalyticsID     string        `ini:"analytics_id"`
    56  	EnableCURL      bool          `ini:"enable_curl"`
    57  	Remotes         *remotes.List `ini:"-"`
    58  }
    59  
    60  var parseRemoteSectionNameRegex = regexp.MustCompile(`remote \"(.*)\"`)
    61  
    62  // Load the configuration
    63  func (c *Config) Load() error {
    64  	if err := c.openOrCreate(); err != nil {
    65  		return err
    66  	}
    67  
    68  	c.load()
    69  
    70  	var params = c.GetParams()
    71  
    72  	if params.Remotes == nil {
    73  		params.Remotes = &remotes.List{}
    74  	}
    75  
    76  	c.SetParams(params)
    77  
    78  	c.loadDefaultRemotes()
    79  	return c.validateDefaultRemote()
    80  }
    81  
    82  func (c *Config) openOrCreate() error {
    83  	if c.configExists() {
    84  		return c.read()
    85  	}
    86  
    87  	verbose.Debug("Config file not found.")
    88  	c.file = ini.Empty()
    89  	c.banner()
    90  	return nil
    91  }
    92  
    93  func (c *Config) loadDefaultRemotes() {
    94  	var params = c.GetParams()
    95  	var rl = params.Remotes
    96  
    97  	switch rl.Get(defaults.CloudRemote).Infrastructure {
    98  	case defaults.Infrastructure:
    99  	case "":
   100  		rl.Set(defaults.CloudRemote, remotes.Entry{
   101  			Infrastructure:        defaults.Infrastructure,
   102  			InfrastructureComment: "Default cloud remote",
   103  		})
   104  	default:
   105  		_, _ = fmt.Fprintln(os.Stderr, color.Format(
   106  			color.FgHiRed,
   107  			"Warning: Non-standard wedeploy remote cloud detected"))
   108  	}
   109  }
   110  
   111  func (c *Config) validateDefaultRemote() error {
   112  	var params = c.Params
   113  	var rl = params.Remotes
   114  	var keys = rl.Keys()
   115  
   116  	for _, k := range keys {
   117  		if params.DefaultRemote == k {
   118  			return nil
   119  		}
   120  	}
   121  
   122  	return fmt.Errorf(`Remote "%v" is set as default, but not found.
   123  Please fix your ~/.lcp file`, c.Params.DefaultRemote)
   124  }
   125  
   126  // Save the configuration
   127  func (c *Config) Save() error {
   128  	var cfg = c.file
   129  	var params = c.GetParams()
   130  	var err = cfg.ReflectFrom(&params)
   131  
   132  	if err != nil {
   133  		return errwrap.Wrapf("can't load configuration: {{err}}", err)
   134  	}
   135  
   136  	c.updateRemotes()
   137  	c.simplify()
   138  
   139  	err = cfg.SaveToIndent(c.Path, "    ")
   140  
   141  	if err != nil {
   142  		return errwrap.Wrapf("can't save configuration: {{err}}", err)
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  func (c *Config) setDefaults() {
   149  	var params = c.GetParams()
   150  
   151  	params.EnableAnalytics = true
   152  	params.NotifyUpdates = true
   153  	params.ReleaseChannel = defaults.StableReleaseChannel
   154  	params.DefaultRemote = defaults.CloudRemote
   155  
   156  	// By design Windows users should see no color unless they enable it
   157  	// Issue #51.
   158  	// https://github.com/wedeploy/cli/issues/51
   159  	if runtime.GOOS == "windows" {
   160  		params.NoColor = true
   161  	}
   162  
   163  	c.SetParams(params)
   164  }
   165  
   166  func (c *Config) configExists() bool {
   167  	var _, err = os.Stat(c.Path)
   168  
   169  	switch {
   170  	case err == nil:
   171  		return true
   172  	case os.IsNotExist(err):
   173  		return false
   174  	default:
   175  		panic(err)
   176  	}
   177  }
   178  
   179  func (c *Config) load() {
   180  	c.setDefaults()
   181  
   182  	var params = c.GetParams()
   183  
   184  	if err := c.file.MapTo(&params); err != nil {
   185  		panic(err)
   186  	}
   187  
   188  	c.SetParams(params)
   189  }
   190  
   191  func (c *Config) read() error {
   192  	var err error
   193  	c.file, err = ini.Load(c.Path)
   194  
   195  	if err != nil {
   196  		return errwrap.Wrapf("error reading configuration file: {{err}}\n"+
   197  			"Fix "+c.Path+" by hand or erase it.", err)
   198  	}
   199  
   200  	c.readRemotes()
   201  	c.checkNextVersionCacheIsNewer()
   202  
   203  	return nil
   204  }
   205  
   206  func (c *Config) checkNextVersionCacheIsNewer() {
   207  	if defaults.Version == "master" {
   208  		return
   209  	}
   210  
   211  	vThis, err := version.NewVersion(defaults.Version)
   212  
   213  	if err != nil {
   214  		verbose.Debug(err)
   215  		return
   216  	}
   217  
   218  	var params = c.GetParams()
   219  
   220  	vNext, err := version.NewVersion(params.NextVersion)
   221  
   222  	if err != nil {
   223  		verbose.Debug(err)
   224  		return
   225  	}
   226  
   227  	if vThis.GreaterThan(vNext) {
   228  		params.NextVersion = ""
   229  		c.SetParams(params)
   230  	}
   231  }
   232  
   233  func parseRemoteSectionName(parsable string) (parsed string, is bool) {
   234  	var matches = parseRemoteSectionNameRegex.FindStringSubmatch(parsable)
   235  
   236  	if len(matches) == 2 {
   237  		parsed = matches[1]
   238  		is = true
   239  	}
   240  
   241  	return parsed, is
   242  }
   243  
   244  func (c *Config) listRemotes() []string {
   245  	var list = []string{}
   246  
   247  	for _, k := range c.file.SectionStrings() {
   248  		var key, is = parseRemoteSectionName(k)
   249  
   250  		if !is {
   251  			continue
   252  		}
   253  
   254  		list = append(list, key)
   255  	}
   256  
   257  	return list
   258  }
   259  
   260  func (c *Config) getRemote(key string) *ini.Section {
   261  	return c.file.Section(`remote "` + key + `"`)
   262  }
   263  
   264  func (c *Config) deleteRemote(key string) {
   265  	c.file.DeleteSection(`remote "` + key + `"`)
   266  }
   267  
   268  func (c *Config) readRemotes() {
   269  	var rl = &remotes.List{}
   270  
   271  	for _, k := range c.listRemotes() {
   272  		remote := c.getRemote(k)
   273  		infrastructure := remote.Key("infrastructure")
   274  		service := remote.Key("service")
   275  		username := remote.Key("username")
   276  		token := remote.Key("token")
   277  		comment := remote.Comment
   278  
   279  		rl.Set(k, remotes.Entry{
   280  			Comment:               strings.TrimSpace(comment),
   281  			Infrastructure:        strings.TrimSpace(infrastructure.String()),
   282  			InfrastructureComment: strings.TrimSpace(infrastructure.Comment),
   283  			Service:               strings.TrimSpace(service.String()),
   284  			ServiceComment:        strings.TrimSpace(service.Comment),
   285  			Username:              strings.TrimSpace(username.String()),
   286  			UsernameComment:       strings.TrimSpace(username.Comment),
   287  			Token:                 strings.TrimSpace(token.String()),
   288  			TokenComment:          strings.TrimSpace(token.Comment),
   289  		})
   290  	}
   291  
   292  	var params = c.GetParams()
   293  	params.Remotes = rl
   294  	c.SetParams(params)
   295  }
   296  
   297  func (c *Config) simplify() {
   298  	var mainSection = c.file.Section("")
   299  	var omitempty = []string{"past_version", "next_version", "last_update_check"}
   300  
   301  	for _, k := range omitempty {
   302  		var key = mainSection.Key(k)
   303  		if key.Value() == "" && key.Comment == "" {
   304  			mainSection.DeleteKey(k)
   305  		}
   306  	}
   307  }
   308  
   309  func (c *Config) simplifyRemotes() {
   310  	for _, k := range c.listRemotes() {
   311  		remote := c.getRemote(k)
   312  		infrastructure := remote.Key("infrastructure")
   313  		service := remote.Key("service")
   314  
   315  		if infrastructure.Value() == "" && infrastructure.Comment == "" {
   316  			remote.DeleteKey("infrastructure")
   317  		}
   318  
   319  		if service.Value() == "" && service.Comment == "" {
   320  			remote.DeleteKey("service")
   321  		}
   322  
   323  		if len(remote.Keys()) == 0 && remote.Comment == "" {
   324  			c.deleteRemote(k)
   325  		}
   326  
   327  		var omitempty = []string{"username", "token"}
   328  
   329  		for _, k := range omitempty {
   330  			var key = remote.Key(k)
   331  			if key.Value() == "" && key.Comment == "" {
   332  				remote.DeleteKey(k)
   333  			}
   334  		}
   335  	}
   336  }
   337  
   338  func (c *Config) updateRemotes() {
   339  	var params = c.GetParams()
   340  	var rl = params.Remotes
   341  
   342  	for _, k := range c.listRemotes() {
   343  		if !rl.Has(k) {
   344  			c.deleteRemote(k)
   345  		}
   346  	}
   347  
   348  	for _, k := range rl.Keys() {
   349  		var v = rl.Get(k)
   350  		remote := c.getRemote(k)
   351  
   352  		keyInfrastructure := remote.Key("infrastructure")
   353  		keyInfrastructure.SetValue(v.Infrastructure)
   354  		keyInfrastructure.Comment = v.InfrastructureComment
   355  
   356  		keyService := remote.Key("service")
   357  		keyService.SetValue(v.Service)
   358  		keyService.Comment = v.ServiceComment
   359  
   360  		keyUsername := remote.Key("username")
   361  		keyUsername.SetValue(v.Username)
   362  		keyUsername.Comment = v.UsernameComment
   363  
   364  		keyToken := remote.Key("token")
   365  		keyToken.SetValue(v.Token)
   366  		keyToken.Comment = v.TokenComment
   367  
   368  		remote.Comment = v.Comment
   369  	}
   370  
   371  	c.simplifyRemotes()
   372  }
   373  
   374  func (c *Config) banner() {
   375  	c.file.Section("DEFAULT").Comment = `; Configuration file for Liferay Cloud
   376  ; https://www.liferay.com/products/dxp-cloud`
   377  }