diff --git a/modules/context/api.go b/modules/context/api.go index 4a2d5d1665d3e..983f60a34a413 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -272,10 +272,9 @@ func APIContexter() func(http.Handler) http.Handler { var locale = middleware.Locale(w, req) var ctx = APIContext{ Context: &Context{ - Resp: NewResponse(w), - Data: map[string]interface{}{}, - Locale: locale, - Session: session.GetSession(req), + BaseContext: NewBaseContext(w, req, map[string]interface{}{}), + Locale: locale, + Session: session.GetSession(req), Repo: &Repository{ PullRequest: &PullRequest{}, }, diff --git a/modules/context/base.go b/modules/context/base.go new file mode 100644 index 0000000000000..0614fa1fd3023 --- /dev/null +++ b/modules/context/base.go @@ -0,0 +1,166 @@ +// 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 context + +import ( + "net/http" + "net/url" + "path" + "strconv" + "strings" + "time" + + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/log" + + chi "github.com/go-chi/chi/v5" +) + +// BaseContext represents a general context for some simple routes +type BaseContext struct { + Resp ResponseWriter + Req *http.Request + Data map[string]interface{} +} + +// NewBaseContext creates a new base context +func NewBaseContext(resp http.ResponseWriter, req *http.Request, data map[string]interface{}) *BaseContext { + return &BaseContext{ + Resp: NewResponse(resp), + Req: req, + Data: data, + } +} + +// GetData returns the data +func (ctx *BaseContext) GetData() map[string]interface{} { + return ctx.Data +} + +// HasDataKey returns true if value of given name exists. +func (ctx *BaseContext) HasDataKey(name string) bool { + _, ok := ctx.Data[name] + return ok +} + +// Header returns a header +func (ctx *BaseContext) Header() http.Header { + return ctx.Resp.Header() +} + +// RemoteAddr returns the client machie ip address +func (ctx *BaseContext) RemoteAddr() string { + return ctx.Req.RemoteAddr +} + +// Params returns the param on route +func (ctx *BaseContext) Params(p string) string { + s, _ := url.PathUnescape(chi.URLParam(ctx.Req, strings.TrimPrefix(p, ":"))) + return s +} + +// ParamsInt64 returns the param on route as int64 +func (ctx *BaseContext) ParamsInt64(p string) int64 { + v, _ := strconv.ParseInt(ctx.Params(p), 10, 64) + return v +} + +// SetParams set params into routes +func (ctx *BaseContext) SetParams(k, v string) { + chiCtx := chi.RouteContext(ctx) + chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v)) +} + +// Write writes data to webbrowser +func (ctx *BaseContext) Write(bs []byte) (int, error) { + return ctx.Resp.Write(bs) +} + +// Written returns true if there are something sent to web browser +func (ctx *BaseContext) Written() bool { + return ctx.Resp.Status() > 0 +} + +// Status writes status code +func (ctx *BaseContext) Status(status int) { + ctx.Resp.WriteHeader(status) +} + +// Deadline is part of the interface for context.Context and we pass this to the request context +func (ctx *BaseContext) Deadline() (deadline time.Time, ok bool) { + return ctx.Req.Context().Deadline() +} + +// Done is part of the interface for context.Context and we pass this to the request context +func (ctx *BaseContext) Done() <-chan struct{} { + return ctx.Req.Context().Done() +} + +// Err is part of the interface for context.Context and we pass this to the request context +func (ctx *BaseContext) Err() error { + return ctx.Req.Context().Err() +} + +// Value is part of the interface for context.Context and we pass this to the request context +func (ctx *BaseContext) Value(key interface{}) interface{} { + return ctx.Req.Context().Value(key) +} + +// Error returned an error to web browser +func (ctx *BaseContext) Error(status int, contents ...string) { + var v = http.StatusText(status) + if len(contents) > 0 { + v = contents[0] + } + http.Error(ctx.Resp, v, status) +} + +// Redirect redirect the request +func (ctx *BaseContext) Redirect(location string, status ...int) { + code := http.StatusFound + if len(status) == 1 { + code = status[0] + } + + http.Redirect(ctx.Resp, ctx.Req, location, code) +} + +// JSON render content as JSON +func (ctx *BaseContext) JSON(status int, content interface{}) { + ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf-8") + ctx.Resp.WriteHeader(status) + if err := json.NewEncoder(ctx.Resp).Encode(content); err != nil { + log.Error("Render JSON failed: %v", err) + ctx.Status(http.StatusInternalServerError) + } +} + +// PlainText render content as plain text +func (ctx *BaseContext) PlainText(status int, bs []byte) { + ctx.Resp.WriteHeader(status) + ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8") + if _, err := ctx.Resp.Write(bs); err != nil { + log.Error("Render PlainText failed: %v", err) + ctx.Status(http.StatusInternalServerError) + } +} + +// ServeFile serves given file to response. +func (ctx *BaseContext) ServeFile(file string, names ...string) { + var name string + if len(names) > 0 { + name = names[0] + } else { + name = path.Base(file) + } + ctx.Resp.Header().Set("Content-Description", "File Transfer") + ctx.Resp.Header().Set("Content-Type", "application/octet-stream") + ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name) + ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") + ctx.Resp.Header().Set("Expires", "0") + ctx.Resp.Header().Set("Cache-Control", "must-revalidate") + ctx.Resp.Header().Set("Pragma", "public") + http.ServeFile(ctx.Resp, ctx.Req, file) +} diff --git a/modules/context/context.go b/modules/context/context.go index 5a37f8b0bf2a9..aacb1e23f4902 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -24,7 +24,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" mc "code.gitea.io/gitea/modules/cache" - "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" @@ -34,7 +33,6 @@ import ( "gitea.com/go-chi/cache" "gitea.com/go-chi/session" - chi "github.com/go-chi/chi/v5" "github.com/unknwon/com" "github.com/unknwon/i18n" "github.com/unrolled/render" @@ -49,9 +47,7 @@ type Render interface { // Context represents context of a request. type Context struct { - Resp ResponseWriter - Req *http.Request - Data map[string]interface{} // data used by MVC templates + *BaseContext PageData map[string]interface{} // data used by JavaScript modules in one page, it's `window.config.pageData` Render Render translation.Locale @@ -166,12 +162,6 @@ func (ctx *Context) HasError() bool { return hasErr.(bool) } -// HasValue returns true if value of given name exists. -func (ctx *Context) HasValue(name string) bool { - _, ok := ctx.Data[name] - return ok -} - // RedirectToFirst redirects to first not empty URL func (ctx *Context) RedirectToFirst(location ...string) { for _, loc := range location { @@ -326,33 +316,6 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa http.ServeContent(ctx.Resp, ctx.Req, name, modtime, r) } -// PlainText render content as plain text -func (ctx *Context) PlainText(status int, bs []byte) { - ctx.Resp.WriteHeader(status) - ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8") - if _, err := ctx.Resp.Write(bs); err != nil { - ctx.ServerError("Write bytes failed", err) - } -} - -// ServeFile serves given file to response. -func (ctx *Context) ServeFile(file string, names ...string) { - var name string - if len(names) > 0 { - name = names[0] - } else { - name = path.Base(file) - } - ctx.Resp.Header().Set("Content-Description", "File Transfer") - ctx.Resp.Header().Set("Content-Type", "application/octet-stream") - ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name) - ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") - ctx.Resp.Header().Set("Expires", "0") - ctx.Resp.Header().Set("Cache-Control", "must-revalidate") - ctx.Resp.Header().Set("Pragma", "public") - http.ServeFile(ctx.Resp, ctx.Req, file) -} - // ServeStream serves file via io stream func (ctx *Context) ServeStream(rd io.Reader, name string) { ctx.Resp.Header().Set("Content-Description", "File Transfer") @@ -368,34 +331,6 @@ func (ctx *Context) ServeStream(rd io.Reader, name string) { } } -// Error returned an error to web browser -func (ctx *Context) Error(status int, contents ...string) { - var v = http.StatusText(status) - if len(contents) > 0 { - v = contents[0] - } - http.Error(ctx.Resp, v, status) -} - -// JSON render content as JSON -func (ctx *Context) JSON(status int, content interface{}) { - ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf-8") - ctx.Resp.WriteHeader(status) - if err := json.NewEncoder(ctx.Resp).Encode(content); err != nil { - ctx.ServerError("Render JSON failed", err) - } -} - -// Redirect redirect the request -func (ctx *Context) Redirect(location string, status ...int) { - code := http.StatusFound - if len(status) == 1 { - code = status[0] - } - - http.Redirect(ctx.Resp, ctx.Req, location, code) -} - // SetCookie convenience function to set most cookies consistently // CSRF and a few others are the exception here func (ctx *Context) SetCookie(name, value string, expiry int) { @@ -483,64 +418,6 @@ func (ctx *Context) GetCookieFloat64(name string) float64 { return v } -// RemoteAddr returns the client machie ip address -func (ctx *Context) RemoteAddr() string { - return ctx.Req.RemoteAddr -} - -// Params returns the param on route -func (ctx *Context) Params(p string) string { - s, _ := url.PathUnescape(chi.URLParam(ctx.Req, strings.TrimPrefix(p, ":"))) - return s -} - -// ParamsInt64 returns the param on route as int64 -func (ctx *Context) ParamsInt64(p string) int64 { - v, _ := strconv.ParseInt(ctx.Params(p), 10, 64) - return v -} - -// SetParams set params into routes -func (ctx *Context) SetParams(k, v string) { - chiCtx := chi.RouteContext(ctx) - chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v)) -} - -// Write writes data to webbrowser -func (ctx *Context) Write(bs []byte) (int, error) { - return ctx.Resp.Write(bs) -} - -// Written returns true if there are something sent to web browser -func (ctx *Context) Written() bool { - return ctx.Resp.Status() > 0 -} - -// Status writes status code -func (ctx *Context) Status(status int) { - ctx.Resp.WriteHeader(status) -} - -// Deadline is part of the interface for context.Context and we pass this to the request context -func (ctx *Context) Deadline() (deadline time.Time, ok bool) { - return ctx.Req.Context().Deadline() -} - -// Done is part of the interface for context.Context and we pass this to the request context -func (ctx *Context) Done() <-chan struct{} { - return ctx.Req.Context().Done() -} - -// Err is part of the interface for context.Context and we pass this to the request context -func (ctx *Context) Err() error { - return ctx.Req.Context().Err() -} - -// Value is part of the interface for context.Context and we pass this to the request context -func (ctx *Context) Value(key interface{}) interface{} { - return ctx.Req.Context().Value(key) -} - // Handler represents a custom handler type Handler func(*Context) @@ -645,7 +522,12 @@ func Contexter() func(next http.Handler) http.Handler { var startTime = time.Now() var link = setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/") var ctx = Context{ - Resp: NewResponse(resp), + BaseContext: NewBaseContext(resp, req, map[string]interface{}{ + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "PageStartTime": startTime, + "Link": link, + "RunModeIsProd": setting.IsProd, + }), Cache: mc.GetCache(), Locale: locale, Link: link, @@ -655,12 +537,6 @@ func Contexter() func(next http.Handler) http.Handler { PullRequest: &PullRequest{}, }, Org: &Organization{}, - Data: map[string]interface{}{ - "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), - "PageStartTime": startTime, - "Link": link, - "RunModeIsProd": setting.IsProd, - }, } // PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules ctx.PageData = map[string]interface{}{} diff --git a/modules/context/form.go b/modules/context/form.go index 8d185909737c7..f06755458857a 100644 --- a/modules/context/form.go +++ b/modules/context/form.go @@ -12,12 +12,12 @@ import ( ) // FormString returns the first value matching the provided key in the form as a string -func (ctx *Context) FormString(key string) string { +func (ctx *BaseContext) FormString(key string) string { return ctx.Req.FormValue(key) } // FormStrings returns a string slice for the provided key from the form -func (ctx *Context) FormStrings(key string) []string { +func (ctx *BaseContext) FormStrings(key string) []string { if ctx.Req.Form == nil { if err := ctx.Req.ParseMultipartForm(32 << 20); err != nil { return nil @@ -30,31 +30,31 @@ func (ctx *Context) FormStrings(key string) []string { } // FormTrim returns the first value for the provided key in the form as a space trimmed string -func (ctx *Context) FormTrim(key string) string { +func (ctx *BaseContext) FormTrim(key string) string { return strings.TrimSpace(ctx.Req.FormValue(key)) } // FormInt returns the first value for the provided key in the form as an int -func (ctx *Context) FormInt(key string) int { +func (ctx *BaseContext) FormInt(key string) int { v, _ := strconv.Atoi(ctx.Req.FormValue(key)) return v } // FormInt64 returns the first value for the provided key in the form as an int64 -func (ctx *Context) FormInt64(key string) int64 { +func (ctx *BaseContext) FormInt64(key string) int64 { v, _ := strconv.ParseInt(ctx.Req.FormValue(key), 10, 64) return v } // FormBool returns true if the value for the provided key in the form is "1" or "true" -func (ctx *Context) FormBool(key string) bool { +func (ctx *BaseContext) FormBool(key string) bool { v, _ := strconv.ParseBool(ctx.Req.FormValue(key)) return v } // FormOptionalBool returns an OptionalBoolTrue or OptionalBoolFalse if the value // for the provided key exists in the form else it returns OptionalBoolNone -func (ctx *Context) FormOptionalBool(key string) util.OptionalBool { +func (ctx *BaseContext) FormOptionalBool(key string) util.OptionalBool { value := ctx.Req.FormValue(key) if len(value) == 0 { return util.OptionalBoolNone diff --git a/modules/context/private.go b/modules/context/private.go index dfefa1d2f0192..25faeb51eda72 100644 --- a/modules/context/private.go +++ b/modules/context/private.go @@ -7,17 +7,29 @@ package context import ( "context" "net/http" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" ) // PrivateContext represents a context for private routes type PrivateContext struct { - *Context + *BaseContext + Repository *models.Repository + GitRepo *git.Repository } var ( privateContextKey interface{} = "default_private_context" ) +// NewPrivateContext creates a new private context +func NewPrivateContext(resp http.ResponseWriter, req *http.Request, data map[string]interface{}) *PrivateContext { + return &PrivateContext{ + BaseContext: NewBaseContext(resp, req, map[string]interface{}{}), + } +} + // WithPrivateContext set up private context in request func WithPrivateContext(req *http.Request, ctx *PrivateContext) *http.Request { return req.WithContext(context.WithValue(req.Context(), privateContextKey, ctx)) @@ -32,12 +44,7 @@ func GetPrivateContext(req *http.Request) *PrivateContext { func PrivateContexter() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - ctx := &PrivateContext{ - Context: &Context{ - Resp: NewResponse(w), - Data: map[string]interface{}{}, - }, - } + ctx := NewPrivateContext(w, req, map[string]interface{}{}) ctx.Req = WithPrivateContext(req, ctx) next.ServeHTTP(ctx.Resp, ctx.Req) }) diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go index 548e454c8eafc..9d3c1f297bf21 100644 --- a/modules/test/context_tests.go +++ b/modules/test/context_tests.go @@ -27,24 +27,23 @@ import ( // MockContext mock context for unit tests func MockContext(t *testing.T, path string) *context.Context { + requestURL, err := url.Parse(path) + assert.NoError(t, err) + var resp = &mockResponseWriter{} + var req = &http.Request{ + URL: requestURL, + Form: url.Values{}, + } var ctx = context.Context{ - Render: &mockRender{}, - Data: make(map[string]interface{}), + BaseContext: context.NewBaseContext(resp, req, map[string]interface{}{}), + Render: &mockRender{}, Flash: &middleware.Flash{ Values: make(url.Values), }, - Resp: context.NewResponse(resp), Locale: &mockLocale{}, } - requestURL, err := url.Parse(path) - assert.NoError(t, err) - var req = &http.Request{ - URL: requestURL, - Form: url.Values{}, - } - chiCtx := chi.NewRouteContext() req = req.WithContext(scontext.WithValue(req.Context(), chi.RouteCtxKey, chiCtx)) ctx.Req = context.WithContext(req, &ctx) diff --git a/routers/api/v1/misc/markdown_test.go b/routers/api/v1/misc/markdown_test.go index dc6762c4cf239..6a1a67d4599f2 100644 --- a/routers/api/v1/misc/markdown_test.go +++ b/routers/api/v1/misc/markdown_test.go @@ -30,10 +30,8 @@ func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecor var rnd = templates.HTMLRenderer() resp := httptest.NewRecorder() c := &context.Context{ - Req: req, - Resp: context.NewResponse(resp), - Render: rnd, - Data: make(map[string]interface{}), + BaseContext: context.NewBaseContext(resp, req, map[string]interface{}{}), + Render: rnd, } return c, resp } diff --git a/routers/install/install.go b/routers/install/install.go index 45804acf3b5ab..82511eff29aba 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -65,12 +65,7 @@ func Init(next http.Handler) http.Handler { var locale = middleware.Locale(resp, req) var startTime = time.Now() var ctx = context.Context{ - Resp: context.NewResponse(resp), - Flash: &middleware.Flash{}, - Locale: locale, - Render: rnd, - Session: session.GetSession(req), - Data: map[string]interface{}{ + BaseContext: context.NewBaseContext(resp, req, map[string]interface{}{ "Title": locale.Tr("install.install"), "PageIsInstall": true, "DbTypeNames": getDbTypeNames(), @@ -84,7 +79,11 @@ func Init(next http.Handler) http.Handler { return time.Since(startTime).String() }, "PasswordHashAlgorithms": user_model.AvailableHashAlgorithms, - }, + }), + Flash: &middleware.Flash{}, + Locale: locale, + Render: rnd, + Session: session.GetSession(req), } for _, lang := range translation.AllLangs() { if lang.Lang == locale.Language() { @@ -249,12 +248,12 @@ func SubmitInstall(ctx *context.Context) { ctx.Data["CurDbType"] = form.DbType if ctx.HasError() { - if ctx.HasValue("Err_SMTPUser") { + if ctx.HasDataKey("Err_SMTPUser") { ctx.Data["Err_SMTP"] = true } - if ctx.HasValue("Err_AdminName") || - ctx.HasValue("Err_AdminPasswd") || - ctx.HasValue("Err_AdminEmail") { + if ctx.HasDataKey("Err_AdminName") || + ctx.HasDataKey("Err_AdminPasswd") || + ctx.HasDataKey("Err_AdminEmail") { ctx.Data["Err_Admin"] = true } diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 5ab21d525b975..8665244a2698c 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -142,8 +142,8 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN return } - repo := ctx.Repo.Repository - gitRepo := ctx.Repo.GitRepo + repo := ctx.Repository + gitRepo := ctx.GitRepo branchName := strings.TrimPrefix(refFullName, git.BranchPrefix) if branchName == repo.DefaultBranch && newCommitID == git.EmptySHA { @@ -363,9 +363,9 @@ func preReceiveTag(ctx *preReceiveContext, oldCommitID, newCommitID, refFullName if !ctx.gotProtectedTags { var err error - ctx.protectedTags, err = ctx.Repo.Repository.GetProtectedTags() + ctx.protectedTags, err = ctx.Repository.GetProtectedTags() if err != nil { - log.Error("Unable to get protected tags for %-v Error: %v", ctx.Repo.Repository, err) + log.Error("Unable to get protected tags for %-v Error: %v", ctx.Repository, err) ctx.JSON(http.StatusInternalServerError, private.Response{ Err: err.Error(), }) @@ -382,7 +382,7 @@ func preReceiveTag(ctx *preReceiveContext, oldCommitID, newCommitID, refFullName return } if !isAllowed { - log.Warn("Forbidden: Tag %s in %-v is protected", tagName, ctx.Repo.Repository) + log.Warn("Forbidden: Tag %s in %-v is protected", tagName, ctx.Repository) ctx.JSON(http.StatusForbidden, private.Response{ Err: fmt.Sprintf("Tag %s is protected", tagName), }) @@ -395,7 +395,7 @@ func preReceivePullRequest(ctx *preReceiveContext, oldCommitID, newCommitID, ref return } - if ctx.Repo.Repository.IsEmpty { + if ctx.Repository.IsEmpty { ctx.JSON(http.StatusForbidden, map[string]interface{}{ "err": "Can't create pull request for an empty repository.", }) @@ -412,13 +412,13 @@ func preReceivePullRequest(ctx *preReceiveContext, oldCommitID, newCommitID, ref baseBranchName := refFullName[len(git.PullRequestPrefix):] baseBranchExist := false - if ctx.Repo.GitRepo.IsBranchExist(baseBranchName) { + if ctx.GitRepo.IsBranchExist(baseBranchName) { baseBranchExist = true } if !baseBranchExist { for p, v := range baseBranchName { - if v == '/' && ctx.Repo.GitRepo.IsBranchExist(baseBranchName[:p]) && p != len(baseBranchName)-1 { + if v == '/' && ctx.GitRepo.IsBranchExist(baseBranchName[:p]) && p != len(baseBranchName)-1 { baseBranchExist = true break } @@ -460,11 +460,11 @@ func loadUserAndPermission(ctx *gitea_context.PrivateContext, id int64) (user *u return } - perm, err = models.GetUserRepoPermission(ctx.Repo.Repository, user) + perm, err = models.GetUserRepoPermission(ctx.Repository, user) if err != nil { - log.Error("Unable to get Repo permission of repo %s/%s of User %s", ctx.Repo.Repository.OwnerName, ctx.Repo.Repository.Name, user.Name, err) + log.Error("Unable to get Repo permission of repo %s/%s of User %s", ctx.Repository.OwnerName, ctx.Repository.Name, user.Name, err) ctx.JSON(http.StatusInternalServerError, private.Response{ - Err: fmt.Sprintf("Unable to get Repo permission of repo %s/%s of User %s: %v", ctx.Repo.Repository.OwnerName, ctx.Repo.Repository.Name, user.Name, err), + Err: fmt.Sprintf("Unable to get Repo permission of repo %s/%s of User %s: %v", ctx.Repository.OwnerName, ctx.Repository.Name, user.Name, err), }) return } diff --git a/routers/private/hook_proc_receive.go b/routers/private/hook_proc_receive.go index e427a55c56161..8aceff9f4bae9 100644 --- a/routers/private/hook_proc_receive.go +++ b/routers/private/hook_proc_receive.go @@ -10,6 +10,7 @@ import ( gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/agit" @@ -23,7 +24,12 @@ func HookProcReceive(ctx *gitea_context.PrivateContext) { return } - results := agit.ProcRecive(ctx, opts) + results, err := agit.ProcReceive(ctx.Repository, ctx.GitRepo, opts) + if err != nil { + log.Error("agit.ProcReceive failed: %v", err) + ctx.Status(http.StatusInternalServerError) + return + } if ctx.Written() { return } diff --git a/routers/private/internal_repo.go b/routers/private/internal_repo.go index 60daa1dbeaae2..f15799918a8a6 100644 --- a/routers/private/internal_repo.go +++ b/routers/private/internal_repo.go @@ -52,16 +52,14 @@ func RepoAssignment(ctx *gitea_context.PrivateContext) context.CancelFunc { return nil } - ctx.Repo = &gitea_context.Repository{ - Repository: repo, - GitRepo: gitRepo, - } + ctx.Repository = repo + ctx.GitRepo = gitRepo // We opened it, we should close it cancel := func() { // If it's been set to nil then assume someone else has closed it. - if ctx.Repo.GitRepo != nil { - ctx.Repo.GitRepo.Close() + if ctx.GitRepo != nil { + ctx.GitRepo.Close() } } diff --git a/services/agit/agit.go b/services/agit/agit.go index beb21940b0cd1..9cc453a50a080 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -6,13 +6,11 @@ package agit import ( "fmt" - "net/http" "os" "strings" "code.gitea.io/gitea/models" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" @@ -20,8 +18,8 @@ import ( pull_service "code.gitea.io/gitea/services/pull" ) -// ProcRecive handle proc receive work -func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []private.HookProcReceiveRefResult { +// ProcReceive handle proc receive work +func ProcReceive(repo *models.Repository, gitRepo *git.Repository, opts *private.HookOptions) ([]private.HookProcReceiveRefResult, error) { // TODO: Add more options? var ( topicBranch string @@ -31,10 +29,8 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat ) results := make([]private.HookProcReceiveRefResult, 0, len(opts.OldCommitIDs)) - repo := ctx.Repo.Repository - gitRepo := ctx.Repo.GitRepo - ownerName := ctx.Repo.Repository.OwnerName - repoName := ctx.Repo.Repository.Name + ownerName := repo.OwnerName + repoName := repo.Name topicBranch = opts.GitPushOptions["topic"] _, forcePush = opts.GitPushOptions["force-push"] @@ -100,11 +96,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat pr, err := models.GetUnmergedPullRequest(repo.ID, repo.ID, headBranch, baseBranchName, models.PullRequestFlowAGit) if err != nil { if !models.IsErrPullRequestNotExist(err) { - log.Error("Failed to get unmerged agit flow pull request in repository: %s/%s Error: %v", ownerName, repoName, err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ - "Err": fmt.Sprintf("Failed to get unmerged agit flow pull request in repository: %s/%s Error: %v", ownerName, repoName, err), - }) - return nil + return nil, fmt.Errorf("Failed to get unmerged agit flow pull request in repository: %s/%s Error: %v", ownerName, repoName, err) } // create a new pull request @@ -114,24 +106,15 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat if !has || len(title) == 0 { commit, err := gitRepo.GetCommit(opts.NewCommitIDs[i]) if err != nil { - log.Error("Failed to get commit %s in repository: %s/%s Error: %v", opts.NewCommitIDs[i], ownerName, repoName, err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ - "Err": fmt.Sprintf("Failed to get commit %s in repository: %s/%s Error: %v", opts.NewCommitIDs[i], ownerName, repoName, err), - }) - return nil + return nil, fmt.Errorf("Failed to get commit %s in repository: %s/%s Error: %v", opts.NewCommitIDs[i], ownerName, repoName, err) } title = strings.Split(commit.CommitMessage, "\n")[0] } description = opts.GitPushOptions["description"] } - pusher, err := user_model.GetUserByID(opts.UserID) if err != nil { - log.Error("Failed to get user. Error: %v", err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ - "Err": fmt.Sprintf("Failed to get user. Error: %v", err), - }) - return nil + return nil, fmt.Errorf("Failed to get user. Error: %v", err) } prIssue := &models.Issue{ @@ -157,12 +140,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat } if err := pull_service.NewPullRequest(repo, prIssue, []int64{}, []string{}, pr, []int64{}); err != nil { - if models.IsErrUserDoesNotHaveAccessToRepo(err) { - ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error()) - return nil - } - ctx.Error(http.StatusInternalServerError, "NewPullRequest", err.Error()) - return nil + return nil, err } log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID) @@ -178,20 +156,12 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat // update exist pull request if err := pr.LoadBaseRepo(); err != nil { - log.Error("Unable to load base repository for PR[%d] Error: %v", pr.ID, err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ - "Err": fmt.Sprintf("Unable to load base repository for PR[%d] Error: %v", pr.ID, err), - }) - return nil + return nil, fmt.Errorf("Unable to load base repository for PR[%d] Error: %v", pr.ID, err) } oldCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName()) if err != nil { - log.Error("Unable to get ref commit id in base repository for PR[%d] Error: %v", pr.ID, err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ - "Err": fmt.Sprintf("Unable to get ref commit id in base repository for PR[%d] Error: %v", pr.ID, err), - }) - return nil + return nil, fmt.Errorf("Unable to get ref commit id in base repository for PR[%d] Error: %v", pr.ID, err) } if oldCommitID == opts.NewCommitIDs[i] { @@ -207,11 +177,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat if !forcePush { output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+opts.NewCommitIDs[i]).RunInDirWithEnv(repo.RepoPath(), os.Environ()) if err != nil { - log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, opts.NewCommitIDs[i], repo, err) - ctx.JSON(http.StatusInternalServerError, private.Response{ - Err: fmt.Sprintf("Fail to detect force push: %v", err), - }) - return nil + return nil, fmt.Errorf("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, opts.NewCommitIDs[i], repo, err) } else if len(output) > 0 { results = append(results, private.HookProcReceiveRefResult{ OriginalRef: opts.RefFullNames[i], @@ -225,29 +191,17 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat pr.HeadCommitID = opts.NewCommitIDs[i] if err = pull_service.UpdateRef(pr); err != nil { - log.Error("Failed to update pull ref. Error: %v", err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ - "Err": fmt.Sprintf("Failed to update pull ref. Error: %v", err), - }) - return nil + return nil, fmt.Errorf("Failed to update pull ref. Error: %v", err) } pull_service.AddToTaskQueue(pr) pusher, err := user_model.GetUserByID(opts.UserID) if err != nil { - log.Error("Failed to get user. Error: %v", err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ - "Err": fmt.Sprintf("Failed to get user. Error: %v", err), - }) - return nil + return nil, fmt.Errorf("Failed to get user. Error: %v", err) } err = pr.LoadIssue() if err != nil { - log.Error("Failed to load pull issue. Error: %v", err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ - "Err": fmt.Sprintf("Failed to load pull issue. Error: %v", err), - }) - return nil + return nil, fmt.Errorf("Failed to load pull issue. Error: %v", err) } comment, err := models.CreatePushPullComment(pusher, pr, oldCommitID, opts.NewCommitIDs[i]) if err == nil && comment != nil { @@ -265,7 +219,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat }) } - return results + return results, nil } // UserNameChanged hanle user name change for agit flow pull