diff --git a/cmd/web.go b/cmd/web.go index 8722ddb609e57..fea0d717df6cf 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -17,6 +17,8 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/setting/base" + "code.gitea.io/gitea/modules/setting/history" "code.gitea.io/gitea/routers" "code.gitea.io/gitea/routers/install" @@ -157,8 +159,13 @@ func runWeb(ctx *cli.Context) error { } log.Info("Global init") + // Perform global initialization setting.InitProviderFromExistingFile() + + // Print now outdated settings - is before any other setting action to prevent accidental initialization of other keys + history.PrintRemovedSettings(map[history.SettingsSource]base.ConfigProvider{history.SettingsSourceINI: setting.CfgProvider}) // TODO: modules/setting/setting.go#loadCommonSettingsFrom would be more fitting as a place for this call, but it is called twice during initialization for some reason + setting.LoadCommonSettings() routers.GlobalInitInstalled(graceful.GetManager().HammerContext()) @@ -217,6 +224,7 @@ func listen(m http.Handler, handleRedirector bool) error { } _, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: Gitea Server", process.SystemProcessType, true) defer finished() + log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL) // This can be useful for users, many users do wrong to their config and get strange behaviors behind a reverse-proxy. // A user may fix the configuration mistake when he sees this log. diff --git a/modules/setting/actions.go b/modules/setting/actions.go index b11500dab4a44..164e3a6939b4b 100644 --- a/modules/setting/actions.go +++ b/modules/setting/actions.go @@ -5,6 +5,7 @@ package setting import ( "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) // Actions settings @@ -19,7 +20,7 @@ var ( } ) -func loadActionsFrom(rootCfg ConfigProvider) { +func loadActionsFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("actions") if err := sec.MapTo(&Actions); err != nil { log.Fatal("Failed to map Actions settings: %v", err) diff --git a/modules/setting/admin.go b/modules/setting/admin.go index 2d2dd26de94ae..16ece8f61d4c1 100644 --- a/modules/setting/admin.go +++ b/modules/setting/admin.go @@ -3,14 +3,16 @@ package setting +import "code.gitea.io/gitea/modules/setting/base" + // Admin settings var Admin struct { DisableRegularOrgCreation bool DefaultEmailNotification string } -func loadAdminFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "admin", &Admin) +func loadAdminFrom(rootCfg base.ConfigProvider) { + base.MustMapSetting(rootCfg, "admin", &Admin) sec := rootCfg.Section("admin") Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled") } diff --git a/modules/setting/api.go b/modules/setting/api.go index c36f05cfd1c2d..445ae5f185443 100644 --- a/modules/setting/api.go +++ b/modules/setting/api.go @@ -8,6 +8,7 @@ import ( "path" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) // API settings @@ -27,8 +28,8 @@ var API = struct { DefaultMaxBlobSize: 10485760, } -func loadAPIFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "api", &API) +func loadAPIFrom(rootCfg base.ConfigProvider) { + base.MustMapSetting(rootCfg, "api", &API) defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort u, err := url.Parse(rootCfg.Section("server").Key("ROOT_URL").MustString(defaultAppURL)) diff --git a/modules/setting/attachment.go b/modules/setting/attachment.go index 8b6eb9fd7a1ed..f54a9be14fb41 100644 --- a/modules/setting/attachment.go +++ b/modules/setting/attachment.go @@ -3,6 +3,8 @@ package setting +import "code.gitea.io/gitea/modules/setting/base" + // Attachment settings var Attachment = struct { Storage @@ -20,7 +22,7 @@ var Attachment = struct { Enabled: true, } -func loadAttachmentFrom(rootCfg ConfigProvider) { +func loadAttachmentFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("attachment") storageType := sec.Key("STORAGE_TYPE").MustString("") diff --git a/modules/setting/base/config_provider.go b/modules/setting/base/config_provider.go new file mode 100644 index 0000000000000..d0cf407812196 --- /dev/null +++ b/modules/setting/base/config_provider.go @@ -0,0 +1,27 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package base + +import ( + "code.gitea.io/gitea/modules/log" + + ini "gopkg.in/ini.v1" +) + +// ConfigProvider represents a config provider +type ConfigProvider interface { + Section(section string) *ini.Section + NewSection(name string) (*ini.Section, error) + GetSection(name string) (*ini.Section, error) + HasSection(name string) bool +} + +// a file is an implementation ConfigProvider and other implementations are possible, i.e. from docker, k8s, … +var _ ConfigProvider = &ini.File{} + +func MustMapSetting(rootCfg ConfigProvider, sectionName string, setting interface{}) { + if err := rootCfg.Section(sectionName).MapTo(setting); err != nil { + log.Fatal("Failed to map %s settings: %v", sectionName, err) + } +} diff --git a/modules/setting/cache.go b/modules/setting/cache.go index 783246077d906..778177e5d9071 100644 --- a/modules/setting/cache.go +++ b/modules/setting/cache.go @@ -8,6 +8,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) // Cache represents cache settings @@ -49,7 +50,7 @@ var CacheService = struct { // MemcacheMaxTTL represents the maximum memcache TTL const MemcacheMaxTTL = 30 * 24 * time.Hour -func loadCacheFrom(rootCfg ConfigProvider) { +func loadCacheFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("cache") if err := sec.MapTo(&CacheService); err != nil { log.Fatal("Failed to map Cache settings: %v", err) diff --git a/modules/setting/camo.go b/modules/setting/camo.go index 366e9a116cd5b..ef3311ef76ae2 100644 --- a/modules/setting/camo.go +++ b/modules/setting/camo.go @@ -3,7 +3,10 @@ package setting -import "code.gitea.io/gitea/modules/log" +import ( + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" +) var Camo = struct { Enabled bool @@ -12,8 +15,8 @@ var Camo = struct { Allways bool }{} -func loadCamoFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "camo", &Camo) +func loadCamoFrom(rootCfg base.ConfigProvider) { + base.MustMapSetting(rootCfg, "camo", &Camo) if Camo.Enabled { if Camo.ServerURL == "" || Camo.HMACKey == "" { log.Fatal(`Camo settings require "SERVER_URL" and HMAC_KEY`) diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go deleted file mode 100644 index 0244a8d06ed64..0000000000000 --- a/modules/setting/config_provider.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package setting - -import ( - "code.gitea.io/gitea/modules/log" - - ini "gopkg.in/ini.v1" -) - -// ConfigProvider represents a config provider -type ConfigProvider interface { - Section(section string) *ini.Section - NewSection(name string) (*ini.Section, error) - GetSection(name string) (*ini.Section, error) -} - -// a file is an implementation ConfigProvider and other implementations are possible, i.e. from docker, k8s, … -var _ ConfigProvider = &ini.File{} - -func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting interface{}) { - if err := rootCfg.Section(sectionName).MapTo(setting); err != nil { - log.Fatal("Failed to map %s settings: %v", sectionName, err) - } -} - -func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) { - if rootCfg.Section(oldSection).HasKey(oldKey) { - log.Error("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version) - } -} - -// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini -func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) { - if rootCfg.Section(oldSection).HasKey(oldKey) { - log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey) - } -} diff --git a/modules/setting/cors.go b/modules/setting/cors.go index 260848b5df33c..ba04e70170d00 100644 --- a/modules/setting/cors.go +++ b/modules/setting/cors.go @@ -7,6 +7,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) // CORSConfig defines CORS settings @@ -27,8 +28,8 @@ var CORSConfig = struct { XFrameOptions: "SAMEORIGIN", } -func loadCorsFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "cors", &CORSConfig) +func loadCorsFrom(rootCfg base.ConfigProvider) { + base.MustMapSetting(rootCfg, "cors", &CORSConfig) if CORSConfig.Enabled { log.Info("CORS Service Enabled") } diff --git a/modules/setting/cron.go b/modules/setting/cron.go index 45bae4dde37a9..6526352e6735b 100644 --- a/modules/setting/cron.go +++ b/modules/setting/cron.go @@ -3,14 +3,18 @@ package setting -import "reflect" +import ( + "reflect" + + "code.gitea.io/gitea/modules/setting/base" +) // GetCronSettings maps the cron subsection to the provided config func GetCronSettings(name string, config interface{}) (interface{}, error) { return getCronSettings(CfgProvider, name, config) } -func getCronSettings(rootCfg ConfigProvider, name string, config interface{}) (interface{}, error) { +func getCronSettings(rootCfg base.ConfigProvider, name string, config interface{}) (interface{}, error) { if err := rootCfg.Section("cron." + name).MapTo(config); err != nil { return config, err } diff --git a/modules/setting/federation.go b/modules/setting/federation.go index 2bea900633fd1..9c697ba076d3c 100644 --- a/modules/setting/federation.go +++ b/modules/setting/federation.go @@ -5,6 +5,7 @@ package setting import ( "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" "github.com/go-fed/httpsig" ) @@ -33,7 +34,7 @@ var ( // HttpsigAlgs is a constant slice of httpsig algorithm objects var HttpsigAlgs []httpsig.Algorithm -func loadFederationFrom(rootCfg ConfigProvider) { +func loadFederationFrom(rootCfg base.ConfigProvider) { if err := rootCfg.Section("federation").MapTo(&Federation); err != nil { log.Fatal("Failed to map Federation settings: %v", err) } else if !httpsig.IsSupportedDigestAlgorithm(Federation.DigestAlgorithm) { diff --git a/modules/setting/git.go b/modules/setting/git.go index 457b35936e9f2..0d4185e50e871 100644 --- a/modules/setting/git.go +++ b/modules/setting/git.go @@ -8,6 +8,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) // Git settings @@ -67,7 +68,7 @@ var Git = struct { }, } -func loadGitFrom(rootCfg ConfigProvider) { +func loadGitFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("git") if err := sec.MapTo(&Git); err != nil { log.Fatal("Failed to map Git settings: %v", err) diff --git a/modules/setting/history/breaking_changes.go b/modules/setting/history/breaking_changes.go new file mode 100644 index 0000000000000..9106428a0acb4 --- /dev/null +++ b/modules/setting/history/breaking_changes.go @@ -0,0 +1,72 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package history + +import "code.gitea.io/gitea/modules/log" + +// This file is the only file that should be changed frequently in this package + +var currentGiteaVersion = getVersion("1.19") + +// Adds all previously removed settings +// It should declare all breaking configuration changes in chronological order to ensure a monotone increasing error log +func init() { + log.Trace("Start checking settings for if they are outdated") + MoveIniSettingInSection("1.6", "api", "ENABLE_SWAGGER_ENDPOINT", "ENABLE_SWAGGER") + + PurgeIniSettings("1.9", "log.database", "LEVEL", "DRIVER", "CONN") + + MoveIniSetting("1.12", "markup.sanitizer", "ELEMENT", "markup.sanitizer.1", "ELEMENT") + MoveIniSetting("1.12", "markup.sanitizer", "ALLOW_ATTR", "markup.sanitizer.1", "ALLOW_ATTR") + MoveIniSetting("1.12", "markup.sanitizer", "REGEXP", "markup.sanitizer.1", "REGEXP") + + PurgeIniSettings("1.14", "log", "MACARON", "REDIRECT_MACARON_LOG") + + MoveIniSetting("1.15", "indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE") + MoveIniSetting("1.15", "indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR") + MoveIniSetting("1.15", "indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR") + MoveIniSetting("1.15", "indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH") + MoveIniSetting("1.15", "indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH") + + MoveIniSettingInSection("1.17", "cron.archive_cleanup", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.update_mirrors", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.repo_health_check", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.check_repo_stats", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.update_migration_poster_id", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.sync_external_users", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.deleted_branches_cleanup", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.delete_inactive_accounts", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.delete_repo_archives", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.git_gc_repos", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.resync_all_sshkeys", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.resync_all_hooks", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.reinit_missing_repos", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.delete_missing_repos", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.delete_generated_repository_avatars", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + MoveIniSettingInSection("1.17", "cron.delete_old_actions", "NO_SUCCESS_NOTICE", "NOTIFY_ON_SUCCESS") + + PurgeIniSettings("1.18", "U2F", "APP_ID") + MoveIniSettingsToDB("1.18", "picture", "ENABLE_FEDERATED_AVATAR", "DISABLE_GRAVATAR") + MoveIniSettingInSection("1.18", "mailer", "HOST", "SMTP_ADDR") + MoveIniSettingInSection("1.18", "mailer", "MAILER_TYPE", "PROTOCOL") + MoveIniSettingInSection("1.18", "mailer", "IS_TLS_ENABLED", "PROTOCOL") + MoveIniSettingInSection("1.18", "mailer", "DISABLE_HELO", "ENABLE_HELO") + MoveIniSettingInSection("1.18", "mailer", "SKIP_VERIFY", "FORCE_TRUST_SERVER_CERT") + MoveIniSettingInSection("1.18", "mailer", "USE_CERTIFICATE", "USE_CLIENT_CERT") + MoveIniSettingInSection("1.18", "mailer", "CERT_FILE", "CLIENT_CERT_FILE") + MoveIniSettingInSection("1.18", "mailer", "KEY_FILE", "CLIENT_KEY_FILE") + MoveIniSettingInSection("1.18", "server", "ENABLE_LETSENCRYPT", "ENABLE_ACME") + MoveIniSettingInSection("1.18", "server", "LETSENCRYPT_ACCEPTTOS", "ACME_ACCEPTTOS") + MoveIniSettingInSection("1.18", "server", "LETSENCRYPT_DIRECTORY", "ACME_DIRECTORY") + MoveIniSettingInSection("1.18", "server", "LETSENCRYPT_EMAIL", "ACME_EMAIL") + MoveIniSetting("1.18", "server", "LFS_CONTENT_PATH", "lfs", "PATH") + MoveIniSetting("1.18", "task", "QUEUE_TYPE", "queue.task", "TYPE") + MoveIniSetting("1.18", "task", "QUEUE_CONN_STR", "queue.task", "CONN_STR") + MoveIniSetting("1.18", "task", "QUEUE_LENGTH", "queue.task", "LENGTH") + MoveIniSetting("1.18", "repository", "DISABLE_MIRRORS", "mirror", "ENABLED") + + PurgeIniSettings("1.19", "ui", "ONLY_SHOW_RELEVANT_REPOS") + + log.Trace("Finish checking settings for if they are outdated") +} diff --git a/modules/setting/history/exposed_api.go b/modules/setting/history/exposed_api.go new file mode 100644 index 0000000000000..32a5ae40b7a74 --- /dev/null +++ b/modules/setting/history/exposed_api.go @@ -0,0 +1,131 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package history + +// This file contains the methods that will be exposed to other packages +import ( + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" +) + +var removedSettings map[SettingsSource]map[string][]*historyEntry = make(map[SettingsSource]map[string][]*historyEntry) // ordered by old source and then by old section (for performance) + +func addHistoryEntry(entry *historyEntry) { + source := entry.oldValue.Source() + section := entry.oldValue.Section() + sections := removedSettings[source] + + if sections == nil { + sections = make(map[string][]*historyEntry) + removedSettings[source] = sections + } + + entriesInSection := sections[section] + entriesInSection = append(entriesInSection, entry) + sections[section] = entriesInSection +} + +// MoveIniSetting adds a notice that the given setting under "[section].key" has been replaced by "[replacementSection].replacementKey" inside the ini configuration file +// Everything should be exactly like it is in the configuration file +func MoveIniSetting(version, section, key, replacementSection, replacementKey string) { + addHistoryEntry(&historyEntry{ + happensIn: getVersion(version), + oldValue: &iniSetting{ + section: section, + key: key, + }, + newValue: &iniSetting{ + section: replacementSection, + key: replacementKey, + }, + event: typeMovedFromIniToIni, + }) +} + +// MoveIniSettingInSection adds a notice that the given setting under "[section].key" has been replaced by "[section].replacementKey" +// Everything should be exactly like it is in the app.ini +func MoveIniSettingInSection(version, section, key, replacementKey string) { + MoveIniSetting(version, section, key, section, replacementKey) +} + +// PurgeIniSettings adds a notice that the given settings under "[section].key(s)" have been removed without any replacement +// Everything should be exactly like it is in the app.ini +func PurgeIniSettings(version, section string, keys ...string) { + for _, key := range keys { + addHistoryEntry(&historyEntry{ + happensIn: getVersion(version), + oldValue: &iniSetting{ + section: section, + key: key, + }, + event: typeRemoved, + }) + } +} + +// MoveIniSettingsToDB marks all given setting keys in the given section as moved to the database. +// keys should be formatted exactly like they are in the app.ini +func MoveIniSettingsToDB(version, section string, keys ...string) { + for _, key := range keys { + addHistoryEntry(&historyEntry{ + happensIn: getVersion(version), + oldValue: &iniSetting{ + section: section, + key: key, + }, + newValue: &dbSetting{ + section: section, + key: key, + }, + event: typeMovedFromIniToDB, + }) + } +} + +// PrintRemovedSettings adds a warning in the logs for all settings that are still present despite not being used anymore +// settingsProviders maps each settings source to its "physical" counterpart providing the actual settings, i.e. maps SettingsSourceINI to an *ini.File +// Pass action to specify what will be done when an invalid setting has been found. Defaults to "log.Error(template, args)" +// Template arguments are +// - %[1]s: old settings value ([section].key) +// - %[2]s: old setting source (ini, db, …) +// - %[3]s: gitea version of the change (1.19.0) +// - %[4]s: new settings value (if present) +// - %[5]s: new setting source (if present) +func PrintRemovedSettings(settingsProviders map[SettingsSource]base.ConfigProvider, action ...func(template string, args ...interface{}) error) error { + onInvalid := func(template string, args ...interface{}) error { + log.Error(template, args...) + return nil + } + if len(action) > 0 { + onInvalid = action[0] + } + for source, provider := range settingsProviders { + if err := printBreakingSettingChanges(provider, removedSettings[source], onInvalid); err != nil { + return err + } + } + return nil +} + +func printBreakingSettingChanges(settingProvider base.ConfigProvider, outdatedSettings map[string][]*historyEntry, action func(template string, args ...interface{}) error) error { + for sectionName, removedList := range outdatedSettings { + if !settingProvider.HasSection(sectionName) { + continue + } + section, err := settingProvider.GetSection(sectionName) + if err != nil { + return err + } + for _, removed := range removedList { + if section.HasKey(removed.oldValue.Key()) { + if removed.newValue == nil { + action(removed.getTemplateLogMessage(), removed.oldValue.String(), string(removed.oldValue.Source()), removed.happensIn.String()) + } else { + action(removed.getTemplateLogMessage(), removed.oldValue.String(), string(removed.oldValue.Source()), removed.happensIn.String(), removed.newValue.String(), string(removed.newValue.Source())) + } + } + } + } + return nil +} diff --git a/modules/setting/history/history_entry.go b/modules/setting/history/history_entry.go new file mode 100644 index 0000000000000..db51d2a6a6af5 --- /dev/null +++ b/modules/setting/history/history_entry.go @@ -0,0 +1,53 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package history + +import ( + "strconv" + + version "github.com/hashicorp/go-version" +) + +type eventType int + +const ( + typeRemoved eventType = iota + typeMovedFromIniToIni + typeMovedFromIniToDB +) + +type historyEntry struct { + happensIn *version.Version // Gitea version that removed it / will remove it + oldValue setting + newValue setting // nil means removed without replacement + event eventType +} + +// getTemplateLogMessage returns an unformatted log message for this history entry. +// The returned template accepts the following commands: +// - %[1]s: old settings value ([section].key) +// - %[2]s: old setting source (ini, db, …) +// - %[3]s: gitea version of the change (1.19.0) +// - %[4]s: new settings value (if present) +// - %[5]s: new setting source (if present) +func (e *historyEntry) getTemplateLogMessage() string { + return "The setting %[1]s in your %[2]s is no longer used starting with Gitea %[3]s. " + e.getReplacementHint() +} + +// getReplacementHint returns an unformatted string on how to replace the setting with its new value +// Parameters are: +// - %[4]s: new settings value (if present) +// - %[5]s: new setting source (if present) +func (e *historyEntry) getReplacementHint() string { + switch e.event { + case typeRemoved: + return "It has no documented replacement." + case typeMovedFromIniToIni: + return "Please use the new value %[4]s in the %[5]s instead." + case typeMovedFromIniToDB: + return "Please use the key %[4]s in the %[5]s instead. The current value will be/has been copied to it." + default: + panic("Unimplemented history event type: " + strconv.Itoa(int(e.event))) + } +} diff --git a/modules/setting/history/setting_types.go b/modules/setting/history/setting_types.go new file mode 100644 index 0000000000000..26d8d1e07f700 --- /dev/null +++ b/modules/setting/history/setting_types.go @@ -0,0 +1,111 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package history + +import ( + "strings" +) + +type SettingsSource string + +const ( + SettingsSourceINI SettingsSource = "ini config file" + SettingsSourceDB SettingsSource = "database table 'system setting'" +) + +var settingsSources []SettingsSource = []SettingsSource{SettingsSourceINI, SettingsSourceDB} + +type setting interface { + String() string // Returns a string representation of the given setting that can be found like that in the given settings source + Section() string // If a type doesn't support sectioning, use "" as the global scope + // Returns the un-normalized section this setting belongs to as passed in the init method of this package. + // There might be settings that don't conform with the normalization (which is why they are replaced) + + Key() string // The un-normalized key of this setting + IsNormalized() bool + Normalize() + Source() SettingsSource // What type is it contained in? app.ini, db, … +} + +// A setting that is contained in the app.ini +type iniSetting struct { + section, key string + normalizedSection, normalizedKey string + isNormalized bool +} + +var _ setting = &iniSetting{} + +func (s *iniSetting) Normalize() { + if s.IsNormalized() { + return + } + s.normalizedSection = strings.ToLower(s.section) + s.normalizedKey = strings.ToUpper(s.key) + s.isNormalized = true +} + +func (s *iniSetting) IsNormalized() bool { + return s.isNormalized +} + +func (s *iniSetting) String() string { + s.Normalize() + return "[" + s.normalizedSection + "]." + s.normalizedKey +} + +func (s *iniSetting) Section() string { + s.Normalize() + return s.section +} + +func (s *iniSetting) Key() string { + s.Normalize() + return s.key +} + +func (_ *iniSetting) Source() SettingsSource { + return SettingsSourceINI +} + +// A setting that is stored in the database +type dbSetting struct { + section, key string + normalizedSection, normalizedKey string + isNormalized bool +} + +var _ setting = &dbSetting{} + +func (s *dbSetting) Normalize() { + if s.IsNormalized() { + return + } + s.normalizedSection = strings.ToLower(s.section) + s.normalizedKey = strings.ToLower(s.key) + s.isNormalized = true +} + +func (s *dbSetting) IsNormalized() bool { + return s.isNormalized +} + +func (s *dbSetting) String() string { + s.Normalize() + return s.section + "." + s.key +} + +func (s *dbSetting) Section() string { + s.Normalize() + return s.section +} + +func (s *dbSetting) Key() string { + s.Normalize() + return s.key +} + +func (_ *dbSetting) Source() SettingsSource { + return SettingsSourceDB +} diff --git a/modules/setting/history/version_cache.go b/modules/setting/history/version_cache.go new file mode 100644 index 0000000000000..d2034425cbaaa --- /dev/null +++ b/modules/setting/history/version_cache.go @@ -0,0 +1,17 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package history + +import ( + version "github.com/hashicorp/go-version" +) + +var versionCache map[string]*version.Version = make(map[string]*version.Version) // Multiple settings share the same version, so cache it instead of always creating a new version + +func getVersion(stringVersion string) *version.Version { + if _, ok := versionCache[stringVersion]; !ok { + versionCache[stringVersion] = version.Must(version.NewVersion(stringVersion)) + } + return versionCache[stringVersion] +} diff --git a/modules/setting/i18n.go b/modules/setting/i18n.go index c3076c0ab70b8..93b99e751c978 100644 --- a/modules/setting/i18n.go +++ b/modules/setting/i18n.go @@ -3,6 +3,8 @@ package setting +import "code.gitea.io/gitea/modules/setting/base" + // defaultI18nLangNames must be a slice, we need the order var defaultI18nLangNames = []string{ "en-US", "English", @@ -54,7 +56,7 @@ var ( Names []string ) -func loadI18nFrom(rootCfg ConfigProvider) { +func loadI18nFrom(rootCfg base.ConfigProvider) { Langs = rootCfg.Section("i18n").Key("LANGS").Strings(",") if len(Langs) == 0 { Langs = defaultI18nLangs() diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go index 75337a312ff6c..172657d15e286 100644 --- a/modules/setting/incoming_email.go +++ b/modules/setting/incoming_email.go @@ -9,6 +9,7 @@ import ( "strings" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) var IncomingEmail = struct { @@ -31,8 +32,8 @@ var IncomingEmail = struct { MaximumMessageSize: 10485760, } -func loadIncomingEmailFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "email.incoming", &IncomingEmail) +func loadIncomingEmailFrom(rootCfg base.ConfigProvider) { + base.MustMapSetting(rootCfg, "email.incoming", &IncomingEmail) if !IncomingEmail.Enabled { return diff --git a/modules/setting/indexer.go b/modules/setting/indexer.go index 5b10018eb7b21..10a7db5e8c6ca 100644 --- a/modules/setting/indexer.go +++ b/modules/setting/indexer.go @@ -9,6 +9,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" "github.com/gobwas/glob" ) @@ -45,7 +46,7 @@ var Indexer = struct { ExcludeVendored: true, } -func loadIndexerFrom(rootCfg ConfigProvider) { +func loadIndexerFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("indexer") Indexer.IssueType = sec.Key("ISSUE_INDEXER_TYPE").MustString("bleve") Indexer.IssuePath = filepath.ToSlash(sec.Key("ISSUE_INDEXER_PATH").MustString(filepath.ToSlash(filepath.Join(AppDataPath, "indexers/issues.bleve")))) @@ -55,15 +56,6 @@ func loadIndexerFrom(rootCfg ConfigProvider) { Indexer.IssueConnStr = sec.Key("ISSUE_INDEXER_CONN_STR").MustString(Indexer.IssueConnStr) Indexer.IssueIndexerName = sec.Key("ISSUE_INDEXER_NAME").MustString(Indexer.IssueIndexerName) - // The following settings are deprecated and can be overridden by settings in [queue] or [queue.issue_indexer] - // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version - // if these are removed, the warning will not be shown - deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE", "v1.19.0") - deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR", "v1.19.0") - deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR", "v1.19.0") - deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH", "v1.19.0") - deprecatedSetting(rootCfg, "indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH", "v1.19.0") - Indexer.RepoIndexerEnabled = sec.Key("REPO_INDEXER_ENABLED").MustBool(false) Indexer.RepoType = sec.Key("REPO_INDEXER_TYPE").MustString("bleve") Indexer.RepoPath = filepath.ToSlash(sec.Key("REPO_INDEXER_PATH").MustString(filepath.ToSlash(filepath.Join(AppDataPath, "indexers/repos.bleve")))) diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go index e04cde100b683..91bed26a73cd5 100644 --- a/modules/setting/lfs.go +++ b/modules/setting/lfs.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ini "gopkg.in/ini.v1" ) @@ -25,7 +26,7 @@ var LFS = struct { Storage }{} -func loadLFSFrom(rootCfg ConfigProvider) { +func loadLFSFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("server") if err := sec.MapTo(&LFS); err != nil { log.Fatal("Failed to map LFS settings: %v", err) @@ -34,12 +35,7 @@ func loadLFSFrom(rootCfg ConfigProvider) { lfsSec := rootCfg.Section("lfs") storageType := lfsSec.Key("STORAGE_TYPE").MustString("") - // Specifically default PATH to LFS_CONTENT_PATH - // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version - // if these are removed, the warning will not be shown - deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH", "v1.19.0") - lfsSec.Key("PATH").MustString( - sec.Key("LFS_CONTENT_PATH").String()) + lfsSec.Key("PATH").MustString("./data/lfs") LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec) diff --git a/modules/setting/log.go b/modules/setting/log.go index 5448650aadb9c..d81ce497f00a7 100644 --- a/modules/setting/log.go +++ b/modules/setting/log.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" "code.gitea.io/gitea/modules/util" ini "gopkg.in/ini.v1" @@ -140,7 +141,7 @@ func getStacktraceLogLevel(section *ini.Section, key, defaultValue string) strin return log.FromString(value).String() } -func loadLogFrom(rootCfg ConfigProvider) { +func loadLogFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("log") Log.Level = getLogLevel(sec, "LEVEL", log.INFO) Log.StacktraceLogLevel = getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", "None") @@ -251,7 +252,7 @@ func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions return mode, jsonConfig, levelName } -func generateNamedLogger(rootCfg ConfigProvider, key string, options defaultLogOptions) *LogDescription { +func generateNamedLogger(rootCfg base.ConfigProvider, key string, options defaultLogOptions) *LogDescription { description := LogDescription{ Name: key, } @@ -292,7 +293,7 @@ func generateNamedLogger(rootCfg ConfigProvider, key string, options defaultLogO } // initLogFrom initializes logging with settings from configuration provider -func initLogFrom(rootCfg ConfigProvider) { +func initLogFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("log") options := newDefaultLogOptions() options.bufferLength = Log.BufferLength @@ -380,7 +381,7 @@ func InitSQLLog(disableConsole bool) { initSQLLogFrom(CfgProvider, disableConsole) } -func initSQLLogFrom(rootCfg ConfigProvider, disableConsole bool) { +func initSQLLogFrom(rootCfg base.ConfigProvider, disableConsole bool) { if Log.EnableXORMLog { options := newDefaultLogOptions() options.filename = filepath.Join(Log.RootPath, "xorm.log") diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 39afce7d46455..f2e49010a0064 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -10,6 +10,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" shellquote "github.com/kballard/go-shellquote" ) @@ -49,85 +50,21 @@ type Mailer struct { // MailService the global mailer var MailService *Mailer -func loadMailsFrom(rootCfg ConfigProvider) { +func loadMailsFrom(rootCfg base.ConfigProvider) { loadMailerFrom(rootCfg) loadRegisterMailFrom(rootCfg) loadNotifyMailFrom(rootCfg) loadIncomingEmailFrom(rootCfg) } -func loadMailerFrom(rootCfg ConfigProvider) { +func loadMailerFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("mailer") // Check mailer setting. if !sec.Key("ENABLED").MustBool() { return } - // Handle Deprecations and map on to new configuration - // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version - // if these are removed, the warning will not be shown - deprecatedSetting(rootCfg, "mailer", "MAILER_TYPE", "mailer", "PROTOCOL", "v1.19.0") - if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") { - if sec.Key("MAILER_TYPE").String() == "sendmail" { - sec.Key("PROTOCOL").MustString("sendmail") - } - } - - deprecatedSetting(rootCfg, "mailer", "HOST", "mailer", "SMTP_ADDR", "v1.19.0") - if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") { - givenHost := sec.Key("HOST").String() - addr, port, err := net.SplitHostPort(givenHost) - if err != nil && strings.Contains(err.Error(), "missing port in address") { - addr = givenHost - } else if err != nil { - log.Fatal("Invalid mailer.HOST (%s): %v", givenHost, err) - } - if addr == "" { - addr = "127.0.0.1" - } - sec.Key("SMTP_ADDR").MustString(addr) - sec.Key("SMTP_PORT").MustString(port) - } - - deprecatedSetting(rootCfg, "mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL", "v1.19.0") - if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") { - if sec.Key("IS_TLS_ENABLED").MustBool() { - sec.Key("PROTOCOL").MustString("smtps") - } else { - sec.Key("PROTOCOL").MustString("smtp+starttls") - } - } - - deprecatedSetting(rootCfg, "mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO", "v1.19.0") - if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") { - sec.Key("ENABLE_HELO").MustBool(!sec.Key("DISABLE_HELO").MustBool()) - } - - deprecatedSetting(rootCfg, "mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT", "v1.19.0") - if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") { - sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(sec.Key("SKIP_VERIFY").MustBool()) - } - - deprecatedSetting(rootCfg, "mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT", "v1.19.0") - if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") { - sec.Key("USE_CLIENT_CERT").MustBool(sec.Key("USE_CERTIFICATE").MustBool()) - } - - deprecatedSetting(rootCfg, "mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE", "v1.19.0") - if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") { - sec.Key("CERT_FILE").MustString(sec.Key("CERT_FILE").String()) - } - - deprecatedSetting(rootCfg, "mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE", "v1.19.0") - if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") { - sec.Key("KEY_FILE").MustString(sec.Key("KEY_FILE").String()) - } - - deprecatedSetting(rootCfg, "mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT", "v1.19.0") - if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") { - sec.Key("SEND_AS_PLAIN_TEXT").MustBool(!sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)) - } - + // TODO: Remove before 1.19.0 if sec.HasKey("PROTOCOL") && sec.Key("PROTOCOL").String() == "smtp+startls" { log.Error("Deprecated fallback `[mailer]` `PROTOCOL = smtp+startls` present. Use `[mailer]` `PROTOCOL = smtp+starttls`` instead. This fallback will be removed in v1.19.0") sec.Key("PROTOCOL").SetValue("smtp+starttls") @@ -136,12 +73,17 @@ func loadMailerFrom(rootCfg ConfigProvider) { // Set default values & validate sec.Key("NAME").MustString(AppName) sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+starttls", "smtp+unix", "sendmail", "dummy"}) + sec.Key("SMTP_ADDR").MustString("") + sec.Key("SMTP_PORT").MustString("") sec.Key("ENABLE_HELO").MustBool(true) sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(false) sec.Key("USE_CLIENT_CERT").MustBool(false) + sec.Key("CLIENT_CERT_FILE").MustString("custom/mailer/cert.pem") + sec.Key("CLIENT_KEY_FILE").MustString("custom/mailer/key.pem") sec.Key("SENDMAIL_PATH").MustString("sendmail") sec.Key("SENDMAIL_TIMEOUT").MustDuration(5 * time.Minute) sec.Key("SENDMAIL_CONVERT_CRLF").MustBool(true) + sec.Key("SEND_AS_PLAIN_TEXT").MustBool(false) sec.Key("FROM").MustString(sec.Key("USER").String()) // Now map the values on to the MailService @@ -236,7 +178,7 @@ func loadMailerFrom(rootCfg ConfigProvider) { log.Info("Mail Service Enabled") } -func loadRegisterMailFrom(rootCfg ConfigProvider) { +func loadRegisterMailFrom(rootCfg base.ConfigProvider) { if !rootCfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() { return } else if MailService == nil { @@ -247,7 +189,7 @@ func loadRegisterMailFrom(rootCfg ConfigProvider) { log.Info("Register Mail Service Enabled") } -func loadNotifyMailFrom(rootCfg ConfigProvider) { +func loadNotifyMailFrom(rootCfg base.ConfigProvider) { if !rootCfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() { return } else if MailService == nil { diff --git a/modules/setting/markup.go b/modules/setting/markup.go index b71a902be65f5..311bc3d2c5980 100644 --- a/modules/setting/markup.go +++ b/modules/setting/markup.go @@ -8,6 +8,7 @@ import ( "strings" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" "gopkg.in/ini.v1" ) @@ -60,8 +61,8 @@ type MarkupSanitizerRule struct { AllowDataURIImages bool } -func loadMarkupFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "markdown", &Markdown) +func loadMarkupFrom(rootCfg base.ConfigProvider) { + base.MustMapSetting(rootCfg, "markdown", &Markdown) MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10) diff --git a/modules/setting/metrics.go b/modules/setting/metrics.go index daa0e3b70b2d1..4e26e7435e448 100644 --- a/modules/setting/metrics.go +++ b/modules/setting/metrics.go @@ -3,6 +3,8 @@ package setting +import "code.gitea.io/gitea/modules/setting/base" + // Metrics settings var Metrics = struct { Enabled bool @@ -16,6 +18,6 @@ var Metrics = struct { EnabledIssueByRepository: false, } -func loadMetricsFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "metrics", &Metrics) +func loadMetricsFrom(rootCfg base.ConfigProvider) { + base.MustMapSetting(rootCfg, "metrics", &Metrics) } diff --git a/modules/setting/migrations.go b/modules/setting/migrations.go index 5a6079b6e2db0..f57b2b42149a0 100644 --- a/modules/setting/migrations.go +++ b/modules/setting/migrations.go @@ -3,6 +3,8 @@ package setting +import "code.gitea.io/gitea/modules/setting/base" + // Migrations settings var Migrations = struct { MaxAttempts int @@ -16,7 +18,7 @@ var Migrations = struct { RetryBackoff: 3, } -func loadMigrationsFrom(rootCfg ConfigProvider) { +func loadMigrationsFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("migrations") Migrations.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Migrations.MaxAttempts) Migrations.RetryBackoff = sec.Key("RETRY_BACKOFF").MustInt(Migrations.RetryBackoff) diff --git a/modules/setting/mime_type_map.go b/modules/setting/mime_type_map.go index 55cb2c028dbf3..9fb9d15f353da 100644 --- a/modules/setting/mime_type_map.go +++ b/modules/setting/mime_type_map.go @@ -3,7 +3,11 @@ package setting -import "strings" +import ( + "strings" + + "code.gitea.io/gitea/modules/setting/base" +) // MimeTypeMap defines custom mime type mapping settings var MimeTypeMap = struct { @@ -14,7 +18,7 @@ var MimeTypeMap = struct { Map: map[string]string{}, } -func loadMimeTypeMapFrom(rootCfg ConfigProvider) { +func loadMimeTypeMapFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("repository.mimetype_mapping") keys := sec.Keys() m := make(map[string]string, len(keys)) diff --git a/modules/setting/mirror.go b/modules/setting/mirror.go index cd6b8d456248d..8382296acf69a 100644 --- a/modules/setting/mirror.go +++ b/modules/setting/mirror.go @@ -7,6 +7,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) // Mirror settings @@ -24,16 +25,7 @@ var Mirror = struct { DefaultInterval: 8 * time.Hour, } -func loadMirrorFrom(rootCfg ConfigProvider) { - // Handle old configuration through `[repository]` `DISABLE_MIRRORS` - // - please note this was badly named and only disabled the creation of new pull mirrors - // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version - // if these are removed, the warning will not be shown - deprecatedSetting(rootCfg, "repository", "DISABLE_MIRRORS", "mirror", "ENABLED", "v1.19.0") - if rootCfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) { - Mirror.DisableNewPull = true - } - +func loadMirrorFrom(rootCfg base.ConfigProvider) { if err := rootCfg.Section("mirror").MapTo(&Mirror); err != nil { log.Fatal("Failed to map Mirror settings: %v", err) } diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go index 44f5568ef4b19..70aaa1b760aa2 100644 --- a/modules/setting/oauth2.go +++ b/modules/setting/oauth2.go @@ -8,6 +8,7 @@ import ( "path/filepath" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" "gopkg.in/ini.v1" ) @@ -62,7 +63,7 @@ var OAuth2Client struct { AccountLinking OAuth2AccountLinkingType } -func loadOAuth2ClientFrom(rootCfg ConfigProvider) { +func loadOAuth2ClientFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("oauth2_client") OAuth2Client.RegisterEmailConfirm = sec.Key("REGISTER_EMAIL_CONFIRM").MustBool(Service.RegisterEmailConfirm) OAuth2Client.OpenIDConnectScopes = parseScopes(sec, "OPENID_CONNECT_SCOPES") @@ -110,7 +111,7 @@ var OAuth2 = struct { MaxTokenLength: math.MaxInt16, } -func loadOAuth2From(rootCfg ConfigProvider) { +func loadOAuth2From(rootCfg base.ConfigProvider) { if err := rootCfg.Section("oauth2").MapTo(&OAuth2); err != nil { log.Fatal("Failed to OAuth2 settings: %v", err) return diff --git a/modules/setting/other.go b/modules/setting/other.go index 4fba754a08487..9689bea280512 100644 --- a/modules/setting/other.go +++ b/modules/setting/other.go @@ -3,6 +3,8 @@ package setting +import "code.gitea.io/gitea/modules/setting/base" + var ( // Other settings ShowFooterBranding bool @@ -12,7 +14,7 @@ var ( EnableSitemap bool ) -func loadOtherFrom(rootCfg ConfigProvider) { +func loadOtherFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("other") ShowFooterBranding = sec.Key("SHOW_FOOTER_BRANDING").MustBool(false) ShowFooterVersion = sec.Key("SHOW_FOOTER_VERSION").MustBool(true) diff --git a/modules/setting/packages.go b/modules/setting/packages.go index 13599e5a63e73..6bd0cff3f2fcc 100644 --- a/modules/setting/packages.go +++ b/modules/setting/packages.go @@ -10,6 +10,7 @@ import ( "path/filepath" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" "github.com/dustin/go-humanize" ini "gopkg.in/ini.v1" @@ -46,7 +47,7 @@ var ( } ) -func loadPackagesFrom(rootCfg ConfigProvider) { +func loadPackagesFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("packages") if err := sec.MapTo(&Packages); err != nil { log.Fatal("Failed to map Packages settings: %v", err) diff --git a/modules/setting/picture.go b/modules/setting/picture.go index 6d7c8b33ce46f..f57c685768e8b 100644 --- a/modules/setting/picture.go +++ b/modules/setting/picture.go @@ -3,6 +3,8 @@ package setting +import "code.gitea.io/gitea/modules/setting/base" + // settings var ( // Picture settings @@ -32,7 +34,7 @@ var ( }{} ) -func loadPictureFrom(rootCfg ConfigProvider) { +func loadPictureFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("picture") avatarSec := rootCfg.Section("avatar") @@ -60,9 +62,7 @@ func loadPictureFrom(rootCfg ConfigProvider) { } DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar()) - deprecatedSettingDB(rootCfg, "", "DISABLE_GRAVATAR") EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar)) - deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR") loadRepoAvatarFrom(rootCfg) } @@ -82,7 +82,7 @@ func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool { return v } -func loadRepoAvatarFrom(rootCfg ConfigProvider) { +func loadRepoAvatarFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("picture") repoAvatarSec := rootCfg.Section("repo-avatar") diff --git a/modules/setting/project.go b/modules/setting/project.go index 803e933b887e4..b4626b5eb0d38 100644 --- a/modules/setting/project.go +++ b/modules/setting/project.go @@ -3,6 +3,8 @@ package setting +import "code.gitea.io/gitea/modules/setting/base" + // Project settings var ( Project = struct { @@ -14,6 +16,6 @@ var ( } ) -func loadProjectFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "project", &Project) +func loadProjectFrom(rootCfg base.ConfigProvider) { + base.MustMapSetting(rootCfg, "project", &Project) } diff --git a/modules/setting/proxy.go b/modules/setting/proxy.go index 4ff420d0900db..e69bdb49273b1 100644 --- a/modules/setting/proxy.go +++ b/modules/setting/proxy.go @@ -7,6 +7,7 @@ import ( "net/url" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) // Proxy settings @@ -21,7 +22,7 @@ var Proxy = struct { ProxyHosts: []string{}, } -func loadProxyFrom(rootCfg ConfigProvider) { +func loadProxyFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("proxy") Proxy.Enabled = sec.Key("PROXY_ENABLED").MustBool(false) Proxy.ProxyURL = sec.Key("PROXY_URL").MustString("") diff --git a/modules/setting/queue.go b/modules/setting/queue.go index bd4bf48e33095..e2aaf611ca8dc 100644 --- a/modules/setting/queue.go +++ b/modules/setting/queue.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ini "gopkg.in/ini.v1" ) @@ -42,7 +43,7 @@ func GetQueueSettings(name string) QueueSettings { return getQueueSettings(CfgProvider, name) } -func getQueueSettings(rootCfg ConfigProvider, name string) QueueSettings { +func getQueueSettings(rootCfg base.ConfigProvider, name string) QueueSettings { q := QueueSettings{} sec := rootCfg.Section("queue." + name) q.Name = name @@ -92,7 +93,7 @@ func LoadQueueSettings() { loadQueueFrom(CfgProvider) } -func loadQueueFrom(rootCfg ConfigProvider) { +func loadQueueFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("queue") Queue.DataDir = filepath.ToSlash(sec.Key("DATADIR").MustString("queues/")) if !filepath.IsAbs(Queue.DataDir) { @@ -174,7 +175,7 @@ func loadQueueFrom(rootCfg ConfigProvider) { // handleOldLengthConfiguration allows fallback to older configuration. `[queue.name]` `LENGTH` will override this configuration, but // if that is left unset then we should fallback to the older configuration. (Except where the new length woul be <=0) -func handleOldLengthConfiguration(rootCfg ConfigProvider, queueName, oldSection, oldKey string, defaultValue int) { +func handleOldLengthConfiguration(rootCfg base.ConfigProvider, queueName, oldSection, oldKey string, defaultValue int) { if rootCfg.Section(oldSection).HasKey(oldKey) { log.Error("Deprecated fallback for %s queue length `[%s]` `%s` present. Use `[queue.%s]` `LENGTH`. This will be removed in v1.18.0", queueName, queueName, oldSection, oldKey) } diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 4964704dbad12..500f94617771b 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -10,6 +10,7 @@ import ( "strings" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) // enumerates all the policy repository creating @@ -270,7 +271,7 @@ var ( }{} ) -func loadRepositoryFrom(rootCfg ConfigProvider) { +func loadRepositoryFrom(rootCfg base.ConfigProvider) { var err error // Determine and create root git repository path. sec := rootCfg.Section("repository") diff --git a/modules/setting/security.go b/modules/setting/security.go index b9841cdb95a53..481089b204c64 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ini "gopkg.in/ini.v1" ) @@ -96,7 +97,7 @@ func generateSaveInternalToken() { }) } -func loadSecurityFrom(rootCfg ConfigProvider) { +func loadSecurityFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("security") InstallLock = sec.Key("INSTALL_LOCK").MustBool(false) LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7) diff --git a/modules/setting/server.go b/modules/setting/server.go index 183906268576c..19c2994fb9b67 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" "code.gitea.io/gitea/modules/util" ) @@ -165,7 +166,7 @@ func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string { return strings.TrimSuffix(staticURLPrefix, "/") } -func loadServerFrom(rootCfg ConfigProvider) { +func loadServerFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("server") AppName = rootCfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea") @@ -179,41 +180,17 @@ func loadServerFrom(rootCfg ConfigProvider) { case "https": Protocol = HTTPS - // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version - // if these are removed, the warning will not be shown - if sec.HasKey("ENABLE_ACME") { - EnableAcme = sec.Key("ENABLE_ACME").MustBool(false) - } else { - deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME", "v1.19.0") - EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) - } + EnableAcme = sec.Key("ENABLE_ACME").MustBool(false) if EnableAcme { AcmeURL = sec.Key("ACME_URL").MustString("") AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("") + AcmeLiveDirectory = sec.Key("ACME_DIRECTORY").MustString("https") + AcmeEmail = sec.Key("ACME_EMAIL").MustString("") - if sec.HasKey("ACME_ACCEPTTOS") { - AcmeTOS = sec.Key("ACME_ACCEPTTOS").MustBool(false) - } else { - deprecatedSetting(rootCfg, "server", "LETSENCRYPT_ACCEPTTOS", "server", "ACME_ACCEPTTOS", "v1.19.0") - AcmeTOS = sec.Key("LETSENCRYPT_ACCEPTTOS").MustBool(false) - } + AcmeTOS = sec.Key("ACME_ACCEPTTOS").MustBool(false) if !AcmeTOS { log.Fatal("ACME TOS is not accepted (ACME_ACCEPTTOS).") } - - if sec.HasKey("ACME_DIRECTORY") { - AcmeLiveDirectory = sec.Key("ACME_DIRECTORY").MustString("https") - } else { - deprecatedSetting(rootCfg, "server", "LETSENCRYPT_DIRECTORY", "server", "ACME_DIRECTORY", "v1.19.0") - AcmeLiveDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https") - } - - if sec.HasKey("ACME_EMAIL") { - AcmeEmail = sec.Key("ACME_EMAIL").MustString("") - } else { - deprecatedSetting(rootCfg, "server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL", "v1.19.0") - AcmeEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("") - } } else { CertFile = sec.Key("CERT_FILE").String() KeyFile = sec.Key("KEY_FILE").String() diff --git a/modules/setting/service.go b/modules/setting/service.go index d4a31ba5d4772..c7bd5c622aa01 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -9,6 +9,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" "code.gitea.io/gitea/modules/structs" ) @@ -114,7 +115,7 @@ func (a AllowedVisibility) ToVisibleTypeSlice() (result []structs.VisibleType) { return result } -func loadServiceFrom(rootCfg ConfigProvider) { +func loadServiceFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("service") Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180) Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180) @@ -193,12 +194,12 @@ func loadServiceFrom(rootCfg ConfigProvider) { } Service.ValidSiteURLSchemes = schemes - mustMapSetting(rootCfg, "service.explore", &Service.Explore) + base.MustMapSetting(rootCfg, "service.explore", &Service.Explore) loadOpenIDSetting(rootCfg) } -func loadOpenIDSetting(rootCfg ConfigProvider) { +func loadOpenIDSetting(rootCfg base.ConfigProvider) { sec := rootCfg.Section("openid") Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock) Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn) diff --git a/modules/setting/session.go b/modules/setting/session.go index b8498335d9867..0e6fae2ded858 100644 --- a/modules/setting/session.go +++ b/modules/setting/session.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) // SessionConfig defines Session settings @@ -40,7 +41,7 @@ var SessionConfig = struct { SameSite: http.SameSiteLaxMode, } -func loadSessionFrom(rootCfg ConfigProvider) { +func loadSessionFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("session") SessionConfig.Provider = sec.Key("PROVIDER").In("memory", []string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache", "db"}) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 87b1e2797fe7d..c195e31c27681 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" "code.gitea.io/gitea/modules/user" "code.gitea.io/gitea/modules/util" @@ -42,7 +43,7 @@ var ( AppWorkPath string // Global setting objects - CfgProvider ConfigProvider + CfgProvider base.ConfigProvider CustomPath string // Custom directory path CustomConf string PIDFile = "/run/gitea.pid" @@ -276,7 +277,7 @@ func LoadCommonSettings() { } // loadCommonSettingsFrom loads common configurations from a configuration provider. -func loadCommonSettingsFrom(cfg ConfigProvider) { +func loadCommonSettingsFrom(cfg base.ConfigProvider) { // WARNNING: don't change the sequence except you know what you are doing. loadRunModeFrom(cfg) loadLogFrom(cfg) @@ -303,7 +304,7 @@ func loadCommonSettingsFrom(cfg ConfigProvider) { loadOtherFrom(cfg) } -func loadRunModeFrom(rootCfg ConfigProvider) { +func loadRunModeFrom(rootCfg base.ConfigProvider) { rootSec := rootCfg.Section("") RunUser = rootSec.Key("RUN_USER").MustString(user.CurrentUsername()) // The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches. diff --git a/modules/setting/ssh.go b/modules/setting/ssh.go index e8796f98d6f14..f289cbf2d93c5 100644 --- a/modules/setting/ssh.go +++ b/modules/setting/ssh.go @@ -12,6 +12,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" "code.gitea.io/gitea/modules/util" gossh "golang.org/x/crypto/ssh" @@ -99,7 +100,7 @@ func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) { return authorizedPrincipalsAllow, true } -func loadSSHFrom(rootCfg ConfigProvider) { +func loadSSHFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("server") if len(SSH.Domain) == 0 { SSH.Domain = Domain diff --git a/modules/setting/storage.go b/modules/setting/storage.go index 4d401614e4929..727a1af939601 100644 --- a/modules/setting/storage.go +++ b/modules/setting/storage.go @@ -7,6 +7,8 @@ import ( "path/filepath" "reflect" + "code.gitea.io/gitea/modules/setting/base" + ini "gopkg.in/ini.v1" ) @@ -30,7 +32,7 @@ func (s *Storage) MapTo(v interface{}) error { return nil } -func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section) Storage { +func getStorage(rootCfg base.ConfigProvider, name, typ string, targetSec *ini.Section) Storage { const sectionName = "storage" sec := rootCfg.Section(sectionName) diff --git a/modules/setting/task.go b/modules/setting/task.go index f75b4f14813f8..d618e046d6fef 100644 --- a/modules/setting/task.go +++ b/modules/setting/task.go @@ -3,24 +3,11 @@ package setting -// DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version -// if these are removed, the warning will not be shown -// - will need to set default for [queue.task] LENGTH to 1000 though -func loadTaskFrom(rootCfg ConfigProvider) { - taskSec := rootCfg.Section("task") - queueTaskSec := rootCfg.Section("queue.task") +import "code.gitea.io/gitea/modules/setting/base" - deprecatedSetting(rootCfg, "task", "QUEUE_TYPE", "queue.task", "TYPE", "v1.19.0") - deprecatedSetting(rootCfg, "task", "QUEUE_CONN_STR", "queue.task", "CONN_STR", "v1.19.0") - deprecatedSetting(rootCfg, "task", "QUEUE_LENGTH", "queue.task", "LENGTH", "v1.19.0") - - switch taskSec.Key("QUEUE_TYPE").MustString("channel") { - case "channel": - queueTaskSec.Key("TYPE").MustString("persistable-channel") - queueTaskSec.Key("CONN_STR").MustString(taskSec.Key("QUEUE_CONN_STR").MustString("")) - case "redis": - queueTaskSec.Key("TYPE").MustString("redis") - queueTaskSec.Key("CONN_STR").MustString(taskSec.Key("QUEUE_CONN_STR").MustString("addrs=127.0.0.1:6379 db=0")) - } - queueTaskSec.Key("LENGTH").MustInt(taskSec.Key("QUEUE_LENGTH").MustInt(1000)) +func loadTaskFrom(rootCfg base.ConfigProvider) { + sec := rootCfg.Section("queue.task") + sec.Key("TYPE").MustString("channel") + sec.Key("CONN_STR").MustString("redis://127.0.0.1:6379/0") + sec.Key("LENGTH").MustInt(1000) } diff --git a/modules/setting/time.go b/modules/setting/time.go index 5fd0fdb92f47b..10c01b67b831f 100644 --- a/modules/setting/time.go +++ b/modules/setting/time.go @@ -7,6 +7,7 @@ import ( "time" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) var ( @@ -16,7 +17,7 @@ var ( DefaultUILocation = time.Local ) -func loadTimeFrom(rootCfg ConfigProvider) { +func loadTimeFrom(rootCfg base.ConfigProvider) { timeFormatKey := rootCfg.Section("time").Key("FORMAT").MustString("") if timeFormatKey != "" { TimeFormat = map[string]string{ diff --git a/modules/setting/ui.go b/modules/setting/ui.go index 2df3c35c76ee3..7dded0e0d3abf 100644 --- a/modules/setting/ui.go +++ b/modules/setting/ui.go @@ -7,6 +7,7 @@ import ( "time" "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/setting/base" ) // UI settings @@ -132,8 +133,8 @@ var UI = struct { }, } -func loadUIFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "ui", &UI) +func loadUIFrom(rootCfg base.ConfigProvider) { + base.MustMapSetting(rootCfg, "ui", &UI) sec := rootCfg.Section("ui") UI.ShowUserEmail = sec.Key("SHOW_USER_EMAIL").MustBool(true) UI.DefaultShowFullName = sec.Key("DEFAULT_SHOW_FULL_NAME").MustBool(false) diff --git a/modules/setting/webhook.go b/modules/setting/webhook.go index c01261dbbd6e9..32061ab98ff7b 100644 --- a/modules/setting/webhook.go +++ b/modules/setting/webhook.go @@ -7,6 +7,7 @@ import ( "net/url" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting/base" ) // Webhook settings @@ -29,7 +30,7 @@ var Webhook = struct { ProxyHosts: []string{}, } -func loadWebhookFrom(rootCfg ConfigProvider) { +func loadWebhookFrom(rootCfg base.ConfigProvider) { sec := rootCfg.Section("webhook") Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000) Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)