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.
This commit is contained in:
wxiaoguang 2023-05-25 11:47:30 +08:00 committed by GitHub
parent 694b38b880
commit 5f39285d6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 36 additions and 15 deletions

View File

@ -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 will automatically detect the current user - but you can set it here change it if you run locally
RUN_USER = ; git RUN_USER = ; git
;; ;;
;; Application run mode, affects performance and debugging. Either "dev", "prod" or "test", default is "prod" ;; Application run mode, affects performance and debugging: "dev" or "prod", default is "prod"
RUN_MODE = ; 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -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. - `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 This should be a dedicated system (non-user) account. Setting this incorrectly will cause Gitea
to not start. 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`) ## Repository (`repository`)

View File

@ -251,7 +251,13 @@ func loadRunModeFrom(rootCfg ConfigProvider) {
if RunMode == "" { if RunMode == "" {
RunMode = rootSec.Key("RUN_MODE").MustString("prod") 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 // check if we run as root
if os.Getuid() == 0 { if os.Getuid() == 0 {

View File

@ -97,6 +97,7 @@ func HTMLRenderer() *HTMLRender {
} }
func ReloadHTMLTemplates() error { func ReloadHTMLTemplates() error {
log.Trace("Reloading HTML templates")
if err := htmlRender.CompileTemplates(); err != nil { if err := htmlRender.CompileTemplates(); err != nil {
log.Error("Template error: %v\n%s", err, log.Stack(2)) log.Error("Template error: %v\n%s", err, log.Stack(2))
return err return err
@ -114,11 +115,11 @@ func initHTMLRenderer() {
htmlRender = &HTMLRender{} htmlRender = &HTMLRender{}
if err := htmlRender.CompileTemplates(); err != nil { if err := htmlRender.CompileTemplates(); err != nil {
p := &templateErrorPrettier{assets: AssetFS()} p := &templateErrorPrettier{assets: AssetFS()}
wrapFatal(p.handleFuncNotDefinedError(err)) wrapTmplErrMsg(p.handleFuncNotDefinedError(err))
wrapFatal(p.handleUnexpectedOperandError(err)) wrapTmplErrMsg(p.handleUnexpectedOperandError(err))
wrapFatal(p.handleExpectedEndError(err)) wrapTmplErrMsg(p.handleExpectedEndError(err))
wrapFatal(p.handleGenericTemplateError(err)) wrapTmplErrMsg(p.handleGenericTemplateError(err))
log.Fatal("HTMLRenderer CompileTemplates error: %v", err) wrapTmplErrMsg(fmt.Sprintf("CompileTemplates error: %v", err))
} }
if !setting.IsProd { if !setting.IsProd {
@ -128,11 +129,17 @@ func initHTMLRenderer() {
} }
} }
func wrapFatal(msg string) { func wrapTmplErrMsg(msg string) {
if msg == "" { if msg == "" {
return 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 { type templateErrorPrettier struct {

View File

@ -61,7 +61,10 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) {
bodyTemplates.Funcs(NewFuncMap()) bodyTemplates.Funcs(NewFuncMap())
assetFS := AssetFS() assetFS := AssetFS()
refreshTemplates := func() { refreshTemplates := func(firstRun bool) {
if !firstRun {
log.Trace("Reloading mail templates")
}
assetPaths, err := ListMailTemplateAssetNames(assetFS) assetPaths, err := ListMailTemplateAssetNames(assetFS)
if err != nil { if err != nil {
log.Error("Failed to list mail templates: %v", err) log.Error("Failed to list mail templates: %v", err)
@ -75,17 +78,21 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) {
continue continue
} }
tmplName := strings.TrimPrefix(strings.TrimSuffix(assetPath, ".tmpl"), "mail/") tmplName := strings.TrimPrefix(strings.TrimSuffix(assetPath, ".tmpl"), "mail/")
if firstRun {
log.Trace("Adding mail template %s: %s by %s", tmplName, assetPath, layerName) log.Trace("Adding mail template %s: %s by %s", tmplName, assetPath, layerName)
}
buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content) buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content)
} }
} }
refreshTemplates() refreshTemplates(true)
if !setting.IsProd { if !setting.IsProd {
// Now subjectTemplates and bodyTemplates are both synchronized // Now subjectTemplates and bodyTemplates are both synchronized
// thus it is safe to call refresh from a different goroutine // 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 return subjectTemplates, bodyTemplates