Merge pull request '[v1.22/gitea] week 2024-19 cherry pick v7.0' (#3659) from earl-warren/forgejo:wip-v7.0-gitea-cherry-pick into v7.0/forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3659 Reviewed-by: Gergely Nagy <algernon@noreply.codeberg.org>
This commit is contained in:
commit
dd3487dbbf
20 changed files with 219 additions and 122 deletions
|
@ -450,65 +450,6 @@ func UpdateIssueMentions(ctx context.Context, issueID int64, mentions []*user_mo
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIssueByAPI updates all allowed fields of given issue.
|
|
||||||
// If the issue status is changed a statusChangeComment is returned
|
|
||||||
// similarly if the title is changed the titleChanged bool is set to true
|
|
||||||
func UpdateIssueByAPI(ctx context.Context, issue *Issue, doer *user_model.User) (statusChangeComment *Comment, titleChanged bool, err error) {
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := issue.LoadRepo(ctx); err != nil {
|
|
||||||
return nil, false, fmt.Errorf("loadRepo: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reload the issue
|
|
||||||
currentIssue, err := GetIssueByID(ctx, issue.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := db.GetEngine(ctx).ID(issue.ID)
|
|
||||||
cols := []string{"name", "content", "milestone_id", "priority", "deadline_unix", "is_locked"}
|
|
||||||
if issue.NoAutoTime {
|
|
||||||
cols = append(cols, "updated_unix")
|
|
||||||
sess.NoAutoTime()
|
|
||||||
}
|
|
||||||
if _, err := sess.Cols(cols...).Update(issue); err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
titleChanged = currentIssue.Title != issue.Title
|
|
||||||
if titleChanged {
|
|
||||||
opts := &CreateCommentOptions{
|
|
||||||
Type: CommentTypeChangeTitle,
|
|
||||||
Doer: doer,
|
|
||||||
Repo: issue.Repo,
|
|
||||||
Issue: issue,
|
|
||||||
OldTitle: currentIssue.Title,
|
|
||||||
NewTitle: issue.Title,
|
|
||||||
}
|
|
||||||
_, err := CreateComment(ctx, opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("createComment: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentIssue.IsClosed != issue.IsClosed {
|
|
||||||
statusChangeComment, err = doChangeIssueStatus(ctx, issue, doer, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := issue.AddCrossReferences(ctx, doer, true); err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
return statusChangeComment, titleChanged, committer.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateIssueDeadline updates an issue deadline and adds comments. Setting a deadline to 0 means deleting it.
|
// UpdateIssueDeadline updates an issue deadline and adds comments. Setting a deadline to 0 means deleting it.
|
||||||
func UpdateIssueDeadline(ctx context.Context, issue *Issue, deadlineUnix timeutil.TimeStamp, doer *user_model.User) (err error) {
|
func UpdateIssueDeadline(ctx context.Context, issue *Issue, deadlineUnix timeutil.TimeStamp, doer *user_model.User) (err error) {
|
||||||
// if the deadline hasn't changed do nothing
|
// if the deadline hasn't changed do nothing
|
||||||
|
|
|
@ -17,11 +17,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
charsetModule "code.gitea.io/gitea/modules/charset"
|
charsetModule "code.gitea.io/gitea/modules/charset"
|
||||||
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/httpcache"
|
"code.gitea.io/gitea/modules/httpcache"
|
||||||
"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/typesniffer"
|
"code.gitea.io/gitea/modules/typesniffer"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
"github.com/klauspost/compress/gzhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServeHeaderOptions struct {
|
type ServeHeaderOptions struct {
|
||||||
|
@ -38,6 +41,11 @@ type ServeHeaderOptions struct {
|
||||||
func ServeSetHeaders(w http.ResponseWriter, opts *ServeHeaderOptions) {
|
func ServeSetHeaders(w http.ResponseWriter, opts *ServeHeaderOptions) {
|
||||||
header := w.Header()
|
header := w.Header()
|
||||||
|
|
||||||
|
skipCompressionExts := container.SetOf(".gz", ".bz2", ".zip", ".xz", ".zst", ".deb", ".apk", ".jar", ".png", ".jpg", ".webp")
|
||||||
|
if skipCompressionExts.Contains(strings.ToLower(path.Ext(opts.Filename))) {
|
||||||
|
w.Header().Add(gzhttp.HeaderNoCompression, "1")
|
||||||
|
}
|
||||||
|
|
||||||
contentType := typesniffer.ApplicationOctetStream
|
contentType := typesniffer.ApplicationOctetStream
|
||||||
if opts.ContentType != "" {
|
if opts.ContentType != "" {
|
||||||
if opts.ContentTypeCharset != "" {
|
if opts.ContentTypeCharset != "" {
|
||||||
|
|
|
@ -85,7 +85,7 @@ type CreatePullRequestOption struct {
|
||||||
// EditPullRequestOption options when modify pull request
|
// EditPullRequestOption options when modify pull request
|
||||||
type EditPullRequestOption struct {
|
type EditPullRequestOption struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Body string `json:"body"`
|
Body *string `json:"body"`
|
||||||
Base string `json:"base"`
|
Base string `json:"base"`
|
||||||
Assignee string `json:"assignee"`
|
Assignee string `json:"assignee"`
|
||||||
Assignees []string `json:"assignees"`
|
Assignees []string `json:"assignees"`
|
||||||
|
|
|
@ -143,9 +143,7 @@ func serveMavenMetadata(ctx *context.Context, params parameters) {
|
||||||
ctx.Resp.Header().Set("Content-Length", strconv.Itoa(len(xmlMetadataWithHeader)))
|
ctx.Resp.Header().Set("Content-Length", strconv.Itoa(len(xmlMetadataWithHeader)))
|
||||||
ctx.Resp.Header().Set("Content-Type", contentTypeXML)
|
ctx.Resp.Header().Set("Content-Type", contentTypeXML)
|
||||||
|
|
||||||
if _, err := ctx.Resp.Write(xmlMetadataWithHeader); err != nil {
|
_, _ = ctx.Resp.Write(xmlMetadataWithHeader)
|
||||||
log.Error("write bytes failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func servePackageFile(ctx *context.Context, params parameters, serveContent bool) {
|
func servePackageFile(ctx *context.Context, params parameters, serveContent bool) {
|
||||||
|
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
issue_service "code.gitea.io/gitea/services/issue"
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
notify_service "code.gitea.io/gitea/services/notify"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SearchIssues searches for issues across the repositories that the user has access to
|
// SearchIssues searches for issues across the repositories that the user has access to
|
||||||
|
@ -810,12 +809,19 @@ func EditIssue(ctx *context.APIContext) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
oldTitle := issue.Title
|
|
||||||
if len(form.Title) > 0 {
|
if len(form.Title) > 0 {
|
||||||
issue.Title = form.Title
|
err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "ChangeTitle", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if form.Body != nil {
|
if form.Body != nil {
|
||||||
issue.Content = *form.Body
|
err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "ChangeContent", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if form.Ref != nil {
|
if form.Ref != nil {
|
||||||
err = issue_service.ChangeIssueRef(ctx, issue, ctx.Doer, *form.Ref)
|
err = issue_service.ChangeIssueRef(ctx, issue, ctx.Doer, *form.Ref)
|
||||||
|
@ -883,24 +889,14 @@ func EditIssue(ctx *context.APIContext) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
issue.IsClosed = api.StateClosed == api.StateType(*form.State)
|
if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", api.StateClosed == api.StateType(*form.State)); err != nil {
|
||||||
}
|
|
||||||
statusChangeComment, titleChanged, err := issues_model.UpdateIssueByAPI(ctx, issue, ctx.Doer)
|
|
||||||
if err != nil {
|
|
||||||
if issues_model.IsErrDependenciesLeft(err) {
|
if issues_model.IsErrDependenciesLeft(err) {
|
||||||
ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
|
ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err)
|
ctx.Error(http.StatusInternalServerError, "ChangeStatus", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if titleChanged {
|
|
||||||
notify_service.IssueChangeTitle(ctx, ctx.Doer, issue, oldTitle)
|
|
||||||
}
|
|
||||||
|
|
||||||
if statusChangeComment != nil {
|
|
||||||
notify_service.IssueChangeStatus(ctx, ctx.Doer, "", issue, statusChangeComment, issue.IsClosed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refetch from database to assign some automatic values
|
// Refetch from database to assign some automatic values
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/services/attachment"
|
"code.gitea.io/gitea/services/attachment"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
|
"code.gitea.io/gitea/services/context/upload"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
issue_service "code.gitea.io/gitea/services/issue"
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
)
|
)
|
||||||
|
@ -159,6 +160,8 @@ func CreateIssueAttachment(ctx *context.APIContext) {
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
// "422":
|
||||||
|
// "$ref": "#/responses/validationError"
|
||||||
// "423":
|
// "423":
|
||||||
// "$ref": "#/responses/repoArchivedError"
|
// "$ref": "#/responses/repoArchivedError"
|
||||||
|
|
||||||
|
@ -207,7 +210,11 @@ func CreateIssueAttachment(ctx *context.APIContext) {
|
||||||
CreatedUnix: issue.UpdatedUnix,
|
CreatedUnix: issue.UpdatedUnix,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if upload.IsErrFileTypeForbidden(err) {
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, "UploadAttachment", err)
|
ctx.Error(http.StatusInternalServerError, "UploadAttachment", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/services/attachment"
|
"code.gitea.io/gitea/services/attachment"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
|
"code.gitea.io/gitea/services/context/upload"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
issue_service "code.gitea.io/gitea/services/issue"
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
)
|
)
|
||||||
|
@ -156,6 +157,8 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
// "422":
|
||||||
|
// "$ref": "#/responses/validationError"
|
||||||
// "423":
|
// "423":
|
||||||
// "$ref": "#/responses/repoArchivedError"
|
// "$ref": "#/responses/repoArchivedError"
|
||||||
|
|
||||||
|
@ -209,9 +212,14 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
|
||||||
CreatedUnix: comment.Issue.UpdatedUnix,
|
CreatedUnix: comment.Issue.UpdatedUnix,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if upload.IsErrFileTypeForbidden(err) {
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, "UploadAttachment", err)
|
ctx.Error(http.StatusInternalServerError, "UploadAttachment", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := comment.LoadAttachments(ctx); err != nil {
|
if err := comment.LoadAttachments(ctx); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
|
ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -601,12 +601,19 @@ func EditPullRequest(ctx *context.APIContext) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
oldTitle := issue.Title
|
|
||||||
if len(form.Title) > 0 {
|
if len(form.Title) > 0 {
|
||||||
issue.Title = form.Title
|
err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "ChangeTitle", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if form.Body != nil {
|
||||||
|
err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "ChangeContent", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if len(form.Body) > 0 {
|
|
||||||
issue.Content = form.Body
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update or remove deadline if set
|
// Update or remove deadline if set
|
||||||
|
@ -683,24 +690,14 @@ func EditPullRequest(ctx *context.APIContext) {
|
||||||
ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
|
ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
issue.IsClosed = api.StateClosed == api.StateType(*form.State)
|
if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", api.StateClosed == api.StateType(*form.State)); err != nil {
|
||||||
}
|
|
||||||
statusChangeComment, titleChanged, err := issues_model.UpdateIssueByAPI(ctx, issue, ctx.Doer)
|
|
||||||
if err != nil {
|
|
||||||
if issues_model.IsErrDependenciesLeft(err) {
|
if issues_model.IsErrDependenciesLeft(err) {
|
||||||
ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies")
|
ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err)
|
ctx.Error(http.StatusInternalServerError, "ChangeStatus", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if titleChanged {
|
|
||||||
notify_service.IssueChangeTitle(ctx, ctx.Doer, issue, oldTitle)
|
|
||||||
}
|
|
||||||
|
|
||||||
if statusChangeComment != nil {
|
|
||||||
notify_service.IssueChangeStatus(ctx, ctx.Doer, "", issue, statusChangeComment, issue.IsClosed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// change pull target branch
|
// change pull target branch
|
||||||
|
|
|
@ -6,10 +6,8 @@ package user
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/perm"
|
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
unit_model "code.gitea.io/gitea/models/unit"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
|
@ -44,7 +42,7 @@ func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
|
||||||
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
|
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ctx.IsSigned && ctx.Doer.IsAdmin || permission.UnitAccessMode(unit_model.TypeCode) >= perm.AccessModeRead {
|
if ctx.IsSigned && ctx.Doer.IsAdmin || permission.HasAccess() {
|
||||||
apiRepos = append(apiRepos, convert.ToRepo(ctx, repos[i], permission))
|
apiRepos = append(apiRepos, convert.ToRepo(ctx, repos[i], permission))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -469,8 +469,9 @@ func AuthorizeOAuth(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect if user already granted access
|
// Redirect if user already granted access and the application is confidential.
|
||||||
if grant != nil {
|
// I.e. always require authorization for public clients as recommended by RFC 6749 Section 10.2
|
||||||
|
if app.ConfidentialClient && grant != nil {
|
||||||
code, err := grant.GenerateNewAuthorizationCode(ctx, form.RedirectURI, form.CodeChallenge, form.CodeChallengeMethod)
|
code, err := grant.GenerateNewAuthorizationCode(ctx, form.RedirectURI, form.CodeChallenge, form.CodeChallengeMethod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleServerError(ctx, form.State, form.RedirectURI)
|
handleServerError(ctx, form.State, form.RedirectURI)
|
||||||
|
|
|
@ -234,9 +234,7 @@ func (b *Base) plainTextInternal(skip, status int, bs []byte) {
|
||||||
b.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
|
b.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
|
||||||
b.Resp.Header().Set("X-Content-Type-Options", "nosniff")
|
b.Resp.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
b.Resp.WriteHeader(status)
|
b.Resp.WriteHeader(status)
|
||||||
if _, err := b.Resp.Write(bs); err != nil {
|
_, _ = b.Resp.Write(bs)
|
||||||
log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlainTextBytes renders bytes as plain text
|
// PlainTextBytes renders bytes as plain text
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
@ -77,7 +78,7 @@ func (ctx *Context) HTML(status int, name base.TplName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data, ctx.TemplateContext)
|
err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data, ctx.TemplateContext)
|
||||||
if err == nil {
|
if err == nil || errors.Is(err, syscall.EPIPE) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
templates/swagger/v1_json.tmpl
generated
6
templates/swagger/v1_json.tmpl
generated
|
@ -6974,6 +6974,9 @@
|
||||||
"404": {
|
"404": {
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/error"
|
||||||
},
|
},
|
||||||
|
"422": {
|
||||||
|
"$ref": "#/responses/validationError"
|
||||||
|
},
|
||||||
"423": {
|
"423": {
|
||||||
"$ref": "#/responses/repoArchivedError"
|
"$ref": "#/responses/repoArchivedError"
|
||||||
}
|
}
|
||||||
|
@ -7600,6 +7603,9 @@
|
||||||
"404": {
|
"404": {
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/error"
|
||||||
},
|
},
|
||||||
|
"422": {
|
||||||
|
"$ref": "#/responses/validationError"
|
||||||
|
},
|
||||||
"423": {
|
"423": {
|
||||||
"$ref": "#/responses/repoArchivedError"
|
"$ref": "#/responses/repoArchivedError"
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,3 +240,31 @@ func TestAPIDeleteCommentAttachment(t *testing.T) {
|
||||||
|
|
||||||
unittest.AssertNotExistsBean(t, &repo_model.Attachment{ID: attachment.ID, CommentID: comment.ID})
|
unittest.AssertNotExistsBean(t, &repo_model.Attachment{ID: attachment.ID, CommentID: comment.ID})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPICreateCommentAttachmentWithUnallowedFile(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2})
|
||||||
|
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
|
||||||
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
|
||||||
|
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||||
|
|
||||||
|
session := loginUser(t, repoOwner.Name)
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
|
||||||
|
|
||||||
|
filename := "file.bad"
|
||||||
|
body := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// Setup multi-part.
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
_, err := writer.CreateFormFile("attachment", filename)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = writer.Close()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req := NewRequestWithBody(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets", repoOwner.Name, repo.Name, comment.ID), body).
|
||||||
|
AddTokenAuth(token).
|
||||||
|
SetHeader("Content-Type", writer.FormDataContentType())
|
||||||
|
|
||||||
|
session.MakeRequest(t, req, http.StatusUnprocessableEntity)
|
||||||
|
}
|
||||||
|
|
|
@ -173,6 +173,33 @@ func TestAPICreateIssueAttachmentAutoDate(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPICreateIssueAttachmentWithUnallowedFile(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||||
|
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID})
|
||||||
|
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||||
|
|
||||||
|
session := loginUser(t, repoOwner.Name)
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
|
||||||
|
|
||||||
|
filename := "file.bad"
|
||||||
|
body := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// Setup multi-part.
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
_, err := writer.CreateFormFile("attachment", filename)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = writer.Close()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req := NewRequestWithBody(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets", repoOwner.Name, repo.Name, issue.Index), body).
|
||||||
|
AddTokenAuth(token)
|
||||||
|
req.Header.Add("Content-Type", writer.FormDataContentType())
|
||||||
|
|
||||||
|
session.MakeRequest(t, req, http.StatusUnprocessableEntity)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAPIEditIssueAttachment(t *testing.T) {
|
func TestAPIEditIssueAttachment(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
|
|
@ -188,6 +188,10 @@ func TestAPIEditIssue(t *testing.T) {
|
||||||
issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
|
issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
|
||||||
repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
|
repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
|
||||||
|
|
||||||
|
// check comment history
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: issueAfter.ID, OldTitle: issueBefore.Title, NewTitle: title})
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{IssueID: issueAfter.ID, ContentText: body, IsFirstCreated: false})
|
||||||
|
|
||||||
// check deleted user
|
// check deleted user
|
||||||
assert.Equal(t, int64(500), issueAfter.PosterID)
|
assert.Equal(t, int64(500), issueAfter.PosterID)
|
||||||
assert.NoError(t, issueAfter.LoadAttributes(db.DefaultContext))
|
assert.NoError(t, issueAfter.LoadAttributes(db.DefaultContext))
|
||||||
|
|
|
@ -223,23 +223,33 @@ func TestAPIEditPull(t *testing.T) {
|
||||||
|
|
||||||
session := loginUser(t, owner10.Name)
|
session := loginUser(t, owner10.Name)
|
||||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
title := "create a success pr"
|
||||||
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &api.CreatePullRequestOption{
|
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &api.CreatePullRequestOption{
|
||||||
Head: "develop",
|
Head: "develop",
|
||||||
Base: "master",
|
Base: "master",
|
||||||
Title: "create a success pr",
|
Title: title,
|
||||||
}).AddTokenAuth(token)
|
}).AddTokenAuth(token)
|
||||||
pull := new(api.PullRequest)
|
apiPull := new(api.PullRequest)
|
||||||
resp := MakeRequest(t, req, http.StatusCreated)
|
resp := MakeRequest(t, req, http.StatusCreated)
|
||||||
DecodeJSON(t, resp, pull)
|
DecodeJSON(t, resp, apiPull)
|
||||||
assert.EqualValues(t, "master", pull.Base.Name)
|
assert.EqualValues(t, "master", apiPull.Base.Name)
|
||||||
|
|
||||||
req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, pull.Index), &api.EditPullRequestOption{
|
newTitle := "edit a this pr"
|
||||||
|
newBody := "edited body"
|
||||||
|
req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, apiPull.Index), &api.EditPullRequestOption{
|
||||||
Base: "feature/1",
|
Base: "feature/1",
|
||||||
Title: "edit a this pr",
|
Title: newTitle,
|
||||||
|
Body: &newBody,
|
||||||
}).AddTokenAuth(token)
|
}).AddTokenAuth(token)
|
||||||
resp = MakeRequest(t, req, http.StatusCreated)
|
resp = MakeRequest(t, req, http.StatusCreated)
|
||||||
DecodeJSON(t, resp, pull)
|
DecodeJSON(t, resp, apiPull)
|
||||||
assert.EqualValues(t, "feature/1", pull.Base.Name)
|
assert.EqualValues(t, "feature/1", apiPull.Base.Name)
|
||||||
|
// check comment history
|
||||||
|
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: apiPull.ID})
|
||||||
|
err := pull.LoadIssue(db.DefaultContext)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: pull.Issue.ID, OldTitle: title, NewTitle: newTitle})
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{IssueID: pull.Issue.ID, ContentText: newBody, IsFirstCreated: false})
|
||||||
|
|
||||||
req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, pull.Index), &api.EditPullRequestOption{
|
req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, pull.Index), &api.EditPullRequestOption{
|
||||||
Base: "not-exist",
|
Base: "not-exist",
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
unit_model "code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
@ -326,6 +327,39 @@ func TestAPIOrgRepos(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See issue #28483. Tests to make sure we consider more than just code unit-enabled repositories.
|
||||||
|
func TestAPIOrgReposWithCodeUnitDisabled(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "repo21"})
|
||||||
|
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo21.OwnerID})
|
||||||
|
|
||||||
|
// Disable code repository unit.
|
||||||
|
var units []unit_model.Type
|
||||||
|
units = append(units, unit_model.TypeCode)
|
||||||
|
|
||||||
|
if err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo21, nil, units); err != nil {
|
||||||
|
assert.Fail(t, "should have been able to delete code repository unit; failed to %v", err)
|
||||||
|
}
|
||||||
|
assert.False(t, repo21.UnitEnabled(db.DefaultContext, unit_model.TypeCode))
|
||||||
|
|
||||||
|
session := loginUser(t, "user2")
|
||||||
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization)
|
||||||
|
|
||||||
|
req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org3.Name).
|
||||||
|
AddTokenAuth(token)
|
||||||
|
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
var apiRepos []*api.Repository
|
||||||
|
DecodeJSON(t, resp, &apiRepos)
|
||||||
|
|
||||||
|
var repoNames []string
|
||||||
|
for _, r := range apiRepos {
|
||||||
|
repoNames = append(repoNames, r.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Contains(t, repoNames, repo21.Name)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAPIGetRepoByIDUnauthorized(t *testing.T) {
|
func TestAPIGetRepoByIDUnauthorized(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
defer tests.PrepareTestEnv(t)()
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||||
|
|
31
tests/integration/repo_archive_test.go
Normal file
31
tests/integration/repo_archive_test.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
"code.gitea.io/gitea/routers"
|
||||||
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRepoDownloadArchive(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
defer test.MockVariableValue(&setting.EnableGzip, true)()
|
||||||
|
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
|
||||||
|
|
||||||
|
req := NewRequest(t, "GET", "/user2/repo1/archive/master.zip")
|
||||||
|
req.Header.Set("Accept-Encoding", "gzip")
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
bs, err := io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, resp.Header().Get("Content-Encoding"))
|
||||||
|
assert.Equal(t, 320, len(bs))
|
||||||
|
}
|
|
@ -31,6 +31,10 @@
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#user-heatmap .vch__day__square:hover {
|
||||||
|
outline: 1.5px solid var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
/* move the "? contributions in the last ? months" text from top to bottom */
|
/* move the "? contributions in the last ? months" text from top to bottom */
|
||||||
#user-heatmap .total-contributions {
|
#user-heatmap .total-contributions {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|
Loading…
Reference in a new issue