mirror of
1
Fork 0

Drop "unrolled/render" package (#23965)

None of the features of `unrolled/render` package is used. 

The Golang builtin "html/template" just works well. Then we can improve
our HTML render to resolve the "$.root.locale.Tr" problem as much as
possible.

Next step: we can have a template render pool (by Clone), then we can
inject global functions with dynamic context to every `Execute` calls.
Then we can use `{{Locale.Tr ....}}` directly in all templates , no need
to pass the `$.root.locale` again and again.
This commit is contained in:
wxiaoguang 2023-04-08 14:21:50 +08:00 committed by GitHub
parent 7ee2c1336c
commit 8f00979f73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 77 additions and 73 deletions

File diff suppressed because one or more lines are too long

View File

@ -7,9 +7,10 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/fs" "io/fs"
"os" "os"
goPath "path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sort" "sort"
@ -27,9 +28,14 @@ type LicenseEntry struct {
} }
func main() { func main() {
if len(os.Args) != 3 {
fmt.Println("usage: go run generate-go-licenses.go <base-dir> <out-json-file>")
os.Exit(1)
}
base, out := os.Args[1], os.Args[2] base, out := os.Args[1], os.Args[2]
paths := []string{} var paths []string
err := filepath.WalkDir(base, func(path string, entry fs.DirEntry, err error) error { err := filepath.WalkDir(base, func(path string, entry fs.DirEntry, err error) error {
if err != nil { if err != nil {
return err return err
@ -46,28 +52,27 @@ func main() {
sort.Strings(paths) sort.Strings(paths)
entries := []LicenseEntry{} var entries []LicenseEntry
for _, path := range paths { for _, filePath := range paths {
path := filepath.ToSlash(path) licenseText, err := os.ReadFile(filePath)
licenseText, err := os.ReadFile(path)
if err != nil { if err != nil {
panic(err) panic(err)
} }
path = strings.Replace(path, base+"/", "", 1) pkgPath := filepath.ToSlash(filePath)
name := goPath.Dir(path) pkgPath = strings.TrimPrefix(pkgPath, base+"/")
pkgName := path.Dir(pkgPath)
// There might be a bug somewhere in go-licenses that sometimes interprets the // There might be a bug somewhere in go-licenses that sometimes interprets the
// root package as "." and sometimes as "code.gitea.io/gitea". Workaround by // root package as "." and sometimes as "code.gitea.io/gitea". Workaround by
// removing both of them for the sake of stable output. // removing both of them for the sake of stable output.
if name == "." || name == "code.gitea.io/gitea" { if pkgName == "." || pkgName == "code.gitea.io/gitea" {
continue continue
} }
entries = append(entries, LicenseEntry{ entries = append(entries, LicenseEntry{
Name: name, Name: pkgName,
Path: path, Path: pkgPath,
LicenseText: string(licenseText), LicenseText: string(licenseText),
}) })
} }

1
go.mod
View File

@ -96,7 +96,6 @@ require (
github.com/stretchr/testify v1.8.1 github.com/stretchr/testify v1.8.1
github.com/syndtr/goleveldb v1.0.0 github.com/syndtr/goleveldb v1.0.0
github.com/tstranex/u2f v1.0.0 github.com/tstranex/u2f v1.0.0
github.com/unrolled/render v1.5.0
github.com/urfave/cli v1.22.12 github.com/urfave/cli v1.22.12
github.com/xanzy/go-gitlab v0.80.2 github.com/xanzy/go-gitlab v0.80.2
github.com/xeipuuv/gojsonschema v1.2.0 github.com/xeipuuv/gojsonschema v1.2.0

2
go.sum
View File

@ -1182,8 +1182,6 @@ github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/unrolled/render v1.5.0 h1:uNTHMvVoI9pyyXfgoDHHycIqFONNY2p4eQR9ty+NsxM=
github.com/unrolled/render v1.5.0/go.mod h1:eLTosBkQqEPEk7pRfkCRApXd++lm++nCsVlFOHpeedw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=

View File

@ -42,14 +42,13 @@ import (
"gitea.com/go-chi/session" "gitea.com/go-chi/session"
chi "github.com/go-chi/chi/v5" chi "github.com/go-chi/chi/v5"
"github.com/minio/sha256-simd" "github.com/minio/sha256-simd"
"github.com/unrolled/render"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
) )
// Render represents a template render // Render represents a template render
type Render interface { type Render interface {
TemplateLookup(tmpl string) *template.Template TemplateLookup(tmpl string) *template.Template
HTML(w io.Writer, status int, name string, binding interface{}, htmlOpt ...render.HTMLOptions) error HTML(w io.Writer, status int, name string, data interface{}) error
} }
// Context represents context of a request. // Context represents context of a request.

View File

@ -7,15 +7,18 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"html/template"
"io"
"net/http"
"path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"sync/atomic"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/watcher" "code.gitea.io/gitea/modules/watcher"
"github.com/unrolled/render"
) )
var ( var (
@ -27,14 +30,50 @@ var (
expectedEndError = regexp.MustCompile(`^template: (.*):([0-9]+): expected end; found (.*)`) expectedEndError = regexp.MustCompile(`^template: (.*):([0-9]+): expected end; found (.*)`)
) )
// HTMLRenderer returns the current html renderer for the context or creates and stores one within the context for future use type HTMLRender struct {
func HTMLRenderer(ctx context.Context) (context.Context, *render.Render) { templates atomic.Pointer[template.Template]
rendererInterface := ctx.Value(rendererKey) }
if rendererInterface != nil {
renderer, ok := rendererInterface.(*render.Render) func (h *HTMLRender) HTML(w io.Writer, status int, name string, data interface{}) error {
if ok { if respWriter, ok := w.(http.ResponseWriter); ok {
return ctx, renderer if respWriter.Header().Get("Content-Type") == "" {
respWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
} }
respWriter.WriteHeader(status)
}
return h.templates.Load().ExecuteTemplate(w, name, data)
}
func (h *HTMLRender) TemplateLookup(t string) *template.Template {
return h.templates.Load().Lookup(t)
}
func (h *HTMLRender) CompileTemplates() error {
dirPrefix := "templates/"
tmpls := template.New("")
for _, path := range GetTemplateAssetNames() {
name := path[len(dirPrefix):]
name = strings.TrimSuffix(name, ".tmpl")
tmpl := tmpls.New(filepath.ToSlash(name))
for _, fm := range NewFuncMap() {
tmpl.Funcs(fm)
}
buf, err := GetAsset(path)
if err != nil {
return err
}
if _, err = tmpl.Parse(string(buf)); err != nil {
return err
}
}
h.templates.Store(tmpls)
return nil
}
// HTMLRenderer returns the current html renderer for the context or creates and stores one within the context for future use
func HTMLRenderer(ctx context.Context) (context.Context, *HTMLRender) {
if renderer, ok := ctx.Value(rendererKey).(*HTMLRender); ok {
return ctx, renderer
} }
rendererType := "static" rendererType := "static"
@ -43,53 +82,24 @@ func HTMLRenderer(ctx context.Context) (context.Context, *render.Render) {
} }
log.Log(1, log.DEBUG, "Creating "+rendererType+" HTML Renderer") log.Log(1, log.DEBUG, "Creating "+rendererType+" HTML Renderer")
compilingTemplates := true renderer := &HTMLRender{}
defer func() { if err := renderer.CompileTemplates(); err != nil {
if !compilingTemplates { handleFatalError(err)
return }
}
panicked := recover()
if panicked == nil {
return
}
// OK try to handle the panic...
err, ok := panicked.(error)
if ok {
handlePanicError(err)
}
log.Fatal("PANIC: Unable to compile templates!\n%v\n\nStacktrace:\n%s", panicked, log.Stack(2))
}()
renderer := render.New(render.Options{
Extensions: []string{".tmpl"},
Directory: "templates",
Funcs: NewFuncMap(),
Asset: GetAsset,
AssetNames: GetTemplateAssetNames,
UseMutexLock: !setting.IsProd,
IsDevelopment: false,
DisableHTTPErrorRendering: true,
})
compilingTemplates = false
if !setting.IsProd { if !setting.IsProd {
watcher.CreateWatcher(ctx, "HTML Templates", &watcher.CreateWatcherOpts{ watcher.CreateWatcher(ctx, "HTML Templates", &watcher.CreateWatcherOpts{
PathsCallback: walkTemplateFiles, PathsCallback: walkTemplateFiles,
BetweenCallback: func() { BetweenCallback: func() {
defer func() { if err := renderer.CompileTemplates(); err != nil {
if err := recover(); err != nil { log.Error("Template error: %v\n%s", err, log.Stack(2))
log.Error("PANIC: %v\n%s", err, log.Stack(2)) }
}
}()
renderer.CompileTemplates()
}, },
}) })
} }
return context.WithValue(ctx, rendererKey, renderer), renderer return context.WithValue(ctx, rendererKey, renderer), renderer
} }
func handlePanicError(err error) { func handleFatalError(err error) {
wrapFatal(handleNotDefinedPanicError(err)) wrapFatal(handleNotDefinedPanicError(err))
wrapFatal(handleUnexpected(err)) wrapFatal(handleUnexpected(err))
wrapFatal(handleExpectedEnd(err)) wrapFatal(handleExpectedEnd(err))

View File

@ -22,7 +22,6 @@ import (
chi "github.com/go-chi/chi/v5" chi "github.com/go-chi/chi/v5"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/unrolled/render"
) )
// MockContext mock context for unit tests // MockContext mock context for unit tests
@ -138,7 +137,7 @@ func (tr *mockRender) TemplateLookup(tmpl string) *template.Template {
return nil return nil
} }
func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ interface{}, _ ...render.HTMLOptions) error { func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ interface{}) error {
if resp, ok := w.(http.ResponseWriter); ok { if resp, ok := w.(http.ResponseWriter); ok {
resp.WriteHeader(status) resp.WriteHeader(status)
} }

View File

@ -23,7 +23,6 @@ import (
gouuid "github.com/google/uuid" gouuid "github.com/google/uuid"
"github.com/quasoft/websspi" "github.com/quasoft/websspi"
"github.com/unrolled/render"
) )
const ( const (
@ -48,7 +47,7 @@ var (
// On successful authentication returns a valid user object. // On successful authentication returns a valid user object.
// Returns nil if authentication fails. // Returns nil if authentication fails.
type SSPI struct { type SSPI struct {
rnd *render.Render rnd *templates.HTMLRender
} }
// Init creates a new global websspi.Authenticator object // Init creates a new global websspi.Authenticator object

View File

@ -27,7 +27,7 @@ func TestExternalMarkupRenderer(t *testing.T) {
const repoURL = "user30/renderer" const repoURL = "user30/renderer"
req := NewRequest(t, "GET", repoURL+"/src/branch/master/README.html") req := NewRequest(t, "GET", repoURL+"/src/branch/master/README.html")
resp := MakeRequest(t, req, http.StatusOK) resp := MakeRequest(t, req, http.StatusOK)
assert.EqualValues(t, "text/html; charset=UTF-8", resp.Header()["Content-Type"][0]) assert.EqualValues(t, "text/html; charset=utf-8", resp.Header()["Content-Type"][0])
bs, err := io.ReadAll(resp.Body) bs, err := io.ReadAll(resp.Body)
assert.NoError(t, err) assert.NoError(t, err)