Replace viper with koanf (#4845)

* Migrate to koanf
* Use temp logger for crashes before config is initialised
* Remove snake case hacks
* Add migration for config file keys
* Add migration note for new migration
* Renamed viper functions
* Remove front-end viper workaround
* Correctly default scan options
This commit is contained in:
WithoutPants
2024-05-21 11:24:47 +10:00
committed by GitHub
parent 0fa71be697
commit bfc60bb23f
17 changed files with 594 additions and 437 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strconv"
@@ -14,7 +15,9 @@ import (
"golang.org/x/crypto/bcrypt"
"github.com/spf13/viper"
"github.com/knadh/koanf"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/file"
"github.com/stashapp/stash/internal/identify"
"github.com/stashapp/stash/pkg/fsutil"
@@ -184,9 +187,9 @@ const (
autostartVideoOnPlaySelectedDefault = true
ContinuePlaylistDefault = "continue_playlist_default"
ShowStudioAsText = "show_studio_as_text"
CSSEnabled = "cssEnabled"
JavascriptEnabled = "javascriptEnabled"
CustomLocalesEnabled = "customLocalesEnabled"
CSSEnabled = "cssenabled"
JavascriptEnabled = "javascriptenabled"
CustomLocalesEnabled = "customlocalesenabled"
ShowScrubber = "show_scrubber"
showScrubberDefault = true
@@ -242,12 +245,12 @@ const (
DLNAPortDefault = 1338
// Logging options
LogFile = "logFile"
LogOut = "logOut"
LogFile = "logfile"
LogOut = "logout"
defaultLogOut = true
LogLevel = "logLevel"
LogLevel = "loglevel"
defaultLogLevel = "Info"
LogAccess = "logAccess"
LogAccess = "logaccess"
defaultLogAccess = true
// Default settings
@@ -261,7 +264,7 @@ const (
deleteGeneratedDefaultDefault = true
// Desktop Integration Options
NoBrowser = "noBrowser"
NoBrowser = "nobrowser"
NoBrowserDefault = false
NotificationsEnabled = "notifications_enabled"
NotificationsEnabledDefault = true
@@ -283,6 +286,10 @@ var (
defaultMenuItems = []string{"scenes", "images", "movies", "markers", "galleries", "performers", "studios", "tags"}
)
var jsonUnmarshalConf = koanf.UnmarshalConf{
Tag: "json",
}
type MissingConfigError struct {
missingFields []string
}
@@ -303,12 +310,13 @@ func (s *StashBoxError) Error() string {
type Config struct {
// main instance - backed by config file
main *viper.Viper
main *koanf.Koanf
// override instance - populated from flags/environment
// not written to config file
overrides *viper.Viper
overrides *koanf.Koanf
filePath string
isNewSystem bool
// configUpdates chan int
certFile string
@@ -326,6 +334,15 @@ func GetInstance() *Config {
return instance
}
func (i *Config) load(f string) error {
if err := i.main.Load(file.Provider(f), yaml.Parser()); err != nil {
return err
}
i.filePath = f
return nil
}
func (i *Config) IsNewSystem() bool {
return i.isNewSystem
}
@@ -333,7 +350,7 @@ func (i *Config) IsNewSystem() bool {
func (i *Config) SetConfigFile(fn string) {
i.Lock()
defer i.Unlock()
i.main.SetConfigFile(fn)
i.filePath = fn
}
func (i *Config) InitTLS() {
@@ -381,13 +398,41 @@ func (i *Config) Set(key string, value interface{}) {
// }
i.Lock()
defer i.Unlock()
i.main.Set(key, value)
i.set(key, value)
}
func (i *Config) set(key string, value interface{}) {
// assumes lock held
// default behaviour for Set is to merge the value
// we want to replace it
i.main.Delete(key)
if value == nil {
return
}
// test for nil interface as well
refVal := reflect.ValueOf(value)
if refVal.Kind() == reflect.Ptr && refVal.IsNil() {
return
}
_ = i.main.Set(key, value)
}
func (i *Config) SetDefault(key string, value interface{}) {
i.Lock()
defer i.Unlock()
i.main.SetDefault(key, value)
i.setDefault(key, value)
}
func (i *Config) setDefault(key string, value interface{}) {
if !i.main.Exists(key) {
i.set(key, value)
}
}
func (i *Config) SetPassword(value string) {
@@ -402,7 +447,24 @@ func (i *Config) SetPassword(value string) {
func (i *Config) Write() error {
i.Lock()
defer i.Unlock()
return i.main.WriteConfig()
data, err := i.marshal()
if err != nil {
return err
}
return os.WriteFile(i.filePath, data, 0644)
}
func (i *Config) Marshal() ([]byte, error) {
i.RLock()
defer i.RUnlock()
return i.marshal()
}
func (i *Config) marshal() ([]byte, error) {
return i.main.Marshal(yaml.Parser())
}
// FileEnvSet returns true if the configuration file environment parameter
@@ -415,7 +477,7 @@ func FileEnvSet() bool {
func (i *Config) GetConfigFile() string {
i.RLock()
defer i.RUnlock()
return i.main.ConfigFileUsed()
return i.filePath
}
// GetConfigPath returns the path of the directory containing the used
@@ -430,12 +492,12 @@ func (i *Config) GetDefaultDatabaseFilePath() string {
return filepath.Join(i.GetConfigPath(), "stash-go.sqlite")
}
// viper returns the viper instance that should be used to get the provided
// forKey returns the Koanf instance that should be used to get the provided
// key. Returns the overrides instance if the key exists there, otherwise it
// returns the main instance. Assumes read lock held.
func (i *Config) viper(key string) *viper.Viper {
func (i *Config) forKey(key string) *koanf.Koanf {
v := i.main
if i.overrides.IsSet(key) {
if i.overrides.Exists(key) {
v = i.overrides
}
@@ -444,10 +506,10 @@ func (i *Config) viper(key string) *viper.Viper {
// viper returns the viper instance that has the key set. Returns nil
// if no instance has the key. Assumes read lock held.
func (i *Config) viperWith(key string) *viper.Viper {
v := i.viper(key)
func (i *Config) with(key string) *koanf.Koanf {
v := i.forKey(key)
if v.IsSet(key) {
if v.Exists(key) {
return v
}
@@ -458,7 +520,7 @@ func (i *Config) HasOverride(key string) bool {
i.RLock()
defer i.RUnlock()
return i.overrides.IsSet(key)
return i.overrides.Exists(key)
}
// These functions wrap the equivalent viper functions, checking the override
@@ -468,28 +530,28 @@ func (i *Config) unmarshalKey(key string, rawVal interface{}) error {
i.RLock()
defer i.RUnlock()
return i.viper(key).UnmarshalKey(key, rawVal)
return i.forKey(key).Unmarshal(key, rawVal)
}
func (i *Config) getStringSlice(key string) []string {
i.RLock()
defer i.RUnlock()
return i.viper(key).GetStringSlice(key)
return i.forKey(key).Strings(key)
}
func (i *Config) getString(key string) string {
i.RLock()
defer i.RUnlock()
return i.viper(key).GetString(key)
return i.forKey(key).String(key)
}
func (i *Config) getBool(key string) bool {
i.RLock()
defer i.RUnlock()
return i.viper(key).GetBool(key)
return i.forKey(key).Bool(key)
}
func (i *Config) getBoolDefault(key string, def bool) bool {
@@ -497,9 +559,9 @@ func (i *Config) getBoolDefault(key string, def bool) bool {
defer i.RUnlock()
ret := def
v := i.viper(key)
if v.IsSet(key) {
ret = v.GetBool(key)
v := i.forKey(key)
if v.Exists(key) {
ret = v.Bool(key)
}
return ret
}
@@ -508,21 +570,21 @@ func (i *Config) getInt(key string) int {
i.RLock()
defer i.RUnlock()
return i.viper(key).GetInt(key)
return i.forKey(key).Int(key)
}
func (i *Config) getFloat64(key string) float64 {
i.RLock()
defer i.RUnlock()
return i.viper(key).GetFloat64(key)
return i.forKey(key).Float64(key)
}
func (i *Config) getStringMapString(key string) map[string]string {
i.RLock()
defer i.RUnlock()
ret := i.viper(key).GetStringMapString(key)
ret := i.forKey(key).StringMap(key)
// GetStringMapString returns an empty map regardless of whether the
// key exists or not.
@@ -543,13 +605,13 @@ func (i *Config) GetStashPaths() StashConfigs {
var ret StashConfigs
v := i.main
if !v.IsSet(Stash) {
if !v.Exists(Stash) {
v = i.overrides
}
if err := v.UnmarshalKey(Stash, &ret); err != nil || len(ret) == 0 {
if err := v.Unmarshal(Stash, &ret); err != nil || len(ret) == 0 {
// fallback to legacy format
ss := v.GetStringSlice(Stash)
ss := v.Strings(Stash)
ret = nil
for _, path := range ss {
toAdd := &StashConfig{
@@ -650,7 +712,7 @@ func (i *Config) GetImageExcludes() []string {
func (i *Config) GetVideoExtensions() []string {
ret := i.getStringSlice(VideoExtensions)
if ret == nil {
if len(ret) == 0 {
ret = defaultVideoExtensions
}
return ret
@@ -658,7 +720,7 @@ func (i *Config) GetVideoExtensions() []string {
func (i *Config) GetImageExtensions() []string {
ret := i.getStringSlice(ImageExtensions)
if ret == nil {
if len(ret) == 0 {
ret = defaultImageExtensions
}
return ret
@@ -666,7 +728,7 @@ func (i *Config) GetImageExtensions() []string {
func (i *Config) GetGalleryExtensions() []string {
ret := i.getStringSlice(GalleryExtensions)
if ret == nil {
if len(ret) == 0 {
ret = defaultGalleryExtensions
}
return ret
@@ -772,16 +834,15 @@ func (i *Config) GetAllPluginConfiguration() map[string]map[string]interface{} {
ret := make(map[string]map[string]interface{})
sub := i.viper(PluginsSetting).GetStringMap(PluginsSetting)
v := i.forKey(PluginsSetting)
sub := v.Cut(PluginsSetting)
if sub == nil {
return ret
}
for plugin := range sub {
// HACK: viper changes map keys to case insensitive values, so the workaround is to
// convert map keys to snake case for storage
name := fromSnakeCase(plugin)
ret[name] = fromSnakeCaseMap(i.viper(PluginsSetting).GetStringMap(PluginsSettingPrefix + plugin))
for plugin := range sub.Raw() {
ret[plugin] = sub.Cut(plugin).Raw()
}
return ret
@@ -791,26 +852,20 @@ func (i *Config) GetPluginConfiguration(pluginID string) map[string]interface{}
i.RLock()
defer i.RUnlock()
key := PluginsSettingPrefix + toSnakeCase(pluginID)
key := PluginsSettingPrefix + pluginID
// HACK: viper changes map keys to case insensitive values, so the workaround is to
// convert map keys to snake case for storage
v := i.viper(key).GetStringMap(key)
return fromSnakeCaseMap(v)
return i.forKey(key).Cut(key).Raw()
}
// SetPluginConfiguration sets the configuration for a plugin.
// It will overwrite any existing configuration.
func (i *Config) SetPluginConfiguration(pluginID string, v map[string]interface{}) {
i.Lock()
defer i.Unlock()
pluginID = toSnakeCase(pluginID)
key := PluginsSettingPrefix + pluginID
// HACK: viper changes map keys to case insensitive values, so the workaround is to
// convert map keys to snake case for storage
i.viper(key).Set(key, toSnakeCaseMap(v))
i.set(key, v)
}
func (i *Config) GetDisabledPlugins() []string {
@@ -1050,9 +1105,9 @@ func (i *Config) GetMaxSessionAge() int {
defer i.RUnlock()
ret := DefaultMaxSessionAge
v := i.viper(MaxSessionAge)
if v.IsSet(MaxSessionAge) {
ret = v.GetInt(MaxSessionAge)
v := i.forKey(MaxSessionAge)
if v.Exists(MaxSessionAge) {
ret = v.Int(MaxSessionAge)
}
return ret
@@ -1076,9 +1131,9 @@ func (i *Config) GetUILocation() string {
func (i *Config) GetMenuItems() []string {
i.RLock()
defer i.RUnlock()
v := i.viper(MenuItems)
if v.IsSet(MenuItems) {
return v.GetStringSlice(MenuItems)
v := i.forKey(MenuItems)
if v.Exists(MenuItems) {
return v.Strings(MenuItems)
}
return defaultMenuItems
}
@@ -1092,9 +1147,9 @@ func (i *Config) GetWallShowTitle() bool {
defer i.RUnlock()
ret := defaultWallShowTitle
v := i.viper(WallShowTitle)
if v.IsSet(WallShowTitle) {
ret = v.GetBool(WallShowTitle)
v := i.forKey(WallShowTitle)
if v.Exists(WallShowTitle) {
ret = v.Bool(WallShowTitle)
}
return ret
}
@@ -1108,9 +1163,9 @@ func (i *Config) GetWallPlayback() string {
defer i.RUnlock()
ret := defaultWallPlayback
v := i.viper(WallPlayback)
if v.IsSet(WallPlayback) {
ret = v.GetString(WallPlayback)
v := i.forKey(WallPlayback)
if v.Exists(WallPlayback) {
ret = v.String(WallPlayback)
}
return ret
@@ -1144,14 +1199,14 @@ func (i *Config) getSlideshowDelay() int {
// assume have lock
ret := defaultImageLightboxSlideshowDelay
v := i.viper(ImageLightboxSlideshowDelay)
if v.IsSet(ImageLightboxSlideshowDelay) {
ret = v.GetInt(ImageLightboxSlideshowDelay)
v := i.forKey(ImageLightboxSlideshowDelay)
if v.Exists(ImageLightboxSlideshowDelay) {
ret = v.Int(ImageLightboxSlideshowDelay)
} else {
// fallback to old location
v := i.viper(legacyImageLightboxSlideshowDelay)
if v.IsSet(legacyImageLightboxSlideshowDelay) {
ret = v.GetInt(legacyImageLightboxSlideshowDelay)
v := i.forKey(legacyImageLightboxSlideshowDelay)
if v.Exists(legacyImageLightboxSlideshowDelay) {
ret = v.Int(legacyImageLightboxSlideshowDelay)
}
}
@@ -1168,24 +1223,24 @@ func (i *Config) GetImageLightboxOptions() ConfigImageLightboxResult {
SlideshowDelay: &delay,
}
if v := i.viperWith(ImageLightboxDisplayModeKey); v != nil {
mode := ImageLightboxDisplayMode(v.GetString(ImageLightboxDisplayModeKey))
if v := i.with(ImageLightboxDisplayModeKey); v != nil {
mode := ImageLightboxDisplayMode(v.String(ImageLightboxDisplayModeKey))
ret.DisplayMode = &mode
}
if v := i.viperWith(ImageLightboxScaleUp); v != nil {
value := v.GetBool(ImageLightboxScaleUp)
if v := i.with(ImageLightboxScaleUp); v != nil {
value := v.Bool(ImageLightboxScaleUp)
ret.ScaleUp = &value
}
if v := i.viperWith(ImageLightboxResetZoomOnNav); v != nil {
value := v.GetBool(ImageLightboxResetZoomOnNav)
if v := i.with(ImageLightboxResetZoomOnNav); v != nil {
value := v.Bool(ImageLightboxResetZoomOnNav)
ret.ResetZoomOnNav = &value
}
if v := i.viperWith(ImageLightboxScrollModeKey); v != nil {
mode := ImageLightboxScrollMode(v.GetString(ImageLightboxScrollModeKey))
if v := i.with(ImageLightboxScrollModeKey); v != nil {
mode := ImageLightboxScrollMode(v.String(ImageLightboxScrollModeKey))
ret.ScrollMode = &mode
}
if v := i.viperWith(ImageLightboxScrollAttemptsBeforeChange); v != nil {
ret.ScrollAttemptsBeforeChange = v.GetInt(ImageLightboxScrollAttemptsBeforeChange)
if v := i.with(ImageLightboxScrollAttemptsBeforeChange); v != nil {
ret.ScrollAttemptsBeforeChange = v.Int(ImageLightboxScrollAttemptsBeforeChange)
}
return ret
@@ -1204,20 +1259,14 @@ func (i *Config) GetUIConfiguration() map[string]interface{} {
i.RLock()
defer i.RUnlock()
// HACK: viper changes map keys to case insensitive values, so the workaround is to
// convert map keys to snake case for storage
v := i.viper(UI).GetStringMap(UI)
return fromSnakeCaseMap(v)
return i.forKey(UI).Cut(UI).Raw()
}
func (i *Config) SetUIConfiguration(v map[string]interface{}) {
i.Lock()
defer i.Unlock()
// HACK: viper changes map keys to case insensitive values, so the workaround is to
// convert map keys to snake case for storage
i.viper(UI).Set(UI, toSnakeCaseMap(v))
i.set(UI, v)
}
func (i *Config) GetCSSPath() string {
@@ -1375,11 +1424,11 @@ func (i *Config) GetDeleteGeneratedDefault() bool {
func (i *Config) GetDefaultIdentifySettings() *identify.Options {
i.RLock()
defer i.RUnlock()
v := i.viper(DefaultIdentifySettings)
v := i.forKey(DefaultIdentifySettings)
if v.IsSet(DefaultIdentifySettings) {
if v.Exists(DefaultIdentifySettings) && v.Get(DefaultIdentifySettings) != nil {
var ret identify.Options
if err := v.UnmarshalKey(DefaultIdentifySettings, &ret); err != nil {
if err := v.UnmarshalWithConf(DefaultIdentifySettings, &ret, jsonUnmarshalConf); err != nil {
return nil
}
return &ret
@@ -1394,11 +1443,11 @@ func (i *Config) GetDefaultIdentifySettings() *identify.Options {
func (i *Config) GetDefaultScanSettings() *ScanMetadataOptions {
i.RLock()
defer i.RUnlock()
v := i.viper(DefaultScanSettings)
v := i.forKey(DefaultScanSettings)
if v.IsSet(DefaultScanSettings) {
if v.Exists(DefaultScanSettings) && v.Get(DefaultScanSettings) != nil {
var ret ScanMetadataOptions
if err := v.UnmarshalKey(DefaultScanSettings, &ret); err != nil {
if err := v.UnmarshalWithConf(DefaultScanSettings, &ret, jsonUnmarshalConf); err != nil {
return nil
}
return &ret
@@ -1413,11 +1462,11 @@ func (i *Config) GetDefaultScanSettings() *ScanMetadataOptions {
func (i *Config) GetDefaultAutoTagSettings() *AutoTagMetadataOptions {
i.RLock()
defer i.RUnlock()
v := i.viper(DefaultAutoTagSettings)
v := i.forKey(DefaultAutoTagSettings)
if v.IsSet(DefaultAutoTagSettings) {
if v.Exists(DefaultAutoTagSettings) {
var ret AutoTagMetadataOptions
if err := v.UnmarshalKey(DefaultAutoTagSettings, &ret); err != nil {
if err := v.Unmarshal(DefaultAutoTagSettings, &ret); err != nil {
return nil
}
return &ret
@@ -1432,11 +1481,11 @@ func (i *Config) GetDefaultAutoTagSettings() *AutoTagMetadataOptions {
func (i *Config) GetDefaultGenerateSettings() *models.GenerateMetadataOptions {
i.RLock()
defer i.RUnlock()
v := i.viper(DefaultGenerateSettings)
v := i.forKey(DefaultGenerateSettings)
if v.IsSet(DefaultGenerateSettings) {
if v.Exists(DefaultGenerateSettings) {
var ret models.GenerateMetadataOptions
if err := v.UnmarshalKey(DefaultGenerateSettings, &ret); err != nil {
if err := v.Unmarshal(DefaultGenerateSettings, &ret); err != nil {
return nil
}
return &ret
@@ -1543,9 +1592,9 @@ func (i *Config) GetMaxUploadSize() int64 {
defer i.RUnlock()
ret := int64(1024)
v := i.viper(MaxUploadSize)
if v.IsSet(MaxUploadSize) {
ret = v.GetInt64(MaxUploadSize)
v := i.forKey(MaxUploadSize)
if v.Exists(MaxUploadSize) {
ret = v.Int64(MaxUploadSize)
}
return ret << 20
}
@@ -1645,7 +1694,7 @@ func (i *Config) Validate() error {
var missingFields []string
for _, p := range mandatoryPaths {
if !i.viper(p).IsSet(p) || i.viper(p).GetString(p) == "" {
if !i.forKey(p).Exists(p) || i.forKey(p).String(p) == "" {
missingFields = append(missingFields, p)
}
}
@@ -1656,7 +1705,7 @@ func (i *Config) Validate() error {
}
}
if i.GetBlobsStorage() == BlobStorageTypeFilesystem && i.viper(BlobsPath).GetString(BlobsPath) == "" {
if i.GetBlobsStorage() == BlobStorageTypeFilesystem && i.forKey(BlobsPath).String(BlobsPath) == "" {
return MissingConfigError{
missingFields: []string{BlobsPath},
}
@@ -1676,52 +1725,52 @@ func (i *Config) setDefaultValues() {
// set the default host and port so that these are written to the config
// file
i.main.SetDefault(Host, hostDefault)
i.main.SetDefault(Port, portDefault)
i.setDefault(Host, hostDefault)
i.setDefault(Port, portDefault)
i.main.SetDefault(ParallelTasks, parallelTasksDefault)
i.main.SetDefault(SequentialScanning, SequentialScanningDefault)
i.main.SetDefault(PreviewSegmentDuration, previewSegmentDurationDefault)
i.main.SetDefault(PreviewSegments, previewSegmentsDefault)
i.main.SetDefault(PreviewExcludeStart, previewExcludeStartDefault)
i.main.SetDefault(PreviewExcludeEnd, previewExcludeEndDefault)
i.main.SetDefault(PreviewAudio, previewAudioDefault)
i.main.SetDefault(SoundOnPreview, false)
i.setDefault(ParallelTasks, parallelTasksDefault)
i.setDefault(SequentialScanning, SequentialScanningDefault)
i.setDefault(PreviewSegmentDuration, previewSegmentDurationDefault)
i.setDefault(PreviewSegments, previewSegmentsDefault)
i.setDefault(PreviewExcludeStart, previewExcludeStartDefault)
i.setDefault(PreviewExcludeEnd, previewExcludeEndDefault)
i.setDefault(PreviewAudio, previewAudioDefault)
i.setDefault(SoundOnPreview, false)
i.main.SetDefault(ThemeColor, DefaultThemeColor)
i.setDefault(ThemeColor, DefaultThemeColor)
i.main.SetDefault(WriteImageThumbnails, writeImageThumbnailsDefault)
i.main.SetDefault(CreateImageClipsFromVideos, createImageClipsFromVideosDefault)
i.setDefault(WriteImageThumbnails, writeImageThumbnailsDefault)
i.setDefault(CreateImageClipsFromVideos, createImageClipsFromVideosDefault)
i.main.SetDefault(Database, defaultDatabaseFilePath)
i.setDefault(Database, defaultDatabaseFilePath)
i.main.SetDefault(dangerousAllowPublicWithoutAuth, dangerousAllowPublicWithoutAuthDefault)
i.main.SetDefault(SecurityTripwireAccessedFromPublicInternet, securityTripwireAccessedFromPublicInternetDefault)
i.setDefault(dangerousAllowPublicWithoutAuth, dangerousAllowPublicWithoutAuthDefault)
i.setDefault(SecurityTripwireAccessedFromPublicInternet, securityTripwireAccessedFromPublicInternetDefault)
// Set generated to the metadata path for backwards compat
i.main.SetDefault(Generated, i.main.GetString(Metadata))
i.setDefault(Generated, i.main.String(Metadata))
i.main.SetDefault(NoBrowser, NoBrowserDefault)
i.main.SetDefault(NotificationsEnabled, NotificationsEnabledDefault)
i.main.SetDefault(ShowOneTimeMovedNotification, ShowOneTimeMovedNotificationDefault)
i.setDefault(NoBrowser, NoBrowserDefault)
i.setDefault(NotificationsEnabled, NotificationsEnabledDefault)
i.setDefault(ShowOneTimeMovedNotification, ShowOneTimeMovedNotificationDefault)
// Set default scrapers and plugins paths
i.main.SetDefault(ScrapersPath, defaultScrapersPath)
i.main.SetDefault(PluginsPath, defaultPluginsPath)
i.setDefault(ScrapersPath, defaultScrapersPath)
i.setDefault(PluginsPath, defaultPluginsPath)
// Set default gallery cover regex
i.main.SetDefault(GalleryCoverRegex, galleryCoverRegexDefault)
i.setDefault(GalleryCoverRegex, galleryCoverRegexDefault)
// Set NoProxy default
i.main.SetDefault(NoProxy, noProxyDefault)
i.setDefault(NoProxy, noProxyDefault)
// set default package sources
i.main.SetDefault(PluginPackageSources, []map[string]string{{
i.setDefault(PluginPackageSources, []map[string]string{{
"name": sourceDefaultName,
"url": pluginPackageSourcesDefault,
"localpath": sourceDefaultPath,
}})
i.main.SetDefault(ScraperPackageSources, []map[string]string{{
i.setDefault(ScraperPackageSources, []map[string]string{{
"name": sourceDefaultName,
"url": scraperPackageSourcesDefault,
"localpath": sourceDefaultPath,
@@ -1737,13 +1786,13 @@ func (i *Config) setExistingSystemDefaults() {
if !i.isNewSystem {
// Existing systems as of the introduction of auto-browser open should retain existing
// behavior and not start the browser automatically.
if !i.main.InConfig(NoBrowser) {
i.main.Set(NoBrowser, true)
if !i.main.Exists(NoBrowser) {
i.set(NoBrowser, true)
}
// Existing systems as of the introduction of the taskbar should inform users.
if !i.main.InConfig(ShowOneTimeMovedNotification) {
i.main.Set(ShowOneTimeMovedNotification, true)
if !i.main.Exists(ShowOneTimeMovedNotification) {
i.set(ShowOneTimeMovedNotification, true)
}
}
}