aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2021-12-01 15:50:01 +0800
committerGitHub <noreply@github.com>2021-12-01 15:50:01 +0800
commit042cac5fedeec8af53080b9666fe043072f3a6be (patch)
treeb13d57faa71ba8bc9f8b3d40f5be7e3735ac66a4 /modules
parenta3517d8668482b58cb80ba10a956fe4e27e1a429 (diff)
downloadgitea-042cac5fedeec8af53080b9666fe043072f3a6be.tar.gz
gitea-042cac5fedeec8af53080b9666fe043072f3a6be.zip
Improve install code to avoid low-level mistakes. (#17779)
* Improve install code to avoid low-level mistakes. If a user tries to do a re-install in a Gitea database, they gets a warning and double check. When Gitea runs, it never create empty app.ini automatically. Also some small (related) refactoring: * Refactor db.InitEngine related logic make it more clean (especially for the install code) * Move some i18n strings out from setting.go to make the setting.go can be easily maintained. * Show errors in CLI code if an incorrect app.ini is used. * APP_DATA_PATH is created when installing, and checked when starting (no empty directory is created any more).
Diffstat (limited to 'modules')
-rw-r--r--modules/doctor/doctor.go2
-rw-r--r--modules/doctor/paths.go2
-rw-r--r--modules/private/internal.go11
-rw-r--r--modules/setting/directory.go40
-rw-r--r--modules/setting/i18n.go51
-rw-r--r--modules/setting/setting.go88
6 files changed, 144 insertions, 50 deletions
diff --git a/modules/doctor/doctor.go b/modules/doctor/doctor.go
index 6451788f9d..20a32f1865 100644
--- a/modules/doctor/doctor.go
+++ b/modules/doctor/doctor.go
@@ -44,7 +44,7 @@ func (w *wrappedLevelLogger) Log(skip int, level log.Level, format string, v ...
}
func initDBDisableConsole(ctx context.Context, disableConsole bool) error {
- setting.NewContext()
+ setting.LoadFromExisting()
setting.InitDBConfig()
setting.NewXORMLogService(disableConsole)
diff --git a/modules/doctor/paths.go b/modules/doctor/paths.go
index 88172d3150..b4eab631ba 100644
--- a/modules/doctor/paths.go
+++ b/modules/doctor/paths.go
@@ -67,7 +67,7 @@ func checkConfigurationFiles(logger log.Logger, autofix bool) error {
return err
}
- setting.NewContext()
+ setting.LoadFromExisting()
configurationFiles := []configurationFile{
{"Configuration File Path", setting.CustomConf, false, true, false},
diff --git a/modules/private/internal.go b/modules/private/internal.go
index f5b5db0ca1..0a39ca7b8e 100644
--- a/modules/private/internal.go
+++ b/modules/private/internal.go
@@ -13,14 +13,18 @@ import (
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
func newRequest(ctx context.Context, url, method string) *httplib.Request {
+ if setting.InternalToken == "" {
+ log.Fatal(`The INTERNAL_TOKEN setting is missing from the configuration file: %q.
+Ensure you are running in the correct environment or set the correct configuration file with -c.`, setting.CustomConf)
+ }
return httplib.NewRequest(url, method).
SetContext(ctx).
- Header("Authorization",
- fmt.Sprintf("Bearer %s", setting.InternalToken))
+ Header("Authorization", fmt.Sprintf("Bearer %s", setting.InternalToken))
}
// Response internal request response
@@ -44,9 +48,6 @@ func newInternalRequest(ctx context.Context, url, method string) *httplib.Reques
})
if setting.Protocol == setting.UnixSocket {
req.SetTransport(&http.Transport{
- Dial: func(_, _ string) (net.Conn, error) {
- return net.Dial("unix", setting.HTTPAddr)
- },
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, "unix", setting.HTTPAddr)
diff --git a/modules/setting/directory.go b/modules/setting/directory.go
new file mode 100644
index 0000000000..5dcdd89c04
--- /dev/null
+++ b/modules/setting/directory.go
@@ -0,0 +1,40 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package setting
+
+import (
+ "fmt"
+ "os"
+)
+
+// PrepareAppDataPath creates app data directory if necessary
+func PrepareAppDataPath() error {
+ // FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
+ // For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs,
+ // then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem.
+ // The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories.
+ // For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK).
+ // Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future.
+
+ st, err := os.Stat(AppDataPath)
+
+ if os.IsNotExist(err) {
+ err = os.MkdirAll(AppDataPath, os.ModePerm)
+ if err != nil {
+ return fmt.Errorf("unable to create the APP_DATA_PATH directory: %q, Error: %v", AppDataPath, err)
+ }
+ return nil
+ }
+
+ if err != nil {
+ return fmt.Errorf("unable to use APP_DATA_PATH %q. Error: %v", AppDataPath, err)
+ }
+
+ if !st.IsDir() /* also works for symlink */ {
+ return fmt.Errorf("the APP_DATA_PATH %q is not a directory (or symlink to a directory) and can't be used", AppDataPath)
+ }
+
+ return nil
+}
diff --git a/modules/setting/i18n.go b/modules/setting/i18n.go
new file mode 100644
index 0000000000..113e1b5585
--- /dev/null
+++ b/modules/setting/i18n.go
@@ -0,0 +1,51 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package setting
+
+// defaultI18nLangNames must be a slice, we need the order
+var defaultI18nLangNames = []string{
+ "en-US", "English",
+ "zh-CN", "简体中文",
+ "zh-HK", "繁體中文(香港)",
+ "zh-TW", "繁體中文(台灣)",
+ "de-DE", "Deutsch",
+ "fr-FR", "français",
+ "nl-NL", "Nederlands",
+ "lv-LV", "latviešu",
+ "ru-RU", "русский",
+ "uk-UA", "Українська",
+ "ja-JP", "日本語",
+ "es-ES", "español",
+ "pt-BR", "português do Brasil",
+ "pt-PT", "Português de Portugal",
+ "pl-PL", "polski",
+ "bg-BG", "български",
+ "it-IT", "italiano",
+ "fi-FI", "suomi",
+ "tr-TR", "Türkçe",
+ "cs-CZ", "čeština",
+ "sr-SP", "српски",
+ "sv-SE", "svenska",
+ "ko-KR", "한국어",
+ "el-GR", "ελληνικά",
+ "fa-IR", "فارسی",
+ "hu-HU", "magyar nyelv",
+ "id-ID", "bahasa Indonesia",
+ "ml-IN", "മലയാളം",
+}
+
+func defaultI18nLangs() (res []string) {
+ for i := 0; i < len(defaultI18nLangNames); i += 2 {
+ res = append(res, defaultI18nLangNames[i])
+ }
+ return
+}
+
+func defaultI18nNames() (res []string) {
+ for i := 0; i < len(defaultI18nLangNames); i += 2 {
+ res = append(res, defaultI18nLangNames[i+1])
+ }
+ return
+}
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 15cdc1fe3a..d219dbaafd 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -546,9 +546,27 @@ func SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath string)
}
}
-// NewContext initializes configuration context.
+// LoadFromExisting initializes setting options from an existing config file (app.ini)
+func LoadFromExisting() {
+ loadFromConf(false)
+}
+
+// LoadAllowEmpty initializes setting options, it's also fine that if the config file (app.ini) doesn't exist
+func LoadAllowEmpty() {
+ loadFromConf(true)
+}
+
+// LoadForTest initializes setting options for tests
+func LoadForTest() {
+ loadFromConf(true)
+ if err := PrepareAppDataPath(); err != nil {
+ log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
+ }
+}
+
+// loadFromConf initializes configuration context.
// NOTE: do not print any log except error.
-func NewContext() {
+func loadFromConf(allowEmpty bool) {
Cfg = ini.Empty()
if WritePIDFile && len(PIDFile) > 0 {
@@ -563,9 +581,10 @@ func NewContext() {
if err := Cfg.Append(CustomConf); err != nil {
log.Fatal("Failed to load custom conf '%s': %v", CustomConf, err)
}
- } else {
- log.Warn("Custom config '%s' not found, ignore this if you're running first time", CustomConf)
- }
+ } else if !allowEmpty {
+ log.Fatal("Unable to find configuration file: %q.\nEnsure you are running in the correct environment or set the correct configuration file with -c.", CustomConf)
+ } // else: no config file, a config file might be created at CustomConf later (might not)
+
Cfg.NameMapper = ini.SnackCase
homeDir, err := com.HomeDir()
@@ -698,18 +717,7 @@ func NewContext() {
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath)
StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
- if _, err = os.Stat(AppDataPath); err != nil {
- // FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
- // For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs,
- // then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem.
- // The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories.
- // For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK).
- // Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future.
- err = os.MkdirAll(AppDataPath, os.ModePerm)
- if err != nil {
- log.Fatal("Failed to create the directory for app data path '%s'", AppDataPath)
- }
- }
+
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(path.Join(AppWorkPath, "data/tmp/pprof"))
@@ -864,6 +872,10 @@ func NewContext() {
SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
InternalToken = loadInternalToken(sec)
+ if InstallLock && InternalToken == "" {
+ // if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
+ generateSaveInternalToken()
+ }
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
if len(cfgdata) == 0 {
@@ -975,19 +987,11 @@ func NewContext() {
Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
if len(Langs) == 0 {
- Langs = []string{
- "en-US", "zh-CN", "zh-HK", "zh-TW", "de-DE", "fr-FR", "nl-NL", "lv-LV",
- "ru-RU", "uk-UA", "ja-JP", "es-ES", "pt-BR", "pt-PT", "pl-PL", "bg-BG",
- "it-IT", "fi-FI", "tr-TR", "cs-CZ", "sr-SP", "sv-SE", "ko-KR", "el-GR",
- "fa-IR", "hu-HU", "id-ID", "ml-IN"}
+ Langs = defaultI18nLangs()
}
Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
if len(Names) == 0 {
- Names = []string{"English", "简体中文", "繁體中文(香港)", "繁體中文(台灣)", "Deutsch",
- "français", "Nederlands", "latviešu", "русский", "Українська", "日本語",
- "español", "português do Brasil", "Português de Portugal", "polski", "български",
- "italiano", "suomi", "Türkçe", "čeština", "српски", "svenska", "한국어", "ελληνικά",
- "فارسی", "magyar nyelv", "bahasa Indonesia", "മലയാളം"}
+ Names = defaultI18nNames()
}
ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool(false)
@@ -1054,8 +1058,8 @@ func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) {
func loadInternalToken(sec *ini.Section) string {
uri := sec.Key("INTERNAL_TOKEN_URI").String()
- if len(uri) == 0 {
- return loadOrGenerateInternalToken(sec)
+ if uri == "" {
+ return sec.Key("INTERNAL_TOKEN").String()
}
tempURI, err := url.Parse(uri)
if err != nil {
@@ -1092,21 +1096,17 @@ func loadInternalToken(sec *ini.Section) string {
return ""
}
-func loadOrGenerateInternalToken(sec *ini.Section) string {
- var err error
- token := sec.Key("INTERNAL_TOKEN").String()
- if len(token) == 0 {
- token, err = generate.NewInternalToken()
- if err != nil {
- log.Fatal("Error generate internal token: %v", err)
- }
-
- // Save secret
- CreateOrAppendToCustomConf(func(cfg *ini.File) {
- cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
- })
+// generateSaveInternalToken generates and saves the internal token to app.ini
+func generateSaveInternalToken() {
+ token, err := generate.NewInternalToken()
+ if err != nil {
+ log.Fatal("Error generate internal token: %v", err)
}
- return token
+
+ InternalToken = token
+ CreateOrAppendToCustomConf(func(cfg *ini.File) {
+ cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
+ })
}
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
@@ -1186,6 +1186,8 @@ func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) {
callback(cfg)
+ log.Info("Settings saved to: %q", CustomConf)
+
if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
log.Fatal("failed to create '%s': %v", CustomConf, err)
return