From 5f39285d6d3f365a44a828b0d8e7c5e0928a32f0 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 25 May 2023 11:47:30 +0800 Subject: [PATCH] Improve RunMode / dev mode (#24886) 1. non-dev mode is treated as prod mode, to protect users from accidentally running in dev mode if there is a typo in this value. 2. in dev mode, do not need to really exit if there are template errors, because the template errors could be fixed by developer soon and the templates get reloaded, help: * https://github.com/go-gitea/gitea/issues/24845#issuecomment-1557615382 3. Fine tune the mail template loading message. --- custom/conf/app.example.ini | 5 +++-- .../config-cheat-sheet.en-us.md | 2 +- modules/setting/setting.go | 8 ++++++- modules/templates/htmlrenderer.go | 21 ++++++++++++------- modules/templates/mailer.go | 15 +++++++++---- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index c497f1be67..344f92be49 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -49,8 +49,9 @@ APP_NAME = ; Gitea: Git with a cup of tea ;; RUN_USER will automatically detect the current user - but you can set it here change it if you run locally RUN_USER = ; git ;; -;; Application run mode, affects performance and debugging. Either "dev", "prod" or "test", default is "prod" -RUN_MODE = ; prod +;; Application run mode, affects performance and debugging: "dev" or "prod", default is "prod" +;; Mode "dev" makes Gitea easier to develop and debug, values other than "dev" are treated as "prod" which is for production use. +;RUN_MODE = prod ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docs/content/doc/administration/config-cheat-sheet.en-us.md b/docs/content/doc/administration/config-cheat-sheet.en-us.md index e7e482fc79..9bb2533dac 100644 --- a/docs/content/doc/administration/config-cheat-sheet.en-us.md +++ b/docs/content/doc/administration/config-cheat-sheet.en-us.md @@ -73,7 +73,7 @@ In addition there is _`StaticRootPath`_ which can be set as a built-in at build - `RUN_USER`: **_current OS username_/`$USER`/`$USERNAME` e.g. git**: The user Gitea will run as. This should be a dedicated system (non-user) account. Setting this incorrectly will cause Gitea to not start. -- `RUN_MODE`: **prod**: Application run mode, affects performance and debugging. Either "dev", "prod" or "test". +- `RUN_MODE`: **prod**: Application run mode, affects performance and debugging: `dev` or `prod`, default is `prod`. Mode `dev` makes Gitea easier to develop and debug, values other than `dev` are treated as `prod` which is for production use. ## Repository (`repository`) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 8f20ef0856..71cd9a12a9 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -251,7 +251,13 @@ func loadRunModeFrom(rootCfg ConfigProvider) { if RunMode == "" { RunMode = rootSec.Key("RUN_MODE").MustString("prod") } - IsProd = strings.EqualFold(RunMode, "prod") + + // non-dev mode is treated as prod mode, to protect users from accidentally running in dev mode if there is a typo in this value. + RunMode = strings.ToLower(RunMode) + if RunMode != "dev" { + RunMode = "prod" + } + IsProd = RunMode != "dev" // check if we run as root if os.Getuid() == 0 { diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index 21c268da78..e0cbc49ff4 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -97,6 +97,7 @@ func HTMLRenderer() *HTMLRender { } func ReloadHTMLTemplates() error { + log.Trace("Reloading HTML templates") if err := htmlRender.CompileTemplates(); err != nil { log.Error("Template error: %v\n%s", err, log.Stack(2)) return err @@ -114,11 +115,11 @@ func initHTMLRenderer() { htmlRender = &HTMLRender{} if err := htmlRender.CompileTemplates(); err != nil { p := &templateErrorPrettier{assets: AssetFS()} - wrapFatal(p.handleFuncNotDefinedError(err)) - wrapFatal(p.handleUnexpectedOperandError(err)) - wrapFatal(p.handleExpectedEndError(err)) - wrapFatal(p.handleGenericTemplateError(err)) - log.Fatal("HTMLRenderer CompileTemplates error: %v", err) + wrapTmplErrMsg(p.handleFuncNotDefinedError(err)) + wrapTmplErrMsg(p.handleUnexpectedOperandError(err)) + wrapTmplErrMsg(p.handleExpectedEndError(err)) + wrapTmplErrMsg(p.handleGenericTemplateError(err)) + wrapTmplErrMsg(fmt.Sprintf("CompileTemplates error: %v", err)) } if !setting.IsProd { @@ -128,11 +129,17 @@ func initHTMLRenderer() { } } -func wrapFatal(msg string) { +func wrapTmplErrMsg(msg string) { if msg == "" { return } - log.Fatal("Unable to compile templates, %s", msg) + if setting.IsProd { + // in prod mode, Gitea must have correct templates to run + log.Fatal("Gitea can't run with template errors: %s", msg) + } else { + // in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded + log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg) + } } type templateErrorPrettier struct { diff --git a/modules/templates/mailer.go b/modules/templates/mailer.go index ac1715fcd0..54d857a8f6 100644 --- a/modules/templates/mailer.go +++ b/modules/templates/mailer.go @@ -61,7 +61,10 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) { bodyTemplates.Funcs(NewFuncMap()) assetFS := AssetFS() - refreshTemplates := func() { + refreshTemplates := func(firstRun bool) { + if !firstRun { + log.Trace("Reloading mail templates") + } assetPaths, err := ListMailTemplateAssetNames(assetFS) if err != nil { log.Error("Failed to list mail templates: %v", err) @@ -75,17 +78,21 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) { continue } tmplName := strings.TrimPrefix(strings.TrimSuffix(assetPath, ".tmpl"), "mail/") - log.Trace("Adding mail template %s: %s by %s", tmplName, assetPath, layerName) + if firstRun { + log.Trace("Adding mail template %s: %s by %s", tmplName, assetPath, layerName) + } buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content) } } - refreshTemplates() + refreshTemplates(true) if !setting.IsProd { // Now subjectTemplates and bodyTemplates are both synchronized // thus it is safe to call refresh from a different goroutine - go assetFS.WatchLocalChanges(ctx, refreshTemplates) + go assetFS.WatchLocalChanges(ctx, func() { + refreshTemplates(false) + }) } return subjectTemplates, bodyTemplates