From e84e5db6de0306d514b1f1a9657931fb7197a188 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 12 Mar 2024 12:21:27 +0800 Subject: [PATCH 001/553] Lazy load object format with command line and don't do it in OpenRepository (#29712) Most time, when invoking `git.OpenRepository`, `objectFormat` will not be used, so it's a waste to invoke commandline to get the object format. This PR make it a lazy operation, only invoke that when necessary. --- modules/git/blame_sha256_test.go | 5 +++-- modules/git/blame_test.go | 4 +++- modules/git/commit_sha256_test.go | 7 +++++-- modules/git/repo_base_nogogit.go | 5 ----- modules/git/repo_commit.go | 7 ++++++- modules/git/repo_commit_gogit.go | 5 ++++- modules/git/repo_commit_nogogit.go | 9 ++++++--- modules/git/repo_compare.go | 6 +++++- modules/git/repo_compare_test.go | 9 ++++++--- modules/git/repo_index.go | 6 +++++- modules/git/repo_tag.go | 4 ++-- modules/git/repo_tag_test.go | 3 +-- modules/git/repo_tree_gogit.go | 7 ++++++- modules/git/repo_tree_nogogit.go | 12 ++++++++++-- modules/git/tree_nogogit.go | 14 ++++++++++---- 15 files changed, 72 insertions(+), 31 deletions(-) diff --git a/modules/git/blame_sha256_test.go b/modules/git/blame_sha256_test.go index 01de0454a3..8cd345714f 100644 --- a/modules/git/blame_sha256_test.go +++ b/modules/git/blame_sha256_test.go @@ -118,11 +118,12 @@ func TestReadingBlameOutputSha256(t *testing.T) { }, } + objectFormat, err := repo.GetObjectFormat() + assert.NoError(t, err) for _, c := range cases { commit, err := repo.GetCommit(c.CommitID) assert.NoError(t, err) - - blameReader, err := CreateBlameReader(ctx, repo.objectFormat, "./tests/repos/repo6_blame_sha256", commit, "blame.txt", c.Bypass) + blameReader, err := CreateBlameReader(ctx, objectFormat, "./tests/repos/repo6_blame_sha256", commit, "blame.txt", c.Bypass) assert.NoError(t, err) assert.NotNil(t, blameReader) defer blameReader.Close() diff --git a/modules/git/blame_test.go b/modules/git/blame_test.go index 327edab767..4220c85600 100644 --- a/modules/git/blame_test.go +++ b/modules/git/blame_test.go @@ -118,11 +118,13 @@ func TestReadingBlameOutput(t *testing.T) { }, } + objectFormat, err := repo.GetObjectFormat() + assert.NoError(t, err) for _, c := range cases { commit, err := repo.GetCommit(c.CommitID) assert.NoError(t, err) - blameReader, err := CreateBlameReader(ctx, repo.objectFormat, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass) + blameReader, err := CreateBlameReader(ctx, objectFormat, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass) assert.NoError(t, err) assert.NotNil(t, blameReader) defer blameReader.Close() diff --git a/modules/git/commit_sha256_test.go b/modules/git/commit_sha256_test.go index 82112cb409..3b8b6d3763 100644 --- a/modules/git/commit_sha256_test.go +++ b/modules/git/commit_sha256_test.go @@ -140,10 +140,13 @@ func TestHasPreviousCommitSha256(t *testing.T) { commit, err := repo.GetCommit("f004f41359117d319dedd0eaab8c5259ee2263da839dcba33637997458627fdc") assert.NoError(t, err) + objectFormat, err := repo.GetObjectFormat() + assert.NoError(t, err) + parentSHA := MustIDFromString("b0ec7af4547047f12d5093e37ef8f1b3b5415ed8ee17894d43a34d7d34212e9c") notParentSHA := MustIDFromString("42e334efd04cd36eea6da0599913333c26116e1a537ca76e5b6e4af4dda00236") - assert.Equal(t, repo.objectFormat, parentSHA.Type()) - assert.Equal(t, repo.objectFormat.Name(), "sha256") + assert.Equal(t, objectFormat, parentSHA.Type()) + assert.Equal(t, objectFormat.Name(), "sha256") haz, err := commit.HasPreviousCommit(parentSHA) assert.NoError(t, err) diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go index 7f6512200b..5511526e78 100644 --- a/modules/git/repo_base_nogogit.go +++ b/modules/git/repo_base_nogogit.go @@ -71,11 +71,6 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) { repo.batchWriter, repo.batchReader, repo.batchCancel = CatFileBatch(ctx, repoPath) repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(ctx, repoPath) - repo.objectFormat, err = repo.GetObjectFormat() - if err != nil { - return nil, err - } - return repo, nil } diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 9c9ee7768f..44273d2253 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -246,7 +246,12 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) } }() - len := repo.objectFormat.FullLength() + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } + + len := objectFormat.FullLength() commits := []*Commit{} shaline := make([]byte, len+1) for { diff --git a/modules/git/repo_commit_gogit.go b/modules/git/repo_commit_gogit.go index 4cab957564..84580be9a5 100644 --- a/modules/git/repo_commit_gogit.go +++ b/modules/git/repo_commit_gogit.go @@ -41,7 +41,10 @@ func (repo *Repository) RemoveReference(name string) error { // ConvertToHash returns a Hash object from a potential ID string func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { - objectFormat := repo.objectFormat + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } if len(commitID) == hash.HexSize && objectFormat.IsValid(commitID) { ID, err := NewIDFromString(commitID) if err == nil { diff --git a/modules/git/repo_commit_nogogit.go b/modules/git/repo_commit_nogogit.go index a7031184e2..ae4c21aaa3 100644 --- a/modules/git/repo_commit_nogogit.go +++ b/modules/git/repo_commit_nogogit.go @@ -132,8 +132,11 @@ func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id ObjectID) // ConvertToGitID returns a GitHash object from a potential ID string func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { - IDType := repo.objectFormat - if len(commitID) == IDType.FullLength() && IDType.IsValid(commitID) { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } + if len(commitID) == objectFormat.FullLength() && objectFormat.IsValid(commitID) { ID, err := NewIDFromString(commitID) if err == nil { return ID, nil @@ -142,7 +145,7 @@ func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx) defer cancel() - _, err := wr.Write([]byte(commitID + "\n")) + _, err = wr.Write([]byte(commitID + "\n")) if err != nil { return nil, err } diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go index 0e9a0c70d7..b6e9d2b44a 100644 --- a/modules/git/repo_compare.go +++ b/modules/git/repo_compare.go @@ -283,8 +283,12 @@ func (repo *Repository) GetPatch(base, head string, w io.Writer) error { // If base is undefined empty SHA (zeros), it only returns the files changed in the head commit // If base is the SHA of an empty tree (EmptyTreeSHA), it returns the files changes from the initial commit to the head commit func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } cmd := NewCommand(repo.Ctx, "diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z") - if base == repo.objectFormat.EmptyObjectID().String() { + if base == objectFormat.EmptyObjectID().String() { cmd.AddDynamicArguments(head) } else { cmd.AddDynamicArguments(base, head) diff --git a/modules/git/repo_compare_test.go b/modules/git/repo_compare_test.go index 526b213550..9983873186 100644 --- a/modules/git/repo_compare_test.go +++ b/modules/git/repo_compare_test.go @@ -126,17 +126,20 @@ func TestGetCommitFilesChanged(t *testing.T) { assert.NoError(t, err) defer repo.Close() + objectFormat, err := repo.GetObjectFormat() + assert.NoError(t, err) + testCases := []struct { base, head string files []string }{ { - repo.objectFormat.EmptyObjectID().String(), + objectFormat.EmptyObjectID().String(), "95bb4d39648ee7e325106df01a621c530863a653", []string{"file1.txt"}, }, { - repo.objectFormat.EmptyObjectID().String(), + objectFormat.EmptyObjectID().String(), "8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2", []string{"file2.txt"}, }, @@ -146,7 +149,7 @@ func TestGetCommitFilesChanged(t *testing.T) { []string{"file2.txt"}, }, { - repo.objectFormat.EmptyTree().String(), + objectFormat.EmptyTree().String(), "8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2", []string{"file1.txt", "file2.txt"}, }, diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go index 47705a92af..6aaab242c1 100644 --- a/modules/git/repo_index.go +++ b/modules/git/repo_index.go @@ -94,6 +94,10 @@ func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { // RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present. func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return err + } cmd := NewCommand(repo.Ctx, "update-index", "--remove", "-z", "--index-info") stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) @@ -101,7 +105,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { for _, file := range filenames { if file != "" { buffer.WriteString("0 ") - buffer.WriteString(repo.objectFormat.EmptyObjectID().String()) + buffer.WriteString(objectFormat.EmptyObjectID().String()) buffer.WriteByte('\t') buffer.WriteString(file) buffer.WriteByte('\000') diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index ae5dbd171f..e8c5ce6fb8 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -141,7 +141,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) { break } - tag, err := parseTagRef(repo.objectFormat, ref) + tag, err := parseTagRef(ref) if err != nil { return nil, 0, fmt.Errorf("GetTagInfos: parse tag: %w", err) } @@ -161,7 +161,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) { } // parseTagRef parses a tag from a 'git for-each-ref'-produced reference. -func parseTagRef(objectFormat ObjectFormat, ref map[string]string) (tag *Tag, err error) { +func parseTagRef(ref map[string]string) (tag *Tag, err error) { tag = &Tag{ Type: ref["objecttype"], Name: ref["refname:lstrip=2"], diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go index 9816e311a8..785c3442a7 100644 --- a/modules/git/repo_tag_test.go +++ b/modules/git/repo_tag_test.go @@ -194,7 +194,6 @@ func TestRepository_GetAnnotatedTag(t *testing.T) { } func TestRepository_parseTagRef(t *testing.T) { - sha1 := Sha1ObjectFormat tests := []struct { name string @@ -351,7 +350,7 @@ Add changelog of v1.9.1 (#7859) for _, test := range tests { tc := test // don't close over loop variable t.Run(tc.name, func(t *testing.T) { - got, err := parseTagRef(sha1, tc.givenRef) + got, err := parseTagRef(tc.givenRef) if tc.wantErr { require.Error(t, err) diff --git a/modules/git/repo_tree_gogit.go b/modules/git/repo_tree_gogit.go index 6391959e6a..dc97ce1344 100644 --- a/modules/git/repo_tree_gogit.go +++ b/modules/git/repo_tree_gogit.go @@ -21,7 +21,12 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) { // GetTree find the tree object in the repository. func (repo *Repository) GetTree(idStr string) (*Tree, error) { - if len(idStr) != repo.objectFormat.FullLength() { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } + + if len(idStr) != objectFormat.FullLength() { res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(&RunOpts{Dir: repo.Path}) if err != nil { return nil, err diff --git a/modules/git/repo_tree_nogogit.go b/modules/git/repo_tree_nogogit.go index 582247b4a4..e82012de6f 100644 --- a/modules/git/repo_tree_nogogit.go +++ b/modules/git/repo_tree_nogogit.go @@ -51,7 +51,11 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) { case "tree": tree := NewTree(repo, id) tree.ResolvedID = id - tree.entries, err = catBatchParseTreeEntries(repo.objectFormat, tree, rd, size) + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } + tree.entries, err = catBatchParseTreeEntries(objectFormat, tree, rd, size) if err != nil { return nil, err } @@ -69,7 +73,11 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) { // GetTree find the tree object in the repository. func (repo *Repository) GetTree(idStr string) (*Tree, error) { - if len(idStr) != repo.objectFormat.FullLength() { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } + if len(idStr) != objectFormat.FullLength() { res, err := repo.GetRefCommitID(idStr) if err != nil { return nil, err diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go index 28d02c7e81..a591485082 100644 --- a/modules/git/tree_nogogit.go +++ b/modules/git/tree_nogogit.go @@ -77,8 +77,11 @@ func (t *Tree) ListEntries() (Entries, error) { return nil, runErr } - var err error - t.entries, err = parseTreeEntries(t.repo.objectFormat, stdout, t) + objectFormat, err := t.repo.GetObjectFormat() + if err != nil { + return nil, err + } + t.entries, err = parseTreeEntries(objectFormat, stdout, t) if err == nil { t.entriesParsed = true } @@ -101,8 +104,11 @@ func (t *Tree) listEntriesRecursive(extraArgs TrustedCmdArgs) (Entries, error) { return nil, runErr } - var err error - t.entriesRecursive, err = parseTreeEntries(t.repo.objectFormat, stdout, t) + objectFormat, err := t.repo.GetObjectFormat() + if err != nil { + return nil, err + } + t.entriesRecursive, err = parseTreeEntries(objectFormat, stdout, t) if err == nil { t.entriesRecursiveParsed = true } From 75a9f61f89caada64f6398130844281e4f088a73 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Tue, 12 Mar 2024 06:29:51 +0200 Subject: [PATCH 002/553] Remove jQuery AJAX from the issue branch reference selection (#29722) - Replaced a single jQuery AJAX instance with our fetch wrapper - Tested the issue branch reference selection and it works as before # Demo using `fetch` instead of jQuery AJAX ![demo](https://github.com/go-gitea/gitea/assets/20454870/7e195632-41f8-494b-b599-f6291860f330) Signed-off-by: Yarden Shoham --- web_src/js/features/repo-legacy.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 8fcc78c177..60950fd171 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -24,6 +24,7 @@ import {initRepoPullRequestCommitStatus} from './repo-issue-pr-status.js'; import {hideElem, showElem} from '../utils/dom.js'; import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js'; import {attachRefIssueContextPopup} from './contextpopup.js'; +import {POST} from '../modules/fetch.js'; const {csrfToken} = window.config; @@ -65,7 +66,7 @@ export function initRepoCommentForm() { const $selectBranch = $('.ui.select-branch'); const $branchMenu = $selectBranch.find('.reference-list-menu'); const $isNewIssue = $branchMenu.hasClass('new-issue'); - $branchMenu.find('.item:not(.no-select)').on('click', function () { + $branchMenu.find('.item:not(.no-select)').on('click', async function () { const selectedValue = $(this).data('id'); const editMode = $('#editing_mode').val(); $($(this).data('id-selector')).val(selectedValue); @@ -76,7 +77,14 @@ export function initRepoCommentForm() { if (editMode === 'true') { const form = $('#update_issueref_form'); - $.post(form.attr('action'), {_csrf: csrfToken, ref: selectedValue}, () => window.location.reload()); + const params = new URLSearchParams(); + params.append('ref', selectedValue); + try { + await POST(form.attr('action'), {data: params}); + window.location.reload(); + } catch (error) { + console.error('Error:', error); + } } else if (editMode === '') { $selectBranch.find('.ui .branch-name').text(selectedValue); } From aed3b53abdd02a3ffbf9e8eb90272ff567333073 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 12 Mar 2024 12:57:19 +0800 Subject: [PATCH 003/553] Some performance optimization on dashboard and issues page (#29010) This PR do some loading speed optimization for feeds user interface pages. - Load action users batchly but not one by one. - Load action repositories batchly but not one by one. - Load action's Repo Owners batchly but not one by one. - Load action's possible issues batchly but not one by one. - Load action's possible comments batchly but not one by one. --- models/activities/action.go | 116 +++++++++++++------------- models/activities/action_list.go | 136 ++++++++++++++++++++++++++----- models/issues/issue_list.go | 10 +++ models/repo/repo.go | 3 + models/repo/repo_list.go | 35 ++++++++ modules/util/slice.go | 9 ++ routers/web/repo/repo.go | 6 +- 7 files changed, 234 insertions(+), 81 deletions(-) diff --git a/models/activities/action.go b/models/activities/action.go index 36205eedd1..7e2ef4c9ae 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -148,6 +148,7 @@ type Action struct { Repo *repo_model.Repository `xorm:"-"` CommentID int64 `xorm:"INDEX"` Comment *issues_model.Comment `xorm:"-"` + Issue *issues_model.Issue `xorm:"-"` // get the issue id from content IsDeleted bool `xorm:"NOT NULL DEFAULT false"` RefName string IsPrivate bool `xorm:"NOT NULL DEFAULT false"` @@ -290,11 +291,6 @@ func (a *Action) GetRepoAbsoluteLink(ctx context.Context) string { return setting.AppURL + url.PathEscape(a.GetRepoUserName(ctx)) + "/" + url.PathEscape(a.GetRepoName(ctx)) } -// GetCommentHTMLURL returns link to action comment. -func (a *Action) GetCommentHTMLURL(ctx context.Context) string { - return a.getCommentHTMLURL(ctx) -} - func (a *Action) loadComment(ctx context.Context) (err error) { if a.CommentID == 0 || a.Comment != nil { return nil @@ -303,7 +299,8 @@ func (a *Action) loadComment(ctx context.Context) (err error) { return err } -func (a *Action) getCommentHTMLURL(ctx context.Context) string { +// GetCommentHTMLURL returns link to action comment. +func (a *Action) GetCommentHTMLURL(ctx context.Context) string { if a == nil { return "#" } @@ -311,34 +308,19 @@ func (a *Action) getCommentHTMLURL(ctx context.Context) string { if a.Comment != nil { return a.Comment.HTMLURL(ctx) } - if len(a.GetIssueInfos()) == 0 { + + if err := a.LoadIssue(ctx); err != nil || a.Issue == nil { return "#" } - // Return link to issue - issueIDString := a.GetIssueInfos()[0] - issueID, err := strconv.ParseInt(issueIDString, 10, 64) - if err != nil { + if err := a.Issue.LoadRepo(ctx); err != nil { return "#" } - issue, err := issues_model.GetIssueByID(ctx, issueID) - if err != nil { - return "#" - } - - if err = issue.LoadRepo(ctx); err != nil { - return "#" - } - - return issue.HTMLURL() + return a.Issue.HTMLURL() } // GetCommentLink returns link to action comment. func (a *Action) GetCommentLink(ctx context.Context) string { - return a.getCommentLink(ctx) -} - -func (a *Action) getCommentLink(ctx context.Context) string { if a == nil { return "#" } @@ -346,26 +328,15 @@ func (a *Action) getCommentLink(ctx context.Context) string { if a.Comment != nil { return a.Comment.Link(ctx) } - if len(a.GetIssueInfos()) == 0 { + + if err := a.LoadIssue(ctx); err != nil || a.Issue == nil { return "#" } - // Return link to issue - issueIDString := a.GetIssueInfos()[0] - issueID, err := strconv.ParseInt(issueIDString, 10, 64) - if err != nil { + if err := a.Issue.LoadRepo(ctx); err != nil { return "#" } - issue, err := issues_model.GetIssueByID(ctx, issueID) - if err != nil { - return "#" - } - - if err = issue.LoadRepo(ctx); err != nil { - return "#" - } - - return issue.Link() + return a.Issue.Link() } // GetBranch returns the action's repository branch. @@ -393,6 +364,10 @@ func (a *Action) GetCreate() time.Time { return a.CreatedUnix.AsTime() } +func (a *Action) IsIssueEvent() bool { + return a.OpType.InActions("comment_issue", "approve_pull_request", "reject_pull_request", "comment_pull", "merge_pull_request") +} + // GetIssueInfos returns a list of associated information with the action. func (a *Action) GetIssueInfos() []string { // make sure it always returns 3 elements, because there are some access to the a[1] and a[2] without checking the length @@ -403,27 +378,52 @@ func (a *Action) GetIssueInfos() []string { return ret } -// GetIssueTitle returns the title of first issue associated with the action. -func (a *Action) GetIssueTitle(ctx context.Context) string { - index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) - issue, err := issues_model.GetIssueByIndex(ctx, a.RepoID, index) - if err != nil { - log.Error("GetIssueByIndex: %v", err) - return "500 when get issue" +func (a *Action) getIssueIndex() int64 { + infos := a.GetIssueInfos() + if len(infos) == 0 { + return 0 } - return issue.Title + index, _ := strconv.ParseInt(infos[0], 10, 64) + return index } -// GetIssueContent returns the content of first issue associated with -// this action. -func (a *Action) GetIssueContent(ctx context.Context) string { - index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) - issue, err := issues_model.GetIssueByIndex(ctx, a.RepoID, index) - if err != nil { - log.Error("GetIssueByIndex: %v", err) - return "500 when get issue" +func (a *Action) LoadIssue(ctx context.Context) error { + if a.Issue != nil { + return nil } - return issue.Content + if index := a.getIssueIndex(); index > 0 { + issue, err := issues_model.GetIssueByIndex(ctx, a.RepoID, index) + if err != nil { + return err + } + a.Issue = issue + a.Issue.Repo = a.Repo + } + return nil +} + +// GetIssueTitle returns the title of first issue associated with the action. +func (a *Action) GetIssueTitle(ctx context.Context) string { + if err := a.LoadIssue(ctx); err != nil { + log.Error("LoadIssue: %v", err) + return "<500 when get issue>" + } + if a.Issue == nil { + return "" + } + return a.Issue.Title +} + +// GetIssueContent returns the content of first issue associated with this action. +func (a *Action) GetIssueContent(ctx context.Context) string { + if err := a.LoadIssue(ctx); err != nil { + log.Error("LoadIssue: %v", err) + return "<500 when get issue>" + } + if a.Issue == nil { + return "" + } + return a.Issue.Content } // GetFeedsOptions options for retrieving feeds @@ -463,7 +463,7 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err return nil, 0, fmt.Errorf("FindAndCount: %w", err) } - if err := ActionList(actions).loadAttributes(ctx); err != nil { + if err := ActionList(actions).LoadAttributes(ctx); err != nil { return nil, 0, fmt.Errorf("LoadAttributes: %w", err) } diff --git a/models/activities/action_list.go b/models/activities/action_list.go index 3d74397c69..fdf0f35d4f 100644 --- a/models/activities/action_list.go +++ b/models/activities/action_list.go @@ -6,11 +6,16 @@ package activities import ( "context" "fmt" + "strconv" "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/util" + + "xorm.io/builder" ) // ActionList defines a list of actions @@ -24,7 +29,7 @@ func (actions ActionList) getUserIDs() []int64 { return userIDs.Values() } -func (actions ActionList) loadUsers(ctx context.Context) (map[int64]*user_model.User, error) { +func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_model.User, error) { if len(actions) == 0 { return nil, nil } @@ -52,7 +57,7 @@ func (actions ActionList) getRepoIDs() []int64 { return repoIDs.Values() } -func (actions ActionList) loadRepositories(ctx context.Context) error { +func (actions ActionList) LoadRepositories(ctx context.Context) error { if len(actions) == 0 { return nil } @@ -63,11 +68,11 @@ func (actions ActionList) loadRepositories(ctx context.Context) error { if err != nil { return fmt.Errorf("find repository: %w", err) } - for _, action := range actions { action.Repo = repoMaps[action.RepoID] } - return nil + repos := repo_model.RepositoryList(util.ValuesOfMap(repoMaps)) + return repos.LoadUnits(ctx) } func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]*user_model.User) (err error) { @@ -75,37 +80,124 @@ func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]* userMap = make(map[int64]*user_model.User) } + userSet := make(container.Set[int64], len(actions)) for _, action := range actions { if action.Repo == nil { continue } - repoOwner, ok := userMap[action.Repo.OwnerID] - if !ok { - repoOwner, err = user_model.GetUserByID(ctx, action.Repo.OwnerID) - if err != nil { - if user_model.IsErrUserNotExist(err) { - continue - } - return err - } - userMap[repoOwner.ID] = repoOwner + if _, ok := userMap[action.Repo.OwnerID]; !ok { + userSet.Add(action.Repo.OwnerID) + } + } + + if err := db.GetEngine(ctx). + In("id", userSet.Values()). + Find(&userMap); err != nil { + return fmt.Errorf("find user: %w", err) + } + + for _, action := range actions { + if action.Repo != nil { + action.Repo.Owner = userMap[action.Repo.OwnerID] } - action.Repo.Owner = repoOwner } return nil } -// loadAttributes loads all attributes -func (actions ActionList) loadAttributes(ctx context.Context) error { - userMap, err := actions.loadUsers(ctx) +// LoadAttributes loads all attributes +func (actions ActionList) LoadAttributes(ctx context.Context) error { + // the load sequence cannot be changed because of the dependencies + userMap, err := actions.LoadActUsers(ctx) if err != nil { return err } - - if err := actions.loadRepositories(ctx); err != nil { + if err := actions.LoadRepositories(ctx); err != nil { return err } - - return actions.loadRepoOwner(ctx, userMap) + if err := actions.loadRepoOwner(ctx, userMap); err != nil { + return err + } + if err := actions.LoadIssues(ctx); err != nil { + return err + } + return actions.LoadComments(ctx) +} + +func (actions ActionList) LoadComments(ctx context.Context) error { + if len(actions) == 0 { + return nil + } + + commentIDs := make([]int64, 0, len(actions)) + for _, action := range actions { + if action.CommentID > 0 { + commentIDs = append(commentIDs, action.CommentID) + } + } + + commentsMap := make(map[int64]*issues_model.Comment, len(commentIDs)) + if err := db.GetEngine(ctx).In("id", commentIDs).Find(&commentsMap); err != nil { + return fmt.Errorf("find comment: %w", err) + } + + for _, action := range actions { + if action.CommentID > 0 { + action.Comment = commentsMap[action.CommentID] + if action.Comment != nil { + action.Comment.Issue = action.Issue + } + } + } + return nil +} + +func (actions ActionList) LoadIssues(ctx context.Context) error { + if len(actions) == 0 { + return nil + } + + conditions := builder.NewCond() + issueNum := 0 + for _, action := range actions { + if action.IsIssueEvent() { + infos := action.GetIssueInfos() + if len(infos) == 0 { + continue + } + index, _ := strconv.ParseInt(infos[0], 10, 64) + if index > 0 { + conditions = conditions.Or(builder.Eq{ + "repo_id": action.RepoID, + "`index`": index, + }) + issueNum++ + } + } + } + if !conditions.IsValid() { + return nil + } + + issuesMap := make(map[string]*issues_model.Issue, issueNum) + issues := make([]*issues_model.Issue, 0, issueNum) + if err := db.GetEngine(ctx).Where(conditions).Find(&issues); err != nil { + return fmt.Errorf("find issue: %w", err) + } + for _, issue := range issues { + issuesMap[fmt.Sprintf("%d-%d", issue.RepoID, issue.Index)] = issue + } + + for _, action := range actions { + if !action.IsIssueEvent() { + continue + } + if index := action.getIssueIndex(); index > 0 { + if issue, ok := issuesMap[fmt.Sprintf("%d-%d", action.RepoID, index)]; ok { + action.Issue = issue + action.Issue.Repo = action.Repo + } + } + } + return nil } diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index a932ac2554..0fb8447ff7 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -476,6 +476,16 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) { } trackedTimes := make(map[int64]int64, len(issues)) + reposMap := make(map[int64]*repo_model.Repository, len(issues)) + for _, issue := range issues { + reposMap[issue.RepoID] = issue.Repo + } + repos := repo_model.RepositoryListOfMap(reposMap) + + if err := repos.LoadUnits(ctx); err != nil { + return err + } + ids := make([]int64, 0, len(issues)) for _, issue := range issues { if issue.Repo.IsTimetrackerEnabled(ctx) { diff --git a/models/repo/repo.go b/models/repo/repo.go index 1d17e565ae..5d5707d1ac 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -531,6 +531,9 @@ func (repo *Repository) GetBaseRepo(ctx context.Context) (err error) { return nil } + if repo.BaseRepo != nil { + return nil + } repo.BaseRepo, err = GetRepositoryByID(ctx, repo.ForkID) return err } diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 6b452291ea..cb7cd47a8d 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -63,6 +63,41 @@ func RepositoryListOfMap(repoMap map[int64]*Repository) RepositoryList { return RepositoryList(ValuesRepository(repoMap)) } +func (repos RepositoryList) LoadUnits(ctx context.Context) error { + if len(repos) == 0 { + return nil + } + + // Load units. + units := make([]*RepoUnit, 0, len(repos)*6) + if err := db.GetEngine(ctx). + In("repo_id", repos.IDs()). + Find(&units); err != nil { + return fmt.Errorf("find units: %w", err) + } + + unitsMap := make(map[int64][]*RepoUnit, len(repos)) + for _, unit := range units { + if !unit.Type.UnitGlobalDisabled() { + unitsMap[unit.RepoID] = append(unitsMap[unit.RepoID], unit) + } + } + + for _, repo := range repos { + repo.Units = unitsMap[repo.ID] + } + + return nil +} + +func (repos RepositoryList) IDs() []int64 { + repoIDs := make([]int64, len(repos)) + for i := range repos { + repoIDs[i] = repos[i].ID + } + return repoIDs +} + // LoadAttributes loads the attributes for the given RepositoryList func (repos RepositoryList) LoadAttributes(ctx context.Context) error { if len(repos) == 0 { diff --git a/modules/util/slice.go b/modules/util/slice.go index a7073fedee..f00e84bf06 100644 --- a/modules/util/slice.go +++ b/modules/util/slice.go @@ -53,3 +53,12 @@ func Sorted[S ~[]E, E cmp.Ordered](values S) S { slices.Sort(values) return values } + +// TODO: Replace with "maps.Values" once available +func ValuesOfMap[K comparable, V any](m map[K]V) []V { + values := make([]V, 0, len(m)) + for _, v := range m { + values = append(values, v) + } + return values +} diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index b54d29c580..7a626a7065 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -547,9 +547,13 @@ func InitiateDownload(ctx *context.Context) { // SearchRepo repositories via options func SearchRepo(ctx *context.Context) { + page := ctx.FormInt("page") + if page <= 0 { + page = 1 + } opts := &repo_model.SearchRepoOptions{ ListOptions: db.ListOptions{ - Page: ctx.FormInt("page"), + Page: page, PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), }, Actor: ctx.Doer, From d8bd6f34f09bc9a6602bebb33bdc9e1f255a0d7c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 12 Mar 2024 15:23:44 +0800 Subject: [PATCH 004/553] Do some performance optimize for issues list and view issue/pull (#29515) This PR do some performance optimzations. - [x] Add `index` for the column `comment_id` of `Attachment` table to accelerate query from the database. - [x] Remove unnecessary database queries when viewing issues. Before some conditions which id = 0 will be sent to the database - [x] Remove duplicated load posters - [x] Batch loading attachements, isread of comments on viewing issue --------- Co-authored-by: Zettat123 --- models/issues/comment.go | 8 +-- models/issues/comment_code.go | 2 +- models/issues/comment_list.go | 78 +++++++++++++++++++++------- models/issues/issue_list.go | 25 +++++++-- models/migrations/migrations.go | 2 + models/migrations/v1_22/v291.go | 14 +++++ models/repo/attachment.go | 2 +- routers/api/v1/repo/issue_comment.go | 4 -- routers/web/repo/issue.go | 39 ++++++-------- routers/web/repo/pull_review.go | 8 ++- 10 files changed, 121 insertions(+), 61 deletions(-) create mode 100644 models/migrations/v1_22/v291.go diff --git a/models/issues/comment.go b/models/issues/comment.go index e37f844b5c..6f65a5dbbc 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -673,7 +673,8 @@ func (c *Comment) LoadTime(ctx context.Context) error { return err } -func (c *Comment) loadReactions(ctx context.Context, repo *repo_model.Repository) (err error) { +// LoadReactions loads comment reactions +func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository) (err error) { if c.Reactions != nil { return nil } @@ -691,11 +692,6 @@ func (c *Comment) loadReactions(ctx context.Context, repo *repo_model.Repository return nil } -// LoadReactions loads comment reactions -func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository) error { - return c.loadReactions(ctx, repo) -} - func (c *Comment) loadReview(ctx context.Context) (err error) { if c.ReviewID == 0 { return nil diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go index 384a595dd9..74a7a86f26 100644 --- a/models/issues/comment_code.go +++ b/models/issues/comment_code.go @@ -122,7 +122,7 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu } // FetchCodeCommentsByLine fetches the code comments for a given treePath and line number -func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64, showOutdatedComments bool) ([]*Comment, error) { +func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64, showOutdatedComments bool) (CommentList, error) { opts := FindCommentsOptions{ Type: CommentTypeCode, IssueID: issue.ID, diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go index 30a437ea50..0047b054ba 100644 --- a/models/issues/comment_list.go +++ b/models/issues/comment_list.go @@ -19,7 +19,9 @@ type CommentList []*Comment func (comments CommentList) getPosterIDs() []int64 { posterIDs := make(container.Set[int64], len(comments)) for _, comment := range comments { - posterIDs.Add(comment.PosterID) + if comment.PosterID > 0 { + posterIDs.Add(comment.PosterID) + } } return posterIDs.Values() } @@ -41,18 +43,12 @@ func (comments CommentList) LoadPosters(ctx context.Context) error { return nil } -func (comments CommentList) getCommentIDs() []int64 { - ids := make([]int64, 0, len(comments)) - for _, comment := range comments { - ids = append(ids, comment.ID) - } - return ids -} - func (comments CommentList) getLabelIDs() []int64 { ids := make(container.Set[int64], len(comments)) for _, comment := range comments { - ids.Add(comment.LabelID) + if comment.LabelID > 0 { + ids.Add(comment.LabelID) + } } return ids.Values() } @@ -100,7 +96,9 @@ func (comments CommentList) loadLabels(ctx context.Context) error { func (comments CommentList) getMilestoneIDs() []int64 { ids := make(container.Set[int64], len(comments)) for _, comment := range comments { - ids.Add(comment.MilestoneID) + if comment.MilestoneID > 0 { + ids.Add(comment.MilestoneID) + } } return ids.Values() } @@ -141,7 +139,9 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { func (comments CommentList) getOldMilestoneIDs() []int64 { ids := make(container.Set[int64], len(comments)) for _, comment := range comments { - ids.Add(comment.OldMilestoneID) + if comment.OldMilestoneID > 0 { + ids.Add(comment.OldMilestoneID) + } } return ids.Values() } @@ -182,7 +182,9 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { func (comments CommentList) getAssigneeIDs() []int64 { ids := make(container.Set[int64], len(comments)) for _, comment := range comments { - ids.Add(comment.AssigneeID) + if comment.AssigneeID > 0 { + ids.Add(comment.AssigneeID) + } } return ids.Values() } @@ -314,7 +316,9 @@ func (comments CommentList) getDependentIssueIDs() []int64 { if comment.DependentIssue != nil { continue } - ids.Add(comment.DependentIssueID) + if comment.DependentIssueID > 0 { + ids.Add(comment.DependentIssueID) + } } return ids.Values() } @@ -369,6 +373,41 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error { return nil } +// getAttachmentCommentIDs only return the comment ids which possibly has attachments +func (comments CommentList) getAttachmentCommentIDs() []int64 { + ids := make(container.Set[int64], len(comments)) + for _, comment := range comments { + if comment.Type == CommentTypeComment || + comment.Type == CommentTypeReview || + comment.Type == CommentTypeCode { + ids.Add(comment.ID) + } + } + return ids.Values() +} + +// LoadAttachmentsByIssue loads attachments by issue id +func (comments CommentList) LoadAttachmentsByIssue(ctx context.Context) error { + if len(comments) == 0 { + return nil + } + + attachments := make([]*repo_model.Attachment, 0, len(comments)/2) + if err := db.GetEngine(ctx).Where("issue_id=? AND comment_id>0", comments[0].IssueID).Find(&attachments); err != nil { + return err + } + + commentAttachmentsMap := make(map[int64][]*repo_model.Attachment, len(comments)) + for _, attach := range attachments { + commentAttachmentsMap[attach.CommentID] = append(commentAttachmentsMap[attach.CommentID], attach) + } + + for _, comment := range comments { + comment.Attachments = commentAttachmentsMap[comment.ID] + } + return nil +} + // LoadAttachments loads attachments func (comments CommentList) LoadAttachments(ctx context.Context) (err error) { if len(comments) == 0 { @@ -376,16 +415,15 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) { } attachments := make(map[int64][]*repo_model.Attachment, len(comments)) - commentsIDs := comments.getCommentIDs() + commentsIDs := comments.getAttachmentCommentIDs() left := len(commentsIDs) for left > 0 { limit := db.DefaultMaxInSize if left < limit { limit = left } - rows, err := db.GetEngine(ctx).Table("attachment"). - Join("INNER", "comment", "comment.id = attachment.comment_id"). - In("comment.id", commentsIDs[:limit]). + rows, err := db.GetEngine(ctx). + In("comment_id", commentsIDs[:limit]). Rows(new(repo_model.Attachment)) if err != nil { return err @@ -415,7 +453,9 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) { func (comments CommentList) getReviewIDs() []int64 { ids := make(container.Set[int64], len(comments)) for _, comment := range comments { - ids.Add(comment.ReviewID) + if comment.ReviewID > 0 { + ids.Add(comment.ReviewID) + } } return ids.Values() } diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index 0fb8447ff7..41a90d133d 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -388,9 +388,8 @@ func (issues IssueList) LoadAttachments(ctx context.Context) (err error) { if left < limit { limit = left } - rows, err := db.GetEngine(ctx).Table("attachment"). - Join("INNER", "issue", "issue.id = attachment.issue_id"). - In("issue.id", issuesIDs[:limit]). + rows, err := db.GetEngine(ctx). + In("issue_id", issuesIDs[:limit]). Rows(new(repo_model.Attachment)) if err != nil { return err @@ -609,3 +608,23 @@ func (issues IssueList) GetApprovalCounts(ctx context.Context) (map[int64][]*Rev return approvalCountMap, nil } + +func (issues IssueList) LoadIsRead(ctx context.Context, userID int64) error { + issueIDs := issues.getIssueIDs() + issueUsers := make([]*IssueUser, 0, len(issueIDs)) + if err := db.GetEngine(ctx).Where("uid =?", userID). + In("issue_id"). + Find(&issueUsers); err != nil { + return err + } + + for _, issueUser := range issueUsers { + for _, issue := range issues { + if issue.ID == issueUser.IssueID { + issue.IsRead = issueUser.IsRead + } + } + } + + return nil +} diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index ce77432db4..87fddefb88 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -566,6 +566,8 @@ var migrations = []Migration{ NewMigration("Add default_wiki_branch to repository table", v1_22.AddDefaultWikiBranch), // v290 -> v291 NewMigration("Add PayloadVersion to HookTask", v1_22.AddPayloadVersionToHookTaskTable), + // v291 -> v292 + NewMigration("Add Index to attachment.comment_id", v1_22.AddCommentIDIndexofAttachment), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go new file mode 100644 index 0000000000..0bfffe5d05 --- /dev/null +++ b/models/migrations/v1_22/v291.go @@ -0,0 +1,14 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func AddCommentIDIndexofAttachment(x *xorm.Engine) error { + type Attachment struct { + CommentID int64 `xorm:"INDEX"` + } + + return x.Sync(&Attachment{}) +} diff --git a/models/repo/attachment.go b/models/repo/attachment.go index 1a588398c1..9b0de11fdc 100644 --- a/models/repo/attachment.go +++ b/models/repo/attachment.go @@ -24,7 +24,7 @@ type Attachment struct { IssueID int64 `xorm:"INDEX"` // maybe zero when creating ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added - CommentID int64 + CommentID int64 `xorm:"INDEX"` Name string DownloadCount int64 `xorm:"DEFAULT 0"` Size int64 `xorm:"DEFAULT 0"` diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 21aabadf3d..070571ba62 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -323,10 +323,6 @@ func ListRepoIssueComments(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadIssues", err) return } - if err := comments.LoadPosters(ctx); err != nil { - ctx.Error(http.StatusInternalServerError, "LoadPosters", err) - return - } if err := comments.LoadAttachments(ctx); err != nil { ctx.Error(http.StatusInternalServerError, "LoadAttachments", err) return diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 83a5b76bf1..8935cc80e2 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -324,15 +324,15 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt return } - // Get posters. - for i := range issues { - // Check read status - if !ctx.IsSigned { - issues[i].IsRead = true - } else if err = issues[i].GetIsRead(ctx, ctx.Doer.ID); err != nil { - ctx.ServerError("GetIsRead", err) + if ctx.IsSigned { + if err := issues.LoadIsRead(ctx, ctx.Doer.ID); err != nil { + ctx.ServerError("LoadIsRead", err) return } + } else { + for i := range issues { + issues[i].IsRead = true + } } commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(ctx, issues) @@ -1604,20 +1604,20 @@ func ViewIssue(ctx *context.Context) { // Render comments and and fetch participants. participants[0] = issue.Poster + + if err := issue.Comments.LoadAttachmentsByIssue(ctx); err != nil { + ctx.ServerError("LoadAttachmentsByIssue", err) + return + } + if err := issue.Comments.LoadPosters(ctx); err != nil { + ctx.ServerError("LoadPosters", err) + return + } + for _, comment = range issue.Comments { comment.Issue = issue - if err := comment.LoadPoster(ctx); err != nil { - ctx.ServerError("LoadPoster", err) - return - } - if comment.Type == issues_model.CommentTypeComment || comment.Type == issues_model.CommentTypeReview { - if err := comment.LoadAttachments(ctx); err != nil { - ctx.ServerError("LoadAttachments", err) - return - } - comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ Links: markup.Links{ Base: ctx.Repo.RepoLink, @@ -1665,7 +1665,6 @@ func ViewIssue(ctx *context.Context) { comment.Milestone = ghostMilestone } } else if comment.Type == issues_model.CommentTypeProject { - if err = comment.LoadProject(ctx); err != nil { ctx.ServerError("LoadProject", err) return @@ -1731,10 +1730,6 @@ func ViewIssue(ctx *context.Context) { for _, codeComments := range comment.Review.CodeComments { for _, lineComments := range codeComments { for _, c := range lineComments { - if err := c.LoadAttachments(ctx); err != nil { - ctx.ServerError("LoadAttachments", err) - return - } // Check tag. role, ok = marked[c.PosterID] if ok { diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index bce807aacd..5385ebfc97 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -179,11 +179,9 @@ func renderConversation(ctx *context.Context, comment *issues_model.Comment, ori return } - for _, c := range comments { - if err := c.LoadAttachments(ctx); err != nil { - ctx.ServerError("LoadAttachments", err) - return - } + if err := comments.LoadAttachments(ctx); err != nil { + ctx.ServerError("LoadAttachments", err) + return } ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled From 171d3d9a3c891d107001094b9118d93b0b00c02c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 12 Mar 2024 18:53:53 +0800 Subject: [PATCH 005/553] Use Get but not Post to get actions artifacts (#29734) --- routers/web/web.go | 2 +- web_src/js/components/RepoActionView.vue | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/web/web.go b/routers/web/web.go index 8710f6e3e5..fc1432873f 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1374,7 +1374,7 @@ func registerRoutes(m *web.Route) { }) m.Post("/cancel", reqRepoActionsWriter, actions.Cancel) m.Post("/approve", reqRepoActionsWriter, actions.Approve) - m.Post("/artifacts", actions.ArtifactsView) + m.Get("/artifacts", actions.ArtifactsView) m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView) m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView) m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index de9625b143..9641431508 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -5,7 +5,7 @@ import {createApp} from 'vue'; import {toggleElem} from '../utils/dom.js'; import {formatDatetime} from '../utils/time.js'; import {renderAnsi} from '../render/ansi.js'; -import {POST, DELETE} from '../modules/fetch.js'; +import {GET, POST, DELETE} from '../modules/fetch.js'; const sfc = { name: 'RepoActionView', @@ -196,7 +196,7 @@ const sfc = { }, async fetchArtifacts() { - const resp = await POST(`${this.actionsURL}/runs/${this.runIndex}/artifacts`); + const resp = await GET(`${this.actionsURL}/runs/${this.runIndex}/artifacts`); return await resp.json(); }, From e5e2b2fcd7e8446f99e8eb61eef9efe2790220c8 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 12 Mar 2024 19:21:09 +0800 Subject: [PATCH 006/553] Add more stats tables (#29730) Add `Tags`, `Branches` and `CommitStatus` to monitor/stats --- models/activities/statistic.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/models/activities/statistic.go b/models/activities/statistic.go index fe5f7d0872..d1a459d1b2 100644 --- a/models/activities/statistic.go +++ b/models/activities/statistic.go @@ -9,6 +9,7 @@ import ( asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" access_model "code.gitea.io/gitea/models/perm/access" @@ -29,7 +30,8 @@ type Statistic struct { Mirror, Release, AuthSource, Webhook, Milestone, Label, HookTask, Team, UpdateTask, Project, - ProjectBoard, Attachment int64 + ProjectBoard, Attachment, + Branches, Tags, CommitStatus int64 IssueByLabel []IssueByLabelCount IssueByRepository []IssueByRepositoryCount } @@ -58,6 +60,9 @@ func GetStatistic(ctx context.Context) (stats Statistic) { stats.Counter.Watch, _ = e.Count(new(repo_model.Watch)) stats.Counter.Star, _ = e.Count(new(repo_model.Star)) stats.Counter.Access, _ = e.Count(new(access_model.Access)) + stats.Counter.Branches, _ = e.Count(new(git_model.Branch)) + stats.Counter.Tags, _ = e.Where("is_draft=?", false).Count(new(repo_model.Release)) + stats.Counter.CommitStatus, _ = e.Count(new(git_model.CommitStatus)) type IssueCount struct { Count int64 From 36de5b299bb3e6e6cf28062c832ab8165e83f91a Mon Sep 17 00:00:00 2001 From: 6543 Date: Tue, 12 Mar 2024 18:32:05 +0100 Subject: [PATCH 007/553] Highlight archived labels (#29680) the issue is, that you can not distinguish between normal and archived labels. So this will make archived labels 80% **grayscale**. And prepend "Archived: " to the tooltip info ![image](https://github.com/go-gitea/gitea/assets/24977596/fd77c4d2-eff5-4afd-9bfa-19cb9991c5e7) ![image](https://github.com/go-gitea/gitea/assets/24977596/2e0f30e5-f301-4c9c-8e9f-677298d90b27) ![image](https://github.com/go-gitea/gitea/assets/24977596/53d70abf-b306-453d-aa95-a3a035b19a33) ![image](https://github.com/go-gitea/gitea/assets/24977596/6020e5f5-2364-4807-979f-37dffa8735e5) --- *Sponsored by Kithara Software GmbH* --------- Co-authored-by: delvh --- modules/templates/util_render.go | 29 +++++++++++++------ templates/repo/issue/filter_actions.tmpl | 2 +- templates/repo/issue/filter_list.tmpl | 2 +- templates/repo/issue/labels/label.tmpl | 2 +- templates/repo/issue/labels/label_list.tmpl | 4 +-- .../issue/labels/labels_selector_field.tmpl | 4 +-- .../repo/issue/view_content/comments.tmpl | 6 ++-- templates/shared/issuelist.tmpl | 2 +- web_src/css/repo.css | 4 +++ 9 files changed, 35 insertions(+), 20 deletions(-) diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index cdff31698c..ba9b050e02 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" ) @@ -118,10 +119,15 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) } // RenderLabel renders a label -func RenderLabel(ctx context.Context, label *issues_model.Label) template.HTML { - labelScope := label.ExclusiveScope() +// locale is needed due to an import cycle with our context providing the `Tr` function +func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML { + var ( + archivedCSSClass string + textColor = "#111" + isArchived = !label.ArchivedUnix.IsZero() + labelScope = label.ExclusiveScope() + ) - textColor := "#111" r, g, b := util.HexToRBGColor(label.Color) // Determine if label text should be light or dark to be readable on background color if util.UseLightTextOnBackground(r, g, b) { @@ -130,10 +136,15 @@ func RenderLabel(ctx context.Context, label *issues_model.Label) template.HTML { description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) + if isArchived { + archivedCSSClass = "archived-label" + description = fmt.Sprintf("(%s) %s", locale.TrString("archived"), description) + } + if labelScope == "" { // Regular label - s := fmt.Sprintf("
%s
", - textColor, label.Color, description, RenderEmoji(ctx, label.Name)) + s := fmt.Sprintf("
%s
", + archivedCSSClass, textColor, label.Color, description, RenderEmoji(ctx, label.Name)) return template.HTML(s) } @@ -166,11 +177,11 @@ func RenderLabel(ctx context.Context, label *issues_model.Label) template.HTML { itemColor := "#" + hex.EncodeToString(itemBytes) scopeColor := "#" + hex.EncodeToString(scopeBytes) - s := fmt.Sprintf(""+ + s := fmt.Sprintf(""+ "
%s
"+ "
%s
"+ "
", - description, + archivedCSSClass, description, textColor, scopeColor, scopeText, textColor, itemColor, itemText) return template.HTML(s) @@ -211,7 +222,7 @@ func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //n return output } -func RenderLabels(ctx context.Context, labels []*issues_model.Label, repoLink string) template.HTML { +func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string) template.HTML { htmlCode := `` for _, label := range labels { // Protect against nil value in labels - shouldn't happen but would cause a panic if so @@ -219,7 +230,7 @@ func RenderLabels(ctx context.Context, labels []*issues_model.Label, repoLink st continue } htmlCode += fmt.Sprintf("%s ", - repoLink, label.ID, RenderLabel(ctx, label)) + repoLink, label.ID, RenderLabel(ctx, locale, label)) } htmlCode += "" return template.HTML(htmlCode) diff --git a/templates/repo/issue/filter_actions.tmpl b/templates/repo/issue/filter_actions.tmpl index a2296f6597..f573b8e09e 100644 --- a/templates/repo/issue/filter_actions.tmpl +++ b/templates/repo/issue/filter_actions.tmpl @@ -30,7 +30,7 @@ {{end}} {{$previousExclusiveScope = $exclusiveScope}}
- {{if SliceUtils.Contains $.SelLabelIDs .ID}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel $.Context .}} + {{if SliceUtils.Contains $.SelLabelIDs .ID}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel $.Context ctx.Locale .}} {{template "repo/issue/labels/label_archived" .}}
{{end}} diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl index 9d3341cc81..d0086fdf8c 100644 --- a/templates/repo/issue/filter_list.tmpl +++ b/templates/repo/issue/filter_list.tmpl @@ -42,7 +42,7 @@ {{svg "octicon-check"}} {{end}} {{end}} - {{RenderLabel $.Context .}} + {{RenderLabel $.Context ctx.Locale .}}

{{template "repo/issue/labels/label_archived" .}}

{{end}} diff --git a/templates/repo/issue/labels/label.tmpl b/templates/repo/issue/labels/label.tmpl index 3ecae09373..d20d5e63d7 100644 --- a/templates/repo/issue/labels/label.tmpl +++ b/templates/repo/issue/labels/label.tmpl @@ -3,5 +3,5 @@ id="label_{{.label.ID}}" href="{{.root.RepoLink}}/{{if or .root.IsPull .root.Issue.IsPull}}pulls{{else}}issues{{end}}?labels={{.label.ID}}"{{/* FIXME: use .root.Issue.Link or create .root.Link */}} > - {{- RenderLabel $.Context .label -}} + {{- RenderLabel $.Context ctx.Locale .label -}} diff --git a/templates/repo/issue/labels/label_list.tmpl b/templates/repo/issue/labels/label_list.tmpl index 9b0061b60e..428a4919aa 100644 --- a/templates/repo/issue/labels/label_list.tmpl +++ b/templates/repo/issue/labels/label_list.tmpl @@ -32,7 +32,7 @@ {{range .Labels}}
  • - {{RenderLabel $.Context .}} + {{RenderLabel $.Context ctx.Locale .}} {{if .Description}}
    {{.Description | RenderEmoji $.Context}}{{end}}
    @@ -72,7 +72,7 @@ {{range .OrgLabels}}
  • - {{RenderLabel $.Context .}} + {{RenderLabel $.Context ctx.Locale .}} {{if .Description}}
    {{.Description | RenderEmoji $.Context}}{{end}}
    diff --git a/templates/repo/issue/labels/labels_selector_field.tmpl b/templates/repo/issue/labels/labels_selector_field.tmpl index e42a1de895..a9c33bc4eb 100644 --- a/templates/repo/issue/labels/labels_selector_field.tmpl +++ b/templates/repo/issue/labels/labels_selector_field.tmpl @@ -21,7 +21,7 @@
    {{end}} {{$previousExclusiveScope = $exclusiveScope}} - {{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel $.Context .}} + {{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel $.Context ctx.Locale .}} {{if .Description}}
    {{.Description | RenderEmoji $.Context}}{{end}}

    {{template "repo/issue/labels/label_archived" .}}

    @@ -34,7 +34,7 @@
    {{end}} {{$previousExclusiveScope = $exclusiveScope}} - {{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel $.Context .}} + {{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel $.Context ctx.Locale .}} {{if .Description}}
    {{.Description | RenderEmoji $.Context}}{{end}}

    {{template "repo/issue/labels/label_archived" .}}

    diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 8bbcb1a54a..db63e2b951 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -173,11 +173,11 @@ {{template "shared/user/authorlink" .Poster}} {{if and .AddedLabels (not .RemovedLabels)}} - {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context .AddedLabels $.RepoLink) $createdStr}} + {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) $createdStr}} {{else if and (not .AddedLabels) .RemovedLabels}} - {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context .RemovedLabels $.RepoLink) $createdStr}} + {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context .AddedLabels $.RepoLink) (RenderLabels $.Context .RemovedLabels $.RepoLink) $createdStr}} + {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}} {{end}}
    diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index a90188297f..cffe579741 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -21,7 +21,7 @@ {{end}} {{range .Labels}} - {{RenderLabel $.Context .}} + {{RenderLabel $.Context ctx.Locale .}} {{end}} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 03d9664331..587a3152e5 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2417,6 +2417,10 @@ gap: 0 !important; } +.archived-label { + filter: grayscale(0.8); +} + .ui.label.scope-left { border-bottom-right-radius: 0; border-top-right-radius: 0; From 3e7ae79f99ef0e5ba3d1201c38f491121ea2a156 Mon Sep 17 00:00:00 2001 From: JakobDev Date: Tue, 12 Mar 2024 22:40:43 +0100 Subject: [PATCH 008/553] Update Chroma to v2.13.0 (#29732) This adds new lexers and includes some fixes. See https://github.com/alecthomas/chroma/releases/tag/v2.13.0 for the full changelog. --------- Co-authored-by: Giteabot --- go.mod | 4 ++-- go.sum | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 9b70a191ce..97f429aadb 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/NYTimes/gziphandler v1.1.1 github.com/PuerkitoBio/goquery v1.8.1 - github.com/alecthomas/chroma/v2 v2.12.0 + github.com/alecthomas/chroma/v2 v2.13.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.3.10 github.com/bufbuild/connect-go v1.10.0 @@ -172,7 +172,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dlclark/regexp2 v1.10.0 // indirect + github.com/dlclark/regexp2 v1.11.0 // indirect github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect diff --git a/go.sum b/go.sum index a44809dde5..916378d759 100644 --- a/go.sum +++ b/go.sum @@ -104,14 +104,14 @@ github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06 github.com/RoaringBitmap/roaring v0.7.1/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I= github.com/RoaringBitmap/roaring v1.7.0 h1:OZF303tJCER1Tj3x+aArx/S5X7hrT186ri6JjrGvG68= github.com/RoaringBitmap/roaring v1.7.0/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= -github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= -github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= +github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= -github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw= -github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw= +github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI= +github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= -github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= -github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= @@ -260,8 +260,8 @@ github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmW github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= -github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= From 225fc405283a21c9ef966aa0bf8dabfe687804a8 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Tue, 12 Mar 2024 23:09:02 +0100 Subject: [PATCH 009/553] Update to labeler v5 (#29721) Updated to actions/labeler@v5 Updated labeler config accordingly, also improved the config and added more labels. --------- Co-authored-by: Giteabot --- .github/labeler.yml | 90 +++++++++++++++++++++++------- .github/workflows/pull-labeler.yml | 6 +- 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 8a5ab26975..a1209c77b8 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,36 +1,84 @@ modifies/docs: - - "**/*.md" - - "docs/**" + - changed-files: + - any-glob-to-any-file: + - "**/*.md" + - "docs/**" modifies/frontend: - - "web_src/**/*" + - changed-files: + - any-glob-to-any-file: + - "web_src/**" + - "tailwind.config.js" + - "webpack.config.js" modifies/templates: - - all: ["templates/**", "!templates/swagger/v1_json.tmpl"] + - changed-files: + - all-globs-to-any-file: + - "templates/**" + - "!templates/swagger/v1_json.tmpl" modifies/api: - - "routers/api/**" - - "templates/swagger/v1_json.tmpl" + - changed-files: + - any-glob-to-any-file: + - "routers/api/**" + - "templates/swagger/v1_json.tmpl" modifies/cli: - - "cmd/**" + - changed-files: + - any-glob-to-any-file: + - "cmd/**" modifies/translation: - - "options/locale/*.ini" + - changed-files: + - any-glob-to-any-file: + - "options/locale/*.ini" modifies/migrations: - - "models/migrations/**/*" + - changed-files: + - any-glob-to-any-file: + - "models/migrations/**" modifies/internal: - - "Makefile" - - "Dockerfile" - - "Dockerfile.rootless" - - "docker/**" - - "webpack.config.js" - - ".eslintrc.yaml" - - ".golangci.yml" - - ".markdownlint.yaml" - - ".spectral.yaml" - - ".stylelintrc.yaml" - - ".yamllint.yaml" - - ".github/**" + - changed-files: + - any-glob-to-any-file: + - ".air.toml" + - "Makefile" + - "Dockerfile" + - "Dockerfile.rootless" + - ".dockerignore" + - "docker/**" + - ".editorconfig" + - ".eslintrc.yaml" + - ".golangci.yml" + - ".gitpod.yml" + - ".markdownlint.yaml" + - ".spectral.yaml" + - ".stylelintrc.yaml" + - ".yamllint.yaml" + - ".github/**" + - ".gitea/" + - ".devcontainer/**" + - "build.go" + - "build/**" + - "contrib/**" + +modifies/dependencies: + - changed-files: + - any-glob-to-any-file: + - "package.json" + - "package-lock.json" + - "poetry.toml" + - "poetry.lock" + - "go.mod" + - "go.sum" + - "pyproject.toml" + +modifies/go: + - changed-files: + - any-glob-to-any-file: + - "**/*.go" + +modifies/js: + - changed-files: + - any-glob-to-any-file: + - "**/*.js" diff --git a/.github/workflows/pull-labeler.yml b/.github/workflows/pull-labeler.yml index edd2f6d16e..812819b599 100644 --- a/.github/workflows/pull-labeler.yml +++ b/.github/workflows/pull-labeler.yml @@ -9,12 +9,12 @@ concurrency: cancel-in-progress: true jobs: - label: + labeler: runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: - - uses: actions/labeler@v4 + - uses: actions/labeler@v5 with: - dot: true + sync-labels: true From 857243bed7f9dccdc597c2941df41e821781cb0f Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 12 Mar 2024 23:37:02 +0100 Subject: [PATCH 010/553] Fix date rendering by adding `` (#29725) Alternative to: https://github.com/go-gitea/gitea/pull/29698 Fixes: https://github.com/go-gitea/gitea/issues/29034 image It also fixes a secondary issue that we were showing timestamp tooltips over date, which makes no sense, so these are now gone as well: image --- modules/timeutil/datetime.go | 16 ++++---- modules/timeutil/datetime_test.go | 14 ++++--- templates/devtest/gitea-ui.tmpl | 10 +++++ web_src/js/webcomponents/GiteaAbsoluteDate.js | 40 +++++++++++++++++++ web_src/js/webcomponents/webcomponents.js | 1 + 5 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 web_src/js/webcomponents/GiteaAbsoluteDate.js diff --git a/modules/timeutil/datetime.go b/modules/timeutil/datetime.go index 62b94f7cf4..50c8d44f13 100644 --- a/modules/timeutil/datetime.go +++ b/modules/timeutil/datetime.go @@ -51,18 +51,16 @@ func DateTime(format string, datetime any, extraAttrs ...string) template.HTML { attrs := make([]string, 0, 10+len(extraAttrs)) attrs = append(attrs, extraAttrs...) - attrs = append(attrs, `data-tooltip-content`, `data-tooltip-interactive="true"`) - attrs = append(attrs, `format="datetime"`, `weekday=""`, `year="numeric"`) + attrs = append(attrs, `weekday=""`, `year="numeric"`) switch format { - case "short": - attrs = append(attrs, `month="short"`, `day="numeric"`) - case "long": - attrs = append(attrs, `month="long"`, `day="numeric"`) - case "full": - attrs = append(attrs, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`) + case "short", "long": // date only + attrs = append(attrs, `month="`+format+`"`, `day="numeric"`) + return template.HTML(fmt.Sprintf(`%s`, strings.Join(attrs, " "), datetimeEscaped, textEscaped)) + case "full": // full date including time + attrs = append(attrs, `format="datetime"`, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`, `data-tooltip-content`, `data-tooltip-interactive="true"`) + return template.HTML(fmt.Sprintf(`%s`, strings.Join(attrs, " "), datetimeEscaped, textEscaped)) default: panic(fmt.Sprintf("Unsupported format %s", format)) } - return template.HTML(fmt.Sprintf(`%s`, strings.Join(attrs, " "), datetimeEscaped, textEscaped)) } diff --git a/modules/timeutil/datetime_test.go b/modules/timeutil/datetime_test.go index 26494b8475..39aecbc43b 100644 --- a/modules/timeutil/datetime_test.go +++ b/modules/timeutil/datetime_test.go @@ -18,6 +18,7 @@ func TestDateTime(t *testing.T) { defer test.MockVariableValue(&setting.DefaultUILocation, testTz)() refTimeStr := "2018-01-01T00:00:00Z" + refDateStr := "2018-01-01" refTime, _ := time.Parse(time.RFC3339, refTimeStr) refTimeStamp := TimeStamp(refTime.Unix()) @@ -27,17 +28,20 @@ func TestDateTime(t *testing.T) { assert.EqualValues(t, "-", DateTime("short", TimeStamp(0))) actual := DateTime("short", "invalid") - assert.EqualValues(t, `invalid`, actual) + assert.EqualValues(t, `invalid`, actual) actual = DateTime("short", refTimeStr) - assert.EqualValues(t, `2018-01-01T00:00:00Z`, actual) + assert.EqualValues(t, `2018-01-01T00:00:00Z`, actual) actual = DateTime("short", refTime) - assert.EqualValues(t, `2018-01-01`, actual) + assert.EqualValues(t, `2018-01-01`, actual) + + actual = DateTime("short", refDateStr) + assert.EqualValues(t, `2018-01-01`, actual) actual = DateTime("short", refTimeStamp) - assert.EqualValues(t, `2017-12-31`, actual) + assert.EqualValues(t, `2017-12-31`, actual) actual = DateTime("full", refTimeStamp) - assert.EqualValues(t, `2017-12-31 19:00:00 -05:00`, actual) + assert.EqualValues(t, `2017-12-31 19:00:00 -05:00`, actual) } diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl index ccf188609c..e551572b96 100644 --- a/templates/devtest/gitea-ui.tmpl +++ b/templates/devtest/gitea-ui.tmpl @@ -110,6 +110,16 @@
    +
    +

    GiteaAbsoluteDate

    +
    +
    +
    +
    +
    +
    relative-time:
    +
    +

    LocaleNumber

    {{ctx.Locale.PrettyNumber 1}}
    diff --git a/web_src/js/webcomponents/GiteaAbsoluteDate.js b/web_src/js/webcomponents/GiteaAbsoluteDate.js new file mode 100644 index 0000000000..660aa99d07 --- /dev/null +++ b/web_src/js/webcomponents/GiteaAbsoluteDate.js @@ -0,0 +1,40 @@ +window.customElements.define('gitea-absolute-date', class extends HTMLElement { + static observedAttributes = ['date', 'year', 'month', 'weekday', 'day']; + + update = () => { + const year = this.getAttribute('year') ?? ''; + const month = this.getAttribute('month') ?? ''; + const weekday = this.getAttribute('weekday') ?? ''; + const day = this.getAttribute('day') ?? ''; + const lang = this.closest('[lang]')?.getAttribute('lang') || + this.ownerDocument.documentElement.getAttribute('lang') || + ''; + + // only extract the `yyyy-mm-dd` part. When converting to Date, it will become midnight UTC and when rendered + // as localized date, will have a offset towards UTC, which we remove to shift the timestamp to midnight in the + // localized date. We should eventually use `Temporal.PlainDate` which will make the correction unnecessary. + // - https://stackoverflow.com/a/14569783/808699 + // - https://tc39.es/proposal-temporal/docs/plaindate.html + const date = new Date(this.getAttribute('date').substring(0, 10)); + const correctedDate = new Date(date.getTime() - date.getTimezoneOffset() * -60000); + + if (!this.shadowRoot) this.attachShadow({mode: 'open'}); + this.shadowRoot.textContent = correctedDate.toLocaleString(lang ?? [], { + ...(year && {year}), + ...(month && {month}), + ...(weekday && {weekday}), + ...(day && {day}), + }); + }; + + attributeChangedCallback(_name, oldValue, newValue) { + if (!this.initialized || oldValue === newValue) return; + this.update(); + } + + connectedCallback() { + this.initialized = false; + this.update(); + this.initialized = true; + } +}); diff --git a/web_src/js/webcomponents/webcomponents.js b/web_src/js/webcomponents/webcomponents.js index 916a588db6..03348d895f 100644 --- a/web_src/js/webcomponents/webcomponents.js +++ b/web_src/js/webcomponents/webcomponents.js @@ -3,3 +3,4 @@ import './polyfill.js'; import '@github/relative-time-element'; import './GiteaOriginUrl.js'; +import './GiteaAbsoluteDate.js'; From 9a93b1816e0bc65101e7ad7ca66786fb38a8e628 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 13 Mar 2024 07:04:07 +0100 Subject: [PATCH 011/553] Refactor label.IsArchived() (#29750) just some missed nits --- models/issues/label.go | 12 ++++++------ modules/templates/util_render.go | 3 +-- routers/web/repo/issue_label.go | 12 +++++------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/models/issues/label.go b/models/issues/label.go index f6ecc68cd1..2397a29e35 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -116,12 +116,17 @@ func (l *Label) CalOpenIssues() { func (l *Label) SetArchived(isArchived bool) { if !isArchived { l.ArchivedUnix = timeutil.TimeStamp(0) - } else if isArchived && l.ArchivedUnix.IsZero() { + } else if isArchived && !l.IsArchived() { // Only change the date when it is newly archived. l.ArchivedUnix = timeutil.TimeStampNow() } } +// IsArchived returns true if label is an archived +func (l *Label) IsArchived() bool { + return !l.ArchivedUnix.IsZero() +} + // CalOpenOrgIssues calculates the open issues of a label for a specific repo func (l *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) { counts, _ := CountIssuesByRepo(ctx, &IssuesOptions{ @@ -166,11 +171,6 @@ func (l *Label) BelongsToOrg() bool { return l.OrgID > 0 } -// IsArchived returns true if label is an archived -func (l *Label) IsArchived() bool { - return l.ArchivedUnix > 0 -} - // BelongsToRepo returns true if label is a repository label func (l *Label) BelongsToRepo() bool { return l.RepoID > 0 diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index ba9b050e02..7ed3a8b9b4 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -124,7 +124,6 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m var ( archivedCSSClass string textColor = "#111" - isArchived = !label.ArchivedUnix.IsZero() labelScope = label.ExclusiveScope() ) @@ -136,7 +135,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) - if isArchived { + if label.IsArchived() { archivedCSSClass = "archived-label" description = fmt.Sprintf("(%s) %s", locale.TrString("archived"), description) } diff --git a/routers/web/repo/issue_label.go b/routers/web/repo/issue_label.go index 9dedaefa4b..81bee4dbb5 100644 --- a/routers/web/repo/issue_label.go +++ b/routers/web/repo/issue_label.go @@ -13,7 +13,6 @@ import ( "code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -112,12 +111,11 @@ func NewLabel(ctx *context.Context) { } l := &issues_model.Label{ - RepoID: ctx.Repo.Repository.ID, - Name: form.Title, - Exclusive: form.Exclusive, - Description: form.Description, - Color: form.Color, - ArchivedUnix: timeutil.TimeStamp(0), + RepoID: ctx.Repo.Repository.ID, + Name: form.Title, + Exclusive: form.Exclusive, + Description: form.Description, + Color: form.Color, } if err := issues_model.NewLabel(ctx, l); err != nil { ctx.ServerError("NewLabel", err) From 67e9f0d49828f62a942ac6db04153207f88e82ee Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 13 Mar 2024 14:57:30 +0800 Subject: [PATCH 012/553] Fix user router possbile panic (#29751) regression from #28023 --- routers/web/user/home.go | 7 +++++-- tests/integration/user_test.go | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/routers/web/user/home.go b/routers/web/user/home.go index caa7115259..e731a2a9b7 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -714,12 +714,16 @@ func UsernameSubRoute(ctx *context.Context) { reloadParam := func(suffix string) (success bool) { ctx.SetParams("username", strings.TrimSuffix(username, suffix)) context.UserAssignmentWeb()(ctx) + if ctx.Written() { + return false + } + // check view permissions if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) { ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name)) return false } - return !ctx.Written() + return true } switch { case strings.HasSuffix(username, ".png"): @@ -740,7 +744,6 @@ func UsernameSubRoute(ctx *context.Context) { return } if reloadParam(".rss") { - context.UserAssignmentWeb()(ctx) feed.ShowUserFeedRSS(ctx) } case strings.HasSuffix(username, ".atom"): diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go index c30733b1b0..c4544f37aa 100644 --- a/tests/integration/user_test.go +++ b/tests/integration/user_test.go @@ -243,6 +243,8 @@ func testExportUserGPGKeys(t *testing.T, user, expected string) { } func TestGetUserRss(t *testing.T) { + defer tests.PrepareTestEnv(t)() + user34 := "the_34-user.with.all.allowedChars" req := NewRequestf(t, "GET", "/%s.rss", user34) resp := MakeRequest(t, req, http.StatusOK) @@ -253,6 +255,13 @@ func TestGetUserRss(t *testing.T) { description, _ := rssDoc.ChildrenFiltered("description").Html() assert.EqualValues(t, "<p dir="auto">some <a href="https://commonmark.org/" rel="nofollow">commonmark</a>!</p>\n", description) } + + req = NewRequestf(t, "GET", "/non-existent-user.rss") + MakeRequest(t, req, http.StatusNotFound) + + session := loginUser(t, "user2") + req = NewRequestf(t, "GET", "/non-existent-user.rss") + session.MakeRequest(t, req, http.StatusNotFound) } func TestListStopWatches(t *testing.T) { From 7fd0a5b276aadcf88dcc012fcd364fe160a58810 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 13 Mar 2024 09:25:53 +0100 Subject: [PATCH 013/553] Refactor to use optional.Option for issue index search option (#29739) Signed-off-by: 6543 <6543@obermui.de> --- modules/indexer/internal/bleve/query.go | 12 ++-- modules/indexer/issues/bleve/bleve.go | 39 ++++++----- modules/indexer/issues/db/options.go | 32 ++++----- modules/indexer/issues/dboptions.go | 12 ++-- .../issues/elasticsearch/elasticsearch.go | 42 ++++++------ modules/indexer/issues/indexer_test.go | 65 ++++++++----------- modules/indexer/issues/internal/model.go | 20 +++--- .../indexer/issues/internal/tests/tests.go | 65 ++++--------------- .../indexer/issues/meilisearch/meilisearch.go | 40 ++++++------ routers/api/v1/repo/issue.go | 24 +++---- routers/web/repo/issue.go | 32 ++++----- routers/web/user/home.go | 20 +++--- 12 files changed, 178 insertions(+), 225 deletions(-) diff --git a/modules/indexer/internal/bleve/query.go b/modules/indexer/internal/bleve/query.go index 2a427c4020..b96875343e 100644 --- a/modules/indexer/internal/bleve/query.go +++ b/modules/indexer/internal/bleve/query.go @@ -4,6 +4,8 @@ package bleve import ( + "code.gitea.io/gitea/modules/optional" + "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/search/query" ) @@ -39,18 +41,18 @@ func BoolFieldQuery(value bool, field string) *query.BoolFieldQuery { return q } -func NumericRangeInclusiveQuery(min, max *int64, field string) *query.NumericRangeQuery { +func NumericRangeInclusiveQuery(min, max optional.Option[int64], field string) *query.NumericRangeQuery { var minF, maxF *float64 var minI, maxI *bool - if min != nil { + if min.Has() { minF = new(float64) - *minF = float64(*min) + *minF = float64(min.Value()) minI = new(bool) *minI = true } - if max != nil { + if max.Has() { maxF = new(float64) - *maxF = float64(*max) + *maxF = float64(max.Value()) maxI = new(bool) *maxI = true } diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index aaea854efa..927ad58cd4 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -224,38 +224,41 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( queries = append(queries, bleve.NewDisjunctionQuery(milestoneQueries...)) } - if options.ProjectID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ProjectID, "project_id")) + if options.ProjectID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectID.Value(), "project_id")) } - if options.ProjectBoardID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ProjectBoardID, "project_board_id")) + if options.ProjectBoardID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectBoardID.Value(), "project_board_id")) } - if options.PosterID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.PosterID, "poster_id")) + if options.PosterID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.PosterID.Value(), "poster_id")) } - if options.AssigneeID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.AssigneeID, "assignee_id")) + if options.AssigneeID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.AssigneeID.Value(), "assignee_id")) } - if options.MentionID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.MentionID, "mention_ids")) + if options.MentionID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.MentionID.Value(), "mention_ids")) } - if options.ReviewedID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ReviewedID, "reviewed_ids")) + if options.ReviewedID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ReviewedID.Value(), "reviewed_ids")) } - if options.ReviewRequestedID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ReviewRequestedID, "review_requested_ids")) + if options.ReviewRequestedID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ReviewRequestedID.Value(), "review_requested_ids")) } - if options.SubscriberID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.SubscriberID, "subscriber_ids")) + if options.SubscriberID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.SubscriberID.Value(), "subscriber_ids")) } - if options.UpdatedAfterUnix != nil || options.UpdatedBeforeUnix != nil { - queries = append(queries, inner_bleve.NumericRangeInclusiveQuery(options.UpdatedAfterUnix, options.UpdatedBeforeUnix, "updated_unix")) + if options.UpdatedAfterUnix.Has() || options.UpdatedBeforeUnix.Has() { + queries = append(queries, inner_bleve.NumericRangeInclusiveQuery( + options.UpdatedAfterUnix, + options.UpdatedBeforeUnix, + "updated_unix")) } var indexerQuery query.Query = bleve.NewConjunctionQuery(queries...) diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go index 69146573a8..eeaf1696ad 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -15,22 +15,6 @@ import ( ) func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_model.IssuesOptions, error) { - // See the comment of issues_model.SearchOptions for the reason why we need to convert - convertID := func(id *int64) int64 { - if id == nil { - return 0 - } - if *id == 0 { - return db.NoConditionID - } - return *id - } - convertInt64 := func(i *int64) int64 { - if i == nil { - return 0 - } - return *i - } var sortType string switch options.SortBy { case internal.SortByCreatedAsc: @@ -53,6 +37,18 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m sortType = "newest" } + // See the comment of issues_model.SearchOptions for the reason why we need to convert + convertID := func(id optional.Option[int64]) int64 { + if !id.Has() { + return 0 + } + value := id.Value() + if value == 0 { + return db.NoConditionID + } + return value + } + opts := &issue_model.IssuesOptions{ Paginator: options.Paginator, RepoIDs: options.RepoIDs, @@ -73,8 +69,8 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m IncludeMilestones: nil, SortType: sortType, IssueIDs: nil, - UpdatedAfterUnix: convertInt64(options.UpdatedAfterUnix), - UpdatedBeforeUnix: convertInt64(options.UpdatedBeforeUnix), + UpdatedAfterUnix: options.UpdatedAfterUnix.Value(), + UpdatedBeforeUnix: options.UpdatedBeforeUnix.Value(), PriorityRepoID: 0, IsArchived: optional.None[bool](), Org: nil, diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go index 80e233e29a..4a98b4588a 100644 --- a/modules/indexer/issues/dboptions.go +++ b/modules/indexer/issues/dboptions.go @@ -6,6 +6,7 @@ package issues import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/optional" ) func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOptions { @@ -38,13 +39,12 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp } // See the comment of issues_model.SearchOptions for the reason why we need to convert - convertID := func(id int64) *int64 { + convertID := func(id int64) optional.Option[int64] { if id > 0 { - return &id + return optional.Some(id) } if id == db.NoConditionID { - var zero int64 - return &zero + return optional.None[int64]() } return nil } @@ -59,10 +59,10 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp searchOpt.SubscriberID = convertID(opts.SubscriberID) if opts.UpdatedAfterUnix > 0 { - searchOpt.UpdatedAfterUnix = &opts.UpdatedAfterUnix + searchOpt.UpdatedAfterUnix = optional.Some(opts.UpdatedAfterUnix) } if opts.UpdatedBeforeUnix > 0 { - searchOpt.UpdatedBeforeUnix = &opts.UpdatedBeforeUnix + searchOpt.UpdatedBeforeUnix = optional.Some(opts.UpdatedBeforeUnix) } searchOpt.Paginator = opts.Paginator diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index 0077da263a..53b383c8d5 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -195,43 +195,43 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query.Must(elastic.NewTermsQuery("milestone_id", toAnySlice(options.MilestoneIDs)...)) } - if options.ProjectID != nil { - query.Must(elastic.NewTermQuery("project_id", *options.ProjectID)) + if options.ProjectID.Has() { + query.Must(elastic.NewTermQuery("project_id", options.ProjectID.Value())) } - if options.ProjectBoardID != nil { - query.Must(elastic.NewTermQuery("project_board_id", *options.ProjectBoardID)) + if options.ProjectBoardID.Has() { + query.Must(elastic.NewTermQuery("project_board_id", options.ProjectBoardID.Value())) } - if options.PosterID != nil { - query.Must(elastic.NewTermQuery("poster_id", *options.PosterID)) + if options.PosterID.Has() { + query.Must(elastic.NewTermQuery("poster_id", options.PosterID.Value())) } - if options.AssigneeID != nil { - query.Must(elastic.NewTermQuery("assignee_id", *options.AssigneeID)) + if options.AssigneeID.Has() { + query.Must(elastic.NewTermQuery("assignee_id", options.AssigneeID.Value())) } - if options.MentionID != nil { - query.Must(elastic.NewTermQuery("mention_ids", *options.MentionID)) + if options.MentionID.Has() { + query.Must(elastic.NewTermQuery("mention_ids", options.MentionID.Value())) } - if options.ReviewedID != nil { - query.Must(elastic.NewTermQuery("reviewed_ids", *options.ReviewedID)) + if options.ReviewedID.Has() { + query.Must(elastic.NewTermQuery("reviewed_ids", options.ReviewedID.Value())) } - if options.ReviewRequestedID != nil { - query.Must(elastic.NewTermQuery("review_requested_ids", *options.ReviewRequestedID)) + if options.ReviewRequestedID.Has() { + query.Must(elastic.NewTermQuery("review_requested_ids", options.ReviewRequestedID.Value())) } - if options.SubscriberID != nil { - query.Must(elastic.NewTermQuery("subscriber_ids", *options.SubscriberID)) + if options.SubscriberID.Has() { + query.Must(elastic.NewTermQuery("subscriber_ids", options.SubscriberID.Value())) } - if options.UpdatedAfterUnix != nil || options.UpdatedBeforeUnix != nil { + if options.UpdatedAfterUnix.Has() || options.UpdatedBeforeUnix.Has() { q := elastic.NewRangeQuery("updated_unix") - if options.UpdatedAfterUnix != nil { - q.Gte(*options.UpdatedAfterUnix) + if options.UpdatedAfterUnix.Has() { + q.Gte(options.UpdatedAfterUnix.Value()) } - if options.UpdatedBeforeUnix != nil { - q.Lte(*options.UpdatedBeforeUnix) + if options.UpdatedBeforeUnix.Has() { + q.Lte(options.UpdatedBeforeUnix.Value()) } query.Must(q) } diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index 10ffa7cbe6..0d0cfc8516 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -134,63 +134,60 @@ func searchIssueInRepo(t *testing.T) { } func searchIssueByID(t *testing.T) { - int64Pointer := func(x int64) *int64 { - return &x - } tests := []struct { opts SearchOptions expectedIDs []int64 }{ { - SearchOptions{ - PosterID: int64Pointer(1), + opts: SearchOptions{ + PosterID: optional.Some(int64(1)), }, - []int64{11, 6, 3, 2, 1}, + expectedIDs: []int64{11, 6, 3, 2, 1}, }, { - SearchOptions{ - AssigneeID: int64Pointer(1), + opts: SearchOptions{ + AssigneeID: optional.Some(int64(1)), }, - []int64{6, 1}, + expectedIDs: []int64{6, 1}, }, { - SearchOptions{ - MentionID: int64Pointer(4), + opts: SearchOptions{ + MentionID: optional.Some(int64(4)), }, - []int64{1}, + expectedIDs: []int64{1}, }, { - SearchOptions{ - ReviewedID: int64Pointer(1), + opts: SearchOptions{ + ReviewedID: optional.Some(int64(1)), }, - []int64{}, + expectedIDs: []int64{}, }, { - SearchOptions{ - ReviewRequestedID: int64Pointer(1), + opts: SearchOptions{ + ReviewRequestedID: optional.Some(int64(1)), }, - []int64{12}, + expectedIDs: []int64{12}, }, { - SearchOptions{ - SubscriberID: int64Pointer(1), + opts: SearchOptions{ + SubscriberID: optional.Some(int64(1)), }, - []int64{11, 6, 5, 3, 2, 1}, + expectedIDs: []int64{11, 6, 5, 3, 2, 1}, }, { // issue 20 request user 15 and team 5 which user 15 belongs to // the review request number of issue 20 should be 1 - SearchOptions{ - ReviewRequestedID: int64Pointer(15), + opts: SearchOptions{ + ReviewRequestedID: optional.Some(int64(15)), }, - []int64{12, 20}, + expectedIDs: []int64{12, 20}, }, { // user 20 approved the issue 20, so return nothing - SearchOptions{ - ReviewRequestedID: int64Pointer(20), + opts: SearchOptions{ + ReviewRequestedID: optional.Some(int64(20)), }, - []int64{}, + expectedIDs: []int64{}, }, } @@ -318,16 +315,13 @@ func searchIssueByLabelID(t *testing.T) { } func searchIssueByTime(t *testing.T) { - int64Pointer := func(i int64) *int64 { - return &i - } tests := []struct { opts SearchOptions expectedIDs []int64 }{ { SearchOptions{ - UpdatedAfterUnix: int64Pointer(0), + UpdatedAfterUnix: optional.Some(int64(0)), }, []int64{22, 21, 17, 16, 15, 14, 13, 12, 11, 20, 6, 5, 19, 18, 10, 7, 4, 9, 8, 3, 2, 1}, }, @@ -363,28 +357,25 @@ func searchIssueWithOrder(t *testing.T) { } func searchIssueInProject(t *testing.T) { - int64Pointer := func(i int64) *int64 { - return &i - } tests := []struct { opts SearchOptions expectedIDs []int64 }{ { SearchOptions{ - ProjectID: int64Pointer(1), + ProjectID: optional.Some(int64(1)), }, []int64{5, 3, 2, 1}, }, { SearchOptions{ - ProjectBoardID: int64Pointer(1), + ProjectBoardID: optional.Some(int64(1)), }, []int64{1}, }, { SearchOptions{ - ProjectBoardID: int64Pointer(0), // issue with in default board + ProjectBoardID: optional.Some(int64(0)), // issue with in default board }, []int64{2}, }, diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index d41fec4aba..b7102c35af 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -89,22 +89,22 @@ type SearchOptions struct { MilestoneIDs []int64 // milestones the issues have - ProjectID *int64 // project the issues belong to - ProjectBoardID *int64 // project board the issues belong to + ProjectID optional.Option[int64] // project the issues belong to + ProjectBoardID optional.Option[int64] // project board the issues belong to - PosterID *int64 // poster of the issues + PosterID optional.Option[int64] // poster of the issues - AssigneeID *int64 // assignee of the issues, zero means no assignee + AssigneeID optional.Option[int64] // assignee of the issues, zero means no assignee - MentionID *int64 // mentioned user of the issues + MentionID optional.Option[int64] // mentioned user of the issues - ReviewedID *int64 // reviewer of the issues - ReviewRequestedID *int64 // requested reviewer of the issues + ReviewedID optional.Option[int64] // reviewer of the issues + ReviewRequestedID optional.Option[int64] // requested reviewer of the issues - SubscriberID *int64 // subscriber of the issues + SubscriberID optional.Option[int64] // subscriber of the issues - UpdatedAfterUnix *int64 - UpdatedBeforeUnix *int64 + UpdatedAfterUnix optional.Option[int64] + UpdatedBeforeUnix optional.Option[int64] db.Paginator diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index 6724471539..91aafd589c 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -300,10 +300,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ProjectID: func() *int64 { - id := int64(1) - return &id - }(), + ProjectID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -321,10 +318,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ProjectID: func() *int64 { - id := int64(0) - return &id - }(), + ProjectID: optional.Some(int64(0)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -342,10 +336,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ProjectBoardID: func() *int64 { - id := int64(1) - return &id - }(), + ProjectBoardID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -363,10 +354,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ProjectBoardID: func() *int64 { - id := int64(0) - return &id - }(), + ProjectBoardID: optional.Some(int64(0)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -384,10 +372,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - PosterID: func() *int64 { - id := int64(1) - return &id - }(), + PosterID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -405,10 +390,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - AssigneeID: func() *int64 { - id := int64(1) - return &id - }(), + AssigneeID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -426,10 +408,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - AssigneeID: func() *int64 { - id := int64(0) - return &id - }(), + AssigneeID: optional.Some(int64(0)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -447,10 +426,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - MentionID: func() *int64 { - id := int64(1) - return &id - }(), + MentionID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -468,10 +444,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ReviewedID: func() *int64 { - id := int64(1) - return &id - }(), + ReviewedID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -489,10 +462,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ReviewRequestedID: func() *int64 { - id := int64(1) - return &id - }(), + ReviewRequestedID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -510,10 +480,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - SubscriberID: func() *int64 { - id := int64(1) - return &id - }(), + SubscriberID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -531,14 +498,8 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - UpdatedAfterUnix: func() *int64 { - var t int64 = 20 - return &t - }(), - UpdatedBeforeUnix: func() *int64 { - var t int64 = 30 - return &t - }(), + UpdatedAfterUnix: optional.Some(int64(20)), + UpdatedBeforeUnix: optional.Some(int64(30)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index c429920065..34066bf559 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -170,41 +170,41 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query.And(inner_meilisearch.NewFilterIn("milestone_id", options.MilestoneIDs...)) } - if options.ProjectID != nil { - query.And(inner_meilisearch.NewFilterEq("project_id", *options.ProjectID)) + if options.ProjectID.Has() { + query.And(inner_meilisearch.NewFilterEq("project_id", options.ProjectID.Value())) } - if options.ProjectBoardID != nil { - query.And(inner_meilisearch.NewFilterEq("project_board_id", *options.ProjectBoardID)) + if options.ProjectBoardID.Has() { + query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectBoardID.Value())) } - if options.PosterID != nil { - query.And(inner_meilisearch.NewFilterEq("poster_id", *options.PosterID)) + if options.PosterID.Has() { + query.And(inner_meilisearch.NewFilterEq("poster_id", options.PosterID.Value())) } - if options.AssigneeID != nil { - query.And(inner_meilisearch.NewFilterEq("assignee_id", *options.AssigneeID)) + if options.AssigneeID.Has() { + query.And(inner_meilisearch.NewFilterEq("assignee_id", options.AssigneeID.Value())) } - if options.MentionID != nil { - query.And(inner_meilisearch.NewFilterEq("mention_ids", *options.MentionID)) + if options.MentionID.Has() { + query.And(inner_meilisearch.NewFilterEq("mention_ids", options.MentionID.Value())) } - if options.ReviewedID != nil { - query.And(inner_meilisearch.NewFilterEq("reviewed_ids", *options.ReviewedID)) + if options.ReviewedID.Has() { + query.And(inner_meilisearch.NewFilterEq("reviewed_ids", options.ReviewedID.Value())) } - if options.ReviewRequestedID != nil { - query.And(inner_meilisearch.NewFilterEq("review_requested_ids", *options.ReviewRequestedID)) + if options.ReviewRequestedID.Has() { + query.And(inner_meilisearch.NewFilterEq("review_requested_ids", options.ReviewRequestedID.Value())) } - if options.SubscriberID != nil { - query.And(inner_meilisearch.NewFilterEq("subscriber_ids", *options.SubscriberID)) + if options.SubscriberID.Has() { + query.And(inner_meilisearch.NewFilterEq("subscriber_ids", options.SubscriberID.Value())) } - if options.UpdatedAfterUnix != nil { - query.And(inner_meilisearch.NewFilterGte("updated_unix", *options.UpdatedAfterUnix)) + if options.UpdatedAfterUnix.Has() { + query.And(inner_meilisearch.NewFilterGte("updated_unix", options.UpdatedAfterUnix.Value())) } - if options.UpdatedBeforeUnix != nil { - query.And(inner_meilisearch.NewFilterLte("updated_unix", *options.UpdatedBeforeUnix)) + if options.UpdatedBeforeUnix.Has() { + query.And(inner_meilisearch.NewFilterLte("updated_unix", options.UpdatedBeforeUnix.Value())) } if options.SortBy == "" { diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 09175fe0ac..61a318baab 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -269,28 +269,28 @@ func SearchIssues(ctx *context.APIContext) { } if since != 0 { - searchOpt.UpdatedAfterUnix = &since + searchOpt.UpdatedAfterUnix = optional.Some(since) } if before != 0 { - searchOpt.UpdatedBeforeUnix = &before + searchOpt.UpdatedBeforeUnix = optional.Some(before) } if ctx.IsSigned { ctxUserID := ctx.Doer.ID if ctx.FormBool("created") { - searchOpt.PosterID = &ctxUserID + searchOpt.PosterID = optional.Some(ctxUserID) } if ctx.FormBool("assigned") { - searchOpt.AssigneeID = &ctxUserID + searchOpt.AssigneeID = optional.Some(ctxUserID) } if ctx.FormBool("mentioned") { - searchOpt.MentionID = &ctxUserID + searchOpt.MentionID = optional.Some(ctxUserID) } if ctx.FormBool("review_requested") { - searchOpt.ReviewRequestedID = &ctxUserID + searchOpt.ReviewRequestedID = optional.Some(ctxUserID) } if ctx.FormBool("reviewed") { - searchOpt.ReviewedID = &ctxUserID + searchOpt.ReviewedID = optional.Some(ctxUserID) } } @@ -502,10 +502,10 @@ func ListIssues(ctx *context.APIContext) { SortBy: issue_indexer.SortByCreatedDesc, } if since != 0 { - searchOpt.UpdatedAfterUnix = &since + searchOpt.UpdatedAfterUnix = optional.Some(since) } if before != 0 { - searchOpt.UpdatedBeforeUnix = &before + searchOpt.UpdatedBeforeUnix = optional.Some(before) } if len(labelIDs) == 1 && labelIDs[0] == 0 { searchOpt.NoLabelOnly = true @@ -526,13 +526,13 @@ func ListIssues(ctx *context.APIContext) { } if createdByID > 0 { - searchOpt.PosterID = &createdByID + searchOpt.PosterID = optional.Some(createdByID) } if assignedByID > 0 { - searchOpt.AssigneeID = &assignedByID + searchOpt.AssigneeID = optional.Some(assignedByID) } if mentionedByID > 0 { - searchOpt.MentionID = &mentionedByID + searchOpt.MentionID = optional.Some(mentionedByID) } ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 8935cc80e2..d5fd8439f3 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2634,9 +2634,9 @@ func SearchIssues(ctx *context.Context) { } } - var projectID *int64 + projectID := optional.None[int64]() if v := ctx.FormInt64("project"); v > 0 { - projectID = &v + projectID = optional.Some(v) } // this api is also used in UI, @@ -2665,28 +2665,28 @@ func SearchIssues(ctx *context.Context) { } if since != 0 { - searchOpt.UpdatedAfterUnix = &since + searchOpt.UpdatedAfterUnix = optional.Some(since) } if before != 0 { - searchOpt.UpdatedBeforeUnix = &before + searchOpt.UpdatedBeforeUnix = optional.Some(before) } if ctx.IsSigned { ctxUserID := ctx.Doer.ID if ctx.FormBool("created") { - searchOpt.PosterID = &ctxUserID + searchOpt.PosterID = optional.Some(ctxUserID) } if ctx.FormBool("assigned") { - searchOpt.AssigneeID = &ctxUserID + searchOpt.AssigneeID = optional.Some(ctxUserID) } if ctx.FormBool("mentioned") { - searchOpt.MentionID = &ctxUserID + searchOpt.MentionID = optional.Some(ctxUserID) } if ctx.FormBool("review_requested") { - searchOpt.ReviewRequestedID = &ctxUserID + searchOpt.ReviewRequestedID = optional.Some(ctxUserID) } if ctx.FormBool("reviewed") { - searchOpt.ReviewedID = &ctxUserID + searchOpt.ReviewedID = optional.Some(ctxUserID) } } @@ -2791,9 +2791,9 @@ func ListIssues(ctx *context.Context) { } } - var projectID *int64 + projectID := optional.None[int64]() if v := ctx.FormInt64("project"); v > 0 { - projectID = &v + projectID = optional.Some(v) } isPull := optional.None[bool]() @@ -2831,10 +2831,10 @@ func ListIssues(ctx *context.Context) { SortBy: issue_indexer.SortByCreatedDesc, } if since != 0 { - searchOpt.UpdatedAfterUnix = &since + searchOpt.UpdatedAfterUnix = optional.Some(since) } if before != 0 { - searchOpt.UpdatedBeforeUnix = &before + searchOpt.UpdatedBeforeUnix = optional.Some(before) } if len(labelIDs) == 1 && labelIDs[0] == 0 { searchOpt.NoLabelOnly = true @@ -2855,13 +2855,13 @@ func ListIssues(ctx *context.Context) { } if createdByID > 0 { - searchOpt.PosterID = &createdByID + searchOpt.PosterID = optional.Some(createdByID) } if assignedByID > 0 { - searchOpt.AssigneeID = &assignedByID + searchOpt.AssigneeID = optional.Some(assignedByID) } if mentionedByID > 0 { - searchOpt.MentionID = &mentionedByID + searchOpt.MentionID = optional.Some(mentionedByID) } ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt) diff --git a/routers/web/user/home.go b/routers/web/user/home.go index e731a2a9b7..4ec6f6be3f 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -792,15 +792,15 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod case issues_model.FilterModeYourRepositories: openClosedOpts.AllPublic = false case issues_model.FilterModeAssign: - openClosedOpts.AssigneeID = &doerID + openClosedOpts.AssigneeID = optional.Some(doerID) case issues_model.FilterModeCreate: - openClosedOpts.PosterID = &doerID + openClosedOpts.PosterID = optional.Some(doerID) case issues_model.FilterModeMention: - openClosedOpts.MentionID = &doerID + openClosedOpts.MentionID = optional.Some(doerID) case issues_model.FilterModeReviewRequested: - openClosedOpts.ReviewRequestedID = &doerID + openClosedOpts.ReviewRequestedID = optional.Some(doerID) case issues_model.FilterModeReviewed: - openClosedOpts.ReviewedID = &doerID + openClosedOpts.ReviewedID = optional.Some(doerID) } openClosedOpts.IsClosed = optional.Some(false) ret.OpenCount, err = issue_indexer.CountIssues(ctx, openClosedOpts) @@ -818,23 +818,23 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod if err != nil { return nil, err } - ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = &doerID })) + ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = optional.Some(doerID) })) if err != nil { return nil, err } - ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = &doerID })) + ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = optional.Some(doerID) })) if err != nil { return nil, err } - ret.MentionCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.MentionID = &doerID })) + ret.MentionCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.MentionID = optional.Some(doerID) })) if err != nil { return nil, err } - ret.ReviewRequestedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewRequestedID = &doerID })) + ret.ReviewRequestedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewRequestedID = optional.Some(doerID) })) if err != nil { return nil, err } - ret.ReviewedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewedID = &doerID })) + ret.ReviewedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewedID = optional.Some(doerID) })) if err != nil { return nil, err } From 9b1a8888fa754676073bc851b783b2b8f1adecfb Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 13 Mar 2024 09:43:58 +0100 Subject: [PATCH 014/553] Configure pinned JS dependencies via updates.config.js (#29696) Split out from https://github.com/go-gitea/gitea/pull/29684. This configures the [`updates`](https://github.com/silverwind/updates) module to exclude these modules for reasons stated in the comments. --- updates.config.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 updates.config.js diff --git a/updates.config.js b/updates.config.js new file mode 100644 index 0000000000..11908dea8e --- /dev/null +++ b/updates.config.js @@ -0,0 +1,6 @@ +export default { + exclude: [ + '@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled + 'eslint-plugin-array-func', // need to migrate to eslint flat config first + ], +}; From 66edc888ee8b2f77a6f11139acd2d03c561ad5ef Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 13 Mar 2024 18:07:53 +0800 Subject: [PATCH 015/553] Move fork router functions to a standalone file (#29756) To reduce the pull.go file's size. --- routers/web/repo/fork.go | 238 +++++++++++++++++++++++++++++++++++++++ routers/web/repo/pull.go | 214 ----------------------------------- 2 files changed, 238 insertions(+), 214 deletions(-) create mode 100644 routers/web/repo/fork.go diff --git a/routers/web/repo/fork.go b/routers/web/repo/fork.go new file mode 100644 index 0000000000..60e37476ee --- /dev/null +++ b/routers/web/repo/fork.go @@ -0,0 +1,238 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "errors" + "net/http" + "net/url" + + "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" + "code.gitea.io/gitea/models/organization" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/forms" + repo_service "code.gitea.io/gitea/services/repository" +) + +const ( + tplFork base.TplName = "repo/pulls/fork" +) + +func getForkRepository(ctx *context.Context) *repo_model.Repository { + forkRepo := ctx.Repo.Repository + if ctx.Written() { + return nil + } + + if forkRepo.IsEmpty { + log.Trace("Empty repository %-v", forkRepo) + ctx.NotFound("getForkRepository", nil) + return nil + } + + if err := forkRepo.LoadOwner(ctx); err != nil { + ctx.ServerError("LoadOwner", err) + return nil + } + + ctx.Data["repo_name"] = forkRepo.Name + ctx.Data["description"] = forkRepo.Description + ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate + canForkToUser := forkRepo.OwnerID != ctx.Doer.ID && !repo_model.HasForkedRepo(ctx, ctx.Doer.ID, forkRepo.ID) + + ctx.Data["ForkRepo"] = forkRepo + + ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx, ctx.Doer.ID) + if err != nil { + ctx.ServerError("GetOrgsCanCreateRepoByUserID", err) + return nil + } + var orgs []*organization.Organization + for _, org := range ownedOrgs { + if forkRepo.OwnerID != org.ID && !repo_model.HasForkedRepo(ctx, org.ID, forkRepo.ID) { + orgs = append(orgs, org) + } + } + + traverseParentRepo := forkRepo + for { + if ctx.Doer.ID == traverseParentRepo.OwnerID { + canForkToUser = false + } else { + for i, org := range orgs { + if org.ID == traverseParentRepo.OwnerID { + orgs = append(orgs[:i], orgs[i+1:]...) + break + } + } + } + + if !traverseParentRepo.IsFork { + break + } + traverseParentRepo, err = repo_model.GetRepositoryByID(ctx, traverseParentRepo.ForkID) + if err != nil { + ctx.ServerError("GetRepositoryByID", err) + return nil + } + } + + ctx.Data["CanForkToUser"] = canForkToUser + ctx.Data["Orgs"] = orgs + + if canForkToUser { + ctx.Data["ContextUser"] = ctx.Doer + } else if len(orgs) > 0 { + ctx.Data["ContextUser"] = orgs[0] + } else { + ctx.Data["CanForkRepo"] = false + ctx.Flash.Error(ctx.Tr("repo.fork_no_valid_owners"), true) + return nil + } + + branches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{ + RepoID: ctx.Repo.Repository.ID, + ListOptions: db.ListOptions{ + ListAll: true, + }, + IsDeletedBranch: optional.Some(false), + // Add it as the first option + ExcludeBranchNames: []string{ctx.Repo.Repository.DefaultBranch}, + }) + if err != nil { + ctx.ServerError("FindBranchNames", err) + return nil + } + ctx.Data["Branches"] = append([]string{ctx.Repo.Repository.DefaultBranch}, branches...) + + return forkRepo +} + +// Fork render repository fork page +func Fork(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("new_fork") + + if ctx.Doer.CanForkRepo() { + ctx.Data["CanForkRepo"] = true + } else { + maxCreationLimit := ctx.Doer.MaxCreationLimit() + msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit) + ctx.Flash.Error(msg, true) + } + + getForkRepository(ctx) + if ctx.Written() { + return + } + + ctx.HTML(http.StatusOK, tplFork) +} + +// ForkPost response for forking a repository +func ForkPost(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.CreateRepoForm) + ctx.Data["Title"] = ctx.Tr("new_fork") + ctx.Data["CanForkRepo"] = true + + ctxUser := checkContextUser(ctx, form.UID) + if ctx.Written() { + return + } + + forkRepo := getForkRepository(ctx) + if ctx.Written() { + return + } + + ctx.Data["ContextUser"] = ctxUser + + if ctx.HasError() { + ctx.HTML(http.StatusOK, tplFork) + return + } + + var err error + traverseParentRepo := forkRepo + for { + if ctxUser.ID == traverseParentRepo.OwnerID { + ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form) + return + } + repo := repo_model.GetForkedRepo(ctx, ctxUser.ID, traverseParentRepo.ID) + if repo != nil { + ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name)) + return + } + if !traverseParentRepo.IsFork { + break + } + traverseParentRepo, err = repo_model.GetRepositoryByID(ctx, traverseParentRepo.ForkID) + if err != nil { + ctx.ServerError("GetRepositoryByID", err) + return + } + } + + // Check if user is allowed to create repo's on the organization. + if ctxUser.IsOrganization() { + isAllowedToFork, err := organization.OrgFromUser(ctxUser).CanCreateOrgRepo(ctx, ctx.Doer.ID) + if err != nil { + ctx.ServerError("CanCreateOrgRepo", err) + return + } else if !isAllowedToFork { + ctx.Error(http.StatusForbidden) + return + } + } + + repo, err := repo_service.ForkRepository(ctx, ctx.Doer, ctxUser, repo_service.ForkRepoOptions{ + BaseRepo: forkRepo, + Name: form.RepoName, + Description: form.Description, + SingleBranch: form.ForkSingleBranch, + }) + if err != nil { + ctx.Data["Err_RepoName"] = true + switch { + case repo_model.IsErrReachLimitOfRepo(err): + maxCreationLimit := ctxUser.MaxCreationLimit() + msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit) + ctx.RenderWithErr(msg, tplFork, &form) + case repo_model.IsErrRepoAlreadyExist(err): + ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form) + case repo_model.IsErrRepoFilesAlreadyExist(err): + switch { + case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories): + ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"), tplFork, form) + case setting.Repository.AllowAdoptionOfUnadoptedRepositories: + ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt"), tplFork, form) + case setting.Repository.AllowDeleteOfUnadoptedRepositories: + ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.delete"), tplFork, form) + default: + ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tplFork, form) + } + case db.IsErrNameReserved(err): + ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplFork, &form) + case db.IsErrNamePatternNotAllowed(err): + ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplFork, &form) + case errors.Is(err, user_model.ErrBlockedUser): + ctx.RenderWithErr(ctx.Tr("repo.fork.blocked_user"), tplFork, form) + default: + ctx.ServerError("ForkPost", err) + } + return + } + + log.Trace("Repository forked[%d]: %s/%s", forkRepo.ID, ctxUser.Name, repo.Name) + ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name)) +} diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index ed063715e5..447781602d 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -10,7 +10,6 @@ import ( "fmt" "html" "net/http" - "net/url" "strconv" "strings" "time" @@ -20,7 +19,6 @@ import ( "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/models/organization" access_model "code.gitea.io/gitea/models/perm/access" pull_model "code.gitea.io/gitea/models/pull" repo_model "code.gitea.io/gitea/models/repo" @@ -32,9 +30,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" issue_template "code.gitea.io/gitea/modules/issue/template" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" @@ -53,7 +49,6 @@ import ( ) const ( - tplFork base.TplName = "repo/pulls/fork" tplCompareDiff base.TplName = "repo/diff/compare" tplPullCommits base.TplName = "repo/pulls/commits" tplPullFiles base.TplName = "repo/pulls/files" @@ -112,215 +107,6 @@ func getRepository(ctx *context.Context, repoID int64) *repo_model.Repository { return repo } -func getForkRepository(ctx *context.Context) *repo_model.Repository { - forkRepo := ctx.Repo.Repository - if ctx.Written() { - return nil - } - - if forkRepo.IsEmpty { - log.Trace("Empty repository %-v", forkRepo) - ctx.NotFound("getForkRepository", nil) - return nil - } - - if err := forkRepo.LoadOwner(ctx); err != nil { - ctx.ServerError("LoadOwner", err) - return nil - } - - ctx.Data["repo_name"] = forkRepo.Name - ctx.Data["description"] = forkRepo.Description - ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate - canForkToUser := forkRepo.OwnerID != ctx.Doer.ID && !repo_model.HasForkedRepo(ctx, ctx.Doer.ID, forkRepo.ID) - - ctx.Data["ForkRepo"] = forkRepo - - ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx, ctx.Doer.ID) - if err != nil { - ctx.ServerError("GetOrgsCanCreateRepoByUserID", err) - return nil - } - var orgs []*organization.Organization - for _, org := range ownedOrgs { - if forkRepo.OwnerID != org.ID && !repo_model.HasForkedRepo(ctx, org.ID, forkRepo.ID) { - orgs = append(orgs, org) - } - } - - traverseParentRepo := forkRepo - for { - if ctx.Doer.ID == traverseParentRepo.OwnerID { - canForkToUser = false - } else { - for i, org := range orgs { - if org.ID == traverseParentRepo.OwnerID { - orgs = append(orgs[:i], orgs[i+1:]...) - break - } - } - } - - if !traverseParentRepo.IsFork { - break - } - traverseParentRepo, err = repo_model.GetRepositoryByID(ctx, traverseParentRepo.ForkID) - if err != nil { - ctx.ServerError("GetRepositoryByID", err) - return nil - } - } - - ctx.Data["CanForkToUser"] = canForkToUser - ctx.Data["Orgs"] = orgs - - if canForkToUser { - ctx.Data["ContextUser"] = ctx.Doer - } else if len(orgs) > 0 { - ctx.Data["ContextUser"] = orgs[0] - } else { - ctx.Data["CanForkRepo"] = false - ctx.Flash.Error(ctx.Tr("repo.fork_no_valid_owners"), true) - return nil - } - - branches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{ - RepoID: ctx.Repo.Repository.ID, - ListOptions: db.ListOptions{ - ListAll: true, - }, - IsDeletedBranch: optional.Some(false), - // Add it as the first option - ExcludeBranchNames: []string{ctx.Repo.Repository.DefaultBranch}, - }) - if err != nil { - ctx.ServerError("FindBranchNames", err) - return nil - } - ctx.Data["Branches"] = append([]string{ctx.Repo.Repository.DefaultBranch}, branches...) - - return forkRepo -} - -// Fork render repository fork page -func Fork(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("new_fork") - - if ctx.Doer.CanForkRepo() { - ctx.Data["CanForkRepo"] = true - } else { - maxCreationLimit := ctx.Doer.MaxCreationLimit() - msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit) - ctx.Flash.Error(msg, true) - } - - getForkRepository(ctx) - if ctx.Written() { - return - } - - ctx.HTML(http.StatusOK, tplFork) -} - -// ForkPost response for forking a repository -func ForkPost(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.CreateRepoForm) - ctx.Data["Title"] = ctx.Tr("new_fork") - ctx.Data["CanForkRepo"] = true - - ctxUser := checkContextUser(ctx, form.UID) - if ctx.Written() { - return - } - - forkRepo := getForkRepository(ctx) - if ctx.Written() { - return - } - - ctx.Data["ContextUser"] = ctxUser - - if ctx.HasError() { - ctx.HTML(http.StatusOK, tplFork) - return - } - - var err error - traverseParentRepo := forkRepo - for { - if ctxUser.ID == traverseParentRepo.OwnerID { - ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form) - return - } - repo := repo_model.GetForkedRepo(ctx, ctxUser.ID, traverseParentRepo.ID) - if repo != nil { - ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name)) - return - } - if !traverseParentRepo.IsFork { - break - } - traverseParentRepo, err = repo_model.GetRepositoryByID(ctx, traverseParentRepo.ForkID) - if err != nil { - ctx.ServerError("GetRepositoryByID", err) - return - } - } - - // Check if user is allowed to create repo's on the organization. - if ctxUser.IsOrganization() { - isAllowedToFork, err := organization.OrgFromUser(ctxUser).CanCreateOrgRepo(ctx, ctx.Doer.ID) - if err != nil { - ctx.ServerError("CanCreateOrgRepo", err) - return - } else if !isAllowedToFork { - ctx.Error(http.StatusForbidden) - return - } - } - - repo, err := repo_service.ForkRepository(ctx, ctx.Doer, ctxUser, repo_service.ForkRepoOptions{ - BaseRepo: forkRepo, - Name: form.RepoName, - Description: form.Description, - SingleBranch: form.ForkSingleBranch, - }) - if err != nil { - ctx.Data["Err_RepoName"] = true - switch { - case repo_model.IsErrReachLimitOfRepo(err): - maxCreationLimit := ctxUser.MaxCreationLimit() - msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit) - ctx.RenderWithErr(msg, tplFork, &form) - case repo_model.IsErrRepoAlreadyExist(err): - ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form) - case repo_model.IsErrRepoFilesAlreadyExist(err): - switch { - case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories): - ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"), tplFork, form) - case setting.Repository.AllowAdoptionOfUnadoptedRepositories: - ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt"), tplFork, form) - case setting.Repository.AllowDeleteOfUnadoptedRepositories: - ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.delete"), tplFork, form) - default: - ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tplFork, form) - } - case db.IsErrNameReserved(err): - ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplFork, &form) - case db.IsErrNamePatternNotAllowed(err): - ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplFork, &form) - case errors.Is(err, user_model.ErrBlockedUser): - ctx.RenderWithErr(ctx.Tr("repo.fork.blocked_user"), tplFork, form) - default: - ctx.ServerError("ForkPost", err) - } - return - } - - log.Trace("Repository forked[%d]: %s/%s", forkRepo.ID, ctxUser.Name, repo.Name) - ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name)) -} - func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) { issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { From 85c59d6c21e10ef9d3ccf11713548f50e47e920f Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Wed, 13 Mar 2024 11:34:58 +0100 Subject: [PATCH 016/553] Use relative links for commits, mentions, and issues in markdown (#29427) Fixes #29404 Use relative links for - commits - mentions - issues --------- Co-authored-by: silverwind --- modules/markup/html.go | 12 +++++------ modules/markup/html_internal_test.go | 1 + modules/markup/html_test.go | 9 +++++--- modules/markup/markdown/markdown_test.go | 6 +++--- modules/markup/renderer.go | 14 ++++++++++--- modules/templates/util_render_test.go | 14 ++++++------- routers/common/markup.go | 6 ++++-- services/mailer/mail.go | 3 ++- services/mailer/mail_test.go | 26 +++++++++++++++++++++++- 9 files changed, 65 insertions(+), 26 deletions(-) diff --git a/modules/markup/html.go b/modules/markup/html.go index 56e1a1c54e..21bd6206e0 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -609,7 +609,7 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) { if ok && strings.Contains(mention, "/") { mentionOrgAndTeam := strings.Split(mention, "/") if mentionOrgAndTeam[0][1:] == ctx.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") { - replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention")) + replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention")) node = node.NextSibling.NextSibling start = 0 continue @@ -620,7 +620,7 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) { mentionedUsername := mention[1:] if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) { - replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mentionedUsername), mention, "mention")) + replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention")) node = node.NextSibling.NextSibling } else { node = node.NextSibling @@ -898,9 +898,9 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { path = "pulls" } if ref.Owner == "" { - link = createLink(util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue") + link = createLink(util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue") } else { - link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue") + link = createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue") } } @@ -939,7 +939,7 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) { } reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha) - link := createLink(util.URLJoin(setting.AppSubURL, ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit") + link := createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit") replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link) node = node.NextSibling.NextSibling @@ -1166,7 +1166,7 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) { continue } - link := util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], "commit", hash) + link := util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], "commit", hash) replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit")) start = 0 node = node.NextSibling.NextSibling diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index 93ba9d7667..e313be7040 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -287,6 +287,7 @@ func TestRender_IssueIndexPattern_Document(t *testing.T) { } func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) { + ctx.Links.AbsolutePrefix = true if ctx.Links.Base == "" { ctx.Links.Base = TestRepoURL } diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index ccb63c6bab..55de65d196 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -43,7 +43,8 @@ func TestRender_Commits(t *testing.T) { Ctx: git.DefaultContext, RelativePath: ".md", Links: markup.Links{ - Base: markup.TestRepoURL, + AbsolutePrefix: true, + Base: markup.TestRepoURL, }, Metas: localMetas, }, input) @@ -96,7 +97,8 @@ func TestRender_CrossReferences(t *testing.T) { Ctx: git.DefaultContext, RelativePath: "a.md", Links: markup.Links{ - Base: setting.AppSubURL, + AbsolutePrefix: true, + Base: setting.AppSubURL, }, Metas: localMetas, }, input) @@ -588,7 +590,8 @@ func TestPostProcess_RenderDocument(t *testing.T) { err := markup.PostProcess(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ - Base: "https://example.com", + AbsolutePrefix: true, + Base: "https://example.com", }, Metas: localMetas, }, strings.NewReader(input), &res) diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index dbf95e5e62..a12bd4f9e7 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -130,11 +130,11 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
  • Links, Language bindings, Engine bindings
  • Tips
  • -

    See commit 65f1bf27bc

    +

    See commit 65f1bf27bc

    Ideas and codes

      -
    • Bezier widget (by @r-lyeh) ocornut/imgui#786
    • -
    • Bezier widget (by @r-lyeh) #786
    • +
    • Bezier widget (by @r-lyeh) ocornut/imgui#786
    • +
    • Bezier widget (by @r-lyeh) #786
    • Node graph editors https://github.com/ocornut/imgui/issues/306
    • Memory Editor
    • Plot var helper
    • diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 5a7adcc553..0f0bf55740 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -82,9 +82,17 @@ type RenderContext struct { } type Links struct { - Base string - BranchPath string - TreePath string + AbsolutePrefix bool + Base string + BranchPath string + TreePath string +} + +func (l *Links) Prefix() string { + if l.AbsolutePrefix { + return setting.AppURL + } + return setting.AppSubURL } func (l *Links) HasBranchInfo() bool { diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 8648967d38..15aee8912d 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -117,21 +117,21 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a582 com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit 👍 mail@domain.com -@mention-user test -#123 +@mention-user test +#123 space` assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput, testMetas)) } func TestRenderCommitMessage(t *testing.T) { - expected := `space @mention-user ` + expected := `space @mention-user ` assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput, testMetas)) } func TestRenderCommitMessageLinkSubject(t *testing.T) { - expected := `space @mention-user` + expected := `space @mention-user` assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas)) } @@ -155,14 +155,14 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit 👍 mail@domain.com @mention-user test -#123 +#123 space ` assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas)) } func TestRenderMarkdownToHtml(t *testing.T) { - expected := `

      space @mention-user
      + expected := `

      space @mention-user
      /just/a/path.bin https://example.com/file.bin local link @@ -179,7 +179,7 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a582 com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit 👍 mail@domain.com -@mention-user test +@mention-user test #123 space

      ` diff --git a/routers/common/markup.go b/routers/common/markup.go index 7819ee7227..2d5638ef61 100644 --- a/routers/common/markup.go +++ b/routers/common/markup.go @@ -34,7 +34,8 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr if err := markdown.RenderRaw(&markup.RenderContext{ Ctx: ctx, Links: markup.Links{ - Base: urlPrefix, + AbsolutePrefix: true, + Base: urlPrefix, }, }, strings.NewReader(text), ctx.Resp); err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) @@ -79,7 +80,8 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr if err := markup.Render(&markup.RenderContext{ Ctx: ctx, Links: markup.Links{ - Base: urlPrefix, + AbsolutePrefix: true, + Base: urlPrefix, }, Metas: meta, IsWiki: wiki, diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 38973ea935..a63ba7a52a 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -222,7 +222,8 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient body, err := markdown.RenderString(&markup.RenderContext{ Ctx: ctx, Links: markup.Links{ - Base: ctx.Issue.Repo.HTMLURL(), + AbsolutePrefix: true, + Base: ctx.Issue.Repo.HTMLURL(), }, Metas: ctx.Issue.Repo.ComposeMetas(ctx), }, ctx.Content) diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index e300aeccb0..d87c57ffe7 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -8,6 +8,8 @@ import ( "context" "fmt" "html/template" + "io" + "mime/quotedprintable" "regexp" "strings" "testing" @@ -19,6 +21,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" @@ -67,6 +70,12 @@ func prepareMailerTest(t *testing.T) (doer *user_model.User, repo *repo_model.Re func TestComposeIssueCommentMessage(t *testing.T) { doer, _, issue, comment := prepareMailerTest(t) + markup.Init(&markup.ProcessorHelper{ + IsUsernameMentionable: func(ctx context.Context, username string) bool { + return username == doer.Name + }, + }) + setting.IncomingEmail.Enabled = true defer func() { setting.IncomingEmail.Enabled = false }() @@ -77,7 +86,8 @@ func TestComposeIssueCommentMessage(t *testing.T) { msgs, err := composeIssueCommentMessages(&mailCommentContext{ Context: context.TODO(), // TODO: use a correct context Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue, - Content: "test body", Comment: comment, + Content: fmt.Sprintf("test @%s %s#%d body", doer.Name, issue.Repo.FullName(), issue.Index), + Comment: comment, }, "en-US", recipients, false, "issue comment") assert.NoError(t, err) assert.Len(t, msgs, 2) @@ -96,6 +106,20 @@ func TestComposeIssueCommentMessage(t *testing.T) { assert.Equal(t, "", gomailMsg.GetHeader("Message-ID")[0], "Message-ID header doesn't match") assert.Equal(t, "", gomailMsg.GetHeader("List-Post")[0]) assert.Len(t, gomailMsg.GetHeader("List-Unsubscribe"), 2) // url + mailto + + var buf bytes.Buffer + gomailMsg.WriteTo(&buf) + + b, err := io.ReadAll(quotedprintable.NewReader(&buf)) + assert.NoError(t, err) + + // text/plain + assert.Contains(t, string(b), fmt.Sprintf(`( %s )`, doer.HTMLURL())) + assert.Contains(t, string(b), fmt.Sprintf(`( %s )`, issue.HTMLURL())) + + // text/html + assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, doer.HTMLURL())) + assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, issue.HTMLURL())) } func TestComposeIssueMessage(t *testing.T) { From 3e94ac5c7c6751919453fdb66ba3472e2793759e Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 13 Mar 2024 21:32:30 +0800 Subject: [PATCH 017/553] Improve QueryEscape helper function (#29768) Make it return "template.URL" to follow Golang template's context auto-escaping. --- modules/templates/helper.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 0997239a55..2452064749 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -38,7 +38,7 @@ func NewFuncMap() template.FuncMap { "SafeHTML": SafeHTML, "HTMLFormat": HTMLFormat, "HTMLEscape": HTMLEscape, - "QueryEscape": url.QueryEscape, + "QueryEscape": QueryEscape, "JSEscape": JSEscapeSafe, "SanitizeHTML": SanitizeHTML, "URLJoin": util.URLJoin, @@ -226,6 +226,10 @@ func JSEscapeSafe(s string) template.HTML { return template.HTML(template.JSEscapeString(s)) } +func QueryEscape(s string) template.URL { + return template.URL(url.QueryEscape(s)) +} + // DotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent autolinkers from detecting these as urls func DotEscape(raw string) string { return strings.ReplaceAll(raw, ".", "\u200d.\u200d") From e01b0014de5b732181ac42c03a77c21219f88c6a Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 13 Mar 2024 21:44:46 +0800 Subject: [PATCH 018/553] Improve a11y document and dropdown item (#29753) Co-authored-by: silverwind --- docs/content/contributing/guidelines-frontend.zh-cn.md | 2 +- web_src/js/modules/fomantic/aria.md | 10 +++++----- web_src/js/modules/fomantic/dropdown.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/content/contributing/guidelines-frontend.zh-cn.md b/docs/content/contributing/guidelines-frontend.zh-cn.md index ace0d97f49..b5fb8964b3 100644 --- a/docs/content/contributing/guidelines-frontend.zh-cn.md +++ b/docs/content/contributing/guidelines-frontend.zh-cn.md @@ -53,7 +53,7 @@ HTML 页面由[Go HTML Template](https://pkg.go.dev/html/template)渲染。 ### 可访问性 / ARIA 在历史上,Gitea大量使用了可访问性不友好的框架 Fomantic UI。 -Gitea使用一些补丁使Fomantic UI更具可访问性(参见`aria.js`和`aria.md`), +Gitea 使用一些补丁使 Fomantic UI 更具可访问性(参见 `aria.md`), 但仍然存在许多问题需要大量的工作和时间来修复。 ### 框架使用 diff --git a/web_src/js/modules/fomantic/aria.md b/web_src/js/modules/fomantic/aria.md index a32d15f46f..f639233346 100644 --- a/web_src/js/modules/fomantic/aria.md +++ b/web_src/js/modules/fomantic/aria.md @@ -2,10 +2,10 @@ This document is used as aria/accessibility(a11y) reference for future developers. -There are a lot of a11y problems in the Fomantic UI library. This `aria.js` is used -as a workaround to make the UI more accessible. +There are a lot of a11y problems in the Fomantic UI library. Files in +`web_src/js/modules/fomantic/` are used as a workaround to make the UI more accessible. -The `aria.js` is designed to avoid touching the official Fomantic UI library, +The aria-related code is designed to avoid touching the official Fomantic UI library, and to be as independent as possible, so it can be easily modified/removed in the future. To test the aria/accessibility with screen readers, developers can use the following steps: @@ -14,7 +14,7 @@ To test the aria/accessibility with screen readers, developers can use the follo * Press `Command + F5` to turn on VoiceOver. * Try to operate the UI with keyboard-only. * Use Tab/Shift+Tab to switch focus between elements. - * Arrow keys to navigate between menu/combobox items (only aria-active, not really focused). + * Arrow keys (Option+Up/Down) to navigate between menu/combobox items (only aria-active, not really focused). * Press Enter to trigger the aria-active element. * On Android, you can use TalkBack. * Go to Settings -> Accessibility -> TalkBack, turn it on. @@ -75,7 +75,7 @@ Fomantic Dropdown is designed to be used for many purposes: Fomantic Dropdown requires that the focus must be on its primary element. If the focus changes, it hides or panics. -At the moment, `aria.js` only tries to partially resolve the a11y problems for dropdowns with items. +At the moment, the aria-related code only tries to partially resolve the a11y problems for dropdowns with items. There are different solutions: diff --git a/web_src/js/modules/fomantic/dropdown.js b/web_src/js/modules/fomantic/dropdown.js index c053256dd5..caba8a2f28 100644 --- a/web_src/js/modules/fomantic/dropdown.js +++ b/web_src/js/modules/fomantic/dropdown.js @@ -38,7 +38,7 @@ function updateMenuItem(dropdown, item) { if (!item.id) item.id = generateAriaId(); item.setAttribute('role', dropdown[ariaPatchKey].listItemRole); item.setAttribute('tabindex', '-1'); - for (const a of item.querySelectorAll('a')) a.setAttribute('tabindex', '-1'); + for (const el of item.querySelectorAll('a, input, button')) el.setAttribute('tabindex', '-1'); } // make the label item and its "delete icon" has correct aria attributes From df60dbfb9918081962614d063485337fb42e0ee7 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 14 Mar 2024 00:24:34 +0800 Subject: [PATCH 019/553] Fix incorrect locale Tr for gpg command (#29754) --- options/locale/locale_en-US.ini | 1 - templates/user/settings/keys_gpg.tmpl | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index afd613af59..836703a480 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -804,7 +804,6 @@ gpg_invalid_token_signature = The provided GPG key, signature and token do not m gpg_token_required = You must provide a signature for the below token gpg_token = Token gpg_token_help = You can generate a signature using: -gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature = Armored GPG signature key_signature_gpg_placeholder = Begins with '-----BEGIN PGP SIGNATURE-----' verify_gpg_key_success = GPG key "%s" has been verified. diff --git a/templates/user/settings/keys_gpg.tmpl b/templates/user/settings/keys_gpg.tmpl index 0dd0059511..e57658b197 100644 --- a/templates/user/settings/keys_gpg.tmpl +++ b/templates/user/settings/keys_gpg.tmpl @@ -22,7 +22,7 @@

      {{ctx.Locale.Tr "settings.gpg_token_help"}}

      -

      {{ctx.Locale.Tr "settings.gpg_token_code" .TokenToSign .PaddedKeyID}}

      +

      {{printf `echo "%s" | gpg -a --default-key %s --detach-sig` .TokenToSign .PaddedKeyID}}

      @@ -90,7 +90,7 @@

      {{ctx.Locale.Tr "settings.gpg_token_help"}}

      -

      {{ctx.Locale.Tr "settings.gpg_token_code" $.TokenToSign .PaddedKeyID}}

      +

      {{printf `echo "%s" | gpg -a --default-key %s --detach-sig` $.TokenToSign .PaddedKeyID}}


      From 712e19fa6fbf2f1a5b0a471782d38a7d91e538ae Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 13 Mar 2024 19:00:38 +0100 Subject: [PATCH 020/553] fix missed RenderLabel change in card template (#29772) regression of #29680 close #29770 PS: it would be nice to have a linter that is able to check template helpers ... --- templates/repo/issue/card.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl index ff635c736a..b461c5fc98 100644 --- a/templates/repo/issue/card.tmpl +++ b/templates/repo/issue/card.tmpl @@ -61,7 +61,7 @@ {{if or .Labels .Assignees}}
      {{range .Labels}} - {{RenderLabel ctx .}} + {{RenderLabel ctx ctx.Locale .}} {{end}}
      {{range .Assignees}} From 83ba882bab7e1545fe02cd41f554ae41b83a6040 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 14 Mar 2024 03:46:15 +0800 Subject: [PATCH 021/553] Fix possible NPE in ToPullReviewList (#29759) Co-authored-by: wxiaoguang --- services/convert/pull_review.go | 2 +- services/convert/pull_review_test.go | 52 ++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 services/convert/pull_review_test.go diff --git a/services/convert/pull_review.go b/services/convert/pull_review.go index aa7ad68a47..29a5ab7466 100644 --- a/services/convert/pull_review.go +++ b/services/convert/pull_review.go @@ -66,7 +66,7 @@ func ToPullReviewList(ctx context.Context, rl []*issues_model.Review, doer *user result := make([]*api.PullReview, 0, len(rl)) for i := range rl { // show pending reviews only for the user who created them - if rl[i].Type == issues_model.ReviewTypePending && !(doer.IsAdmin || doer.ID == rl[i].ReviewerID) { + if rl[i].Type == issues_model.ReviewTypePending && (doer == nil || (!doer.IsAdmin && doer.ID != rl[i].ReviewerID)) { continue } r, err := ToPullReview(ctx, rl[i], doer) diff --git a/services/convert/pull_review_test.go b/services/convert/pull_review_test.go new file mode 100644 index 0000000000..6886950280 --- /dev/null +++ b/services/convert/pull_review_test.go @@ -0,0 +1,52 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package convert + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + + "github.com/stretchr/testify/assert" +) + +func Test_ToPullReview(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 6}) + assert.EqualValues(t, reviewer.ID, review.ReviewerID) + assert.EqualValues(t, issues_model.ReviewTypePending, review.Type) + + reviewList := []*issues_model.Review{review} + + t.Run("Anonymous User", func(t *testing.T) { + prList, err := ToPullReviewList(db.DefaultContext, reviewList, nil) + assert.NoError(t, err) + assert.Empty(t, prList) + }) + + t.Run("Reviewer Himself", func(t *testing.T) { + prList, err := ToPullReviewList(db.DefaultContext, reviewList, reviewer) + assert.NoError(t, err) + assert.Len(t, prList, 1) + }) + + t.Run("Other User", func(t *testing.T) { + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + prList, err := ToPullReviewList(db.DefaultContext, reviewList, user4) + assert.NoError(t, err) + assert.Len(t, prList, 0) + }) + + t.Run("Admin User", func(t *testing.T) { + adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + prList, err := ToPullReviewList(db.DefaultContext, reviewList, adminUser) + assert.NoError(t, err) + assert.Len(t, prList, 1) + }) +} From 43de021ac1ca017212ec75fd88a8a80a9db27c4c Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 14 Mar 2024 09:10:51 +0800 Subject: [PATCH 022/553] Add test for webhook (#29755) Follow #29690 --- modules/util/util.go | 9 ++++ services/webhook/deliver_test.go | 78 ++++++++++++-------------------- 2 files changed, 38 insertions(+), 49 deletions(-) diff --git a/modules/util/util.go b/modules/util/util.go index 5c75158196..c94fb91047 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -212,3 +212,12 @@ func ToFloat64(number any) (float64, error) { func ToPointer[T any](val T) *T { return &val } + +// IfZero returns "def" if "v" is a zero value, otherwise "v" +func IfZero[T comparable](v, def T) T { + var zero T + if v == zero { + return def + } + return v +} diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go index 24924ab214..85de1f9904 100644 --- a/services/webhook/deliver_test.go +++ b/services/webhook/deliver_test.go @@ -18,6 +18,7 @@ import ( webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/hostmatcher" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" "github.com/stretchr/testify/assert" @@ -226,49 +227,29 @@ func TestWebhookDeliverSpecificTypes(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) type hookCase struct { - gotBody chan []byte + gotBody chan []byte + httpMethod string // default to POST } - cases := map[string]hookCase{ - webhook_module.SLACK: { - gotBody: make(chan []byte, 1), - }, - webhook_module.DISCORD: { - gotBody: make(chan []byte, 1), - }, - webhook_module.DINGTALK: { - gotBody: make(chan []byte, 1), - }, - webhook_module.TELEGRAM: { - gotBody: make(chan []byte, 1), - }, - webhook_module.MSTEAMS: { - gotBody: make(chan []byte, 1), - }, - webhook_module.FEISHU: { - gotBody: make(chan []byte, 1), - }, - webhook_module.MATRIX: { - gotBody: make(chan []byte, 1), - }, - webhook_module.WECHATWORK: { - gotBody: make(chan []byte, 1), - }, - webhook_module.PACKAGIST: { - gotBody: make(chan []byte, 1), - }, + cases := map[string]*hookCase{ + webhook_module.SLACK: {}, + webhook_module.DISCORD: {}, + webhook_module.DINGTALK: {}, + webhook_module.TELEGRAM: {}, + webhook_module.MSTEAMS: {}, + webhook_module.FEISHU: {}, + webhook_module.MATRIX: {httpMethod: "PUT"}, + webhook_module.WECHATWORK: {}, + webhook_module.PACKAGIST: {}, } s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + typ := strings.Split(r.URL.Path, "/")[1] // URL: "/{webhook_type}/other-path" assert.Equal(t, "application/json", r.Header.Get("Content-Type"), r.URL.Path) - - typ := strings.Split(r.URL.Path, "/")[1] // take first segment (after skipping leading slash) - hc := cases[typ] - require.NotNil(t, hc.gotBody, r.URL.Path) - body, err := io.ReadAll(r.Body) - assert.NoError(t, err) - w.WriteHeader(200) - hc.gotBody <- body + assert.Equal(t, util.IfZero(cases[typ].httpMethod, "POST"), r.Method, "webhook test request %q", r.URL.Path) + body, _ := io.ReadAll(r.Body) // read request and send it back to the test by testcase's chan + cases[typ].gotBody <- body + w.WriteHeader(http.StatusNoContent) })) t.Cleanup(s.Close) @@ -276,19 +257,17 @@ func TestWebhookDeliverSpecificTypes(t *testing.T) { data, err := p.JSONPayload() assert.NoError(t, err) - for typ, hc := range cases { - typ := typ - hc := hc + for typ := range cases { + cases[typ].gotBody = make(chan []byte, 1) + typ := typ // TODO: remove this workaround when Go >= 1.22 t.Run(typ, func(t *testing.T) { t.Parallel() hook := &webhook_model.Webhook{ - RepoID: 3, - IsActive: true, - Type: typ, - URL: s.URL + "/" + typ, - HTTPMethod: "POST", - ContentType: 0, // set to 0 so that falling back to default request fails with "invalid content type" - Meta: "{}", + RepoID: 3, + IsActive: true, + Type: typ, + URL: s.URL + "/" + typ, + Meta: "{}", } assert.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, hook)) @@ -304,10 +283,11 @@ func TestWebhookDeliverSpecificTypes(t *testing.T) { assert.NotNil(t, hookTask) assert.NoError(t, Deliver(context.Background(), hookTask)) + select { - case gotBody := <-hc.gotBody: + case gotBody := <-cases[typ].gotBody: assert.NotEqual(t, string(data), string(gotBody), "request body must be different from the event payload") - assert.Equal(t, hookTask.RequestInfo.Body, string(gotBody), "request body was not saved") + assert.Equal(t, hookTask.RequestInfo.Body, string(gotBody), "delivered webhook payload doesn't match saved request") case <-time.After(5 * time.Second): t.Fatal("waited to long for request to happen") } From 2da13675c0cfdc531044553636c3b74f2fda3eb4 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 14 Mar 2024 10:37:15 +0900 Subject: [PATCH 023/553] Fix incorrect menu/link on webhook edit page (#29709) Fix #29699 --------- Co-authored-by: silverwind --- routers/web/repo/setting/webhook.go | 1 + tests/integration/repo_webhook_test.go | 41 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tests/integration/repo_webhook_test.go diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index c8e621fac8..1a3549fea4 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -588,6 +588,7 @@ func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) { return nil, nil } ctx.Data["BaseLink"] = orCtx.Link + ctx.Data["BaseLinkNew"] = orCtx.LinkNew var w *webhook.Webhook if orCtx.RepoID > 0 { diff --git a/tests/integration/repo_webhook_test.go b/tests/integration/repo_webhook_test.go new file mode 100644 index 0000000000..ef44a9e2d0 --- /dev/null +++ b/tests/integration/repo_webhook_test.go @@ -0,0 +1,41 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "strings" + "testing" + + "code.gitea.io/gitea/tests" + + "github.com/PuerkitoBio/goquery" + "github.com/stretchr/testify/assert" +) + +func TestNewWebHookLink(t *testing.T) { + defer tests.PrepareTestEnv(t)() + session := loginUser(t, "user2") + + baseurl := "/user2/repo1/settings/hooks" + tests := []string{ + // webhook list page + baseurl, + // new webhook page + baseurl + "/gitea/new", + // edit webhook page + baseurl + "/1", + } + + for _, url := range tests { + resp := session.MakeRequest(t, NewRequest(t, "GET", url), http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + menus := htmlDoc.doc.Find(".ui.top.attached.header .ui.dropdown .menu a") + menus.Each(func(i int, menu *goquery.Selection) { + url, exist := menu.Attr("href") + assert.True(t, exist) + assert.True(t, strings.HasPrefix(url, baseurl)) + }) + } +} From bbef5fc5c304d8e9cef110bffa44a0e4bcf028fb Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 14 Mar 2024 03:23:58 +0100 Subject: [PATCH 024/553] Fix `make generate-swagger` in go 1.22 (#29780) Fixes: https://github.com/go-gitea/gitea/issues/29664. No release available for https://github.com/go-swagger/go-swagger/issues/3070 so let's depend on latest commit hash. Output is the same as before for me. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5ab8655c2f..cedc4b4198 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.1 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1 -SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5 +SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285 XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.0.3 From e79a807a8461a73bd66146d816f635b66e198c89 Mon Sep 17 00:00:00 2001 From: coldWater <254244460@qq.com> Date: Thu, 14 Mar 2024 10:51:55 +0800 Subject: [PATCH 025/553] Refactor markup/csv: don't read all to memory (#29760) --- modules/markup/csv/csv.go | 63 ++++++++++++++++++++++++++-------- modules/markup/csv/csv_test.go | 10 ++++++ 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/modules/markup/csv/csv.go b/modules/markup/csv/csv.go index 12458e954a..570c4f4704 100644 --- a/modules/markup/csv/csv.go +++ b/modules/markup/csv/csv.go @@ -77,29 +77,62 @@ func writeField(w io.Writer, element, class, field string) error { } // Render implements markup.Renderer -func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { +func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { tmpBlock := bufio.NewWriter(output) + maxSize := setting.UI.CSV.MaxFileSize - // FIXME: don't read all to memory - rawBytes, err := io.ReadAll(input) + if maxSize == 0 { + return r.tableRender(ctx, input, tmpBlock) + } + + rawBytes, err := io.ReadAll(io.LimitReader(input, maxSize+1)) if err != nil { return err } - if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < int64(len(rawBytes)) { - if _, err := tmpBlock.WriteString("
      "); err != nil {
      -			return err
      -		}
      -		if _, err := tmpBlock.WriteString(html.EscapeString(string(rawBytes))); err != nil {
      -			return err
      -		}
      -		if _, err := tmpBlock.WriteString("
      "); err != nil { - return err - } - return tmpBlock.Flush() + if int64(len(rawBytes)) <= maxSize { + return r.tableRender(ctx, bytes.NewReader(rawBytes), tmpBlock) + } + return r.fallbackRender(io.MultiReader(bytes.NewReader(rawBytes), input), tmpBlock) +} + +func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error { + _, err := tmpBlock.WriteString("
      ")
      +	if err != nil {
      +		return err
       	}
       
      -	rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, bytes.NewReader(rawBytes))
      +	scan := bufio.NewScanner(input)
      +	scan.Split(bufio.ScanRunes)
      +	for scan.Scan() {
      +		switch scan.Text() {
      +		case `&`:
      +			_, err = tmpBlock.WriteString("&")
      +		case `'`:
      +			_, err = tmpBlock.WriteString("'") // "'" is shorter than "'" and apos was not in HTML until HTML5.
      +		case `<`:
      +			_, err = tmpBlock.WriteString("<")
      +		case `>`:
      +			_, err = tmpBlock.WriteString(">")
      +		case `"`:
      +			_, err = tmpBlock.WriteString(""") // """ is shorter than """.
      +		default:
      +			_, err = tmpBlock.Write(scan.Bytes())
      +		}
      +		if err != nil {
      +			return err
      +		}
      +	}
      +
      +	_, err = tmpBlock.WriteString("
      ") + if err != nil { + return err + } + return tmpBlock.Flush() +} + +func (Renderer) tableRender(ctx *markup.RenderContext, input io.Reader, tmpBlock *bufio.Writer) error { + rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input) if err != nil { return err } diff --git a/modules/markup/csv/csv_test.go b/modules/markup/csv/csv_test.go index 8c07184b21..3d12be477c 100644 --- a/modules/markup/csv/csv_test.go +++ b/modules/markup/csv/csv_test.go @@ -4,6 +4,8 @@ package markup import ( + "bufio" + "bytes" "strings" "testing" @@ -29,4 +31,12 @@ func TestRenderCSV(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, v, buf.String()) } + + t.Run("fallbackRender", func(t *testing.T) { + var buf bytes.Buffer + err := render.fallbackRender(strings.NewReader("1,\n2,"), bufio.NewWriter(&buf)) + assert.NoError(t, err) + want := "
      1,<a>\n2,<b>
      " + assert.Equal(t, want, buf.String()) + }) } From 7a90e5954f8515329f20ff0e391130e1ee7b8864 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Thu, 14 Mar 2024 04:18:04 +0100 Subject: [PATCH 026/553] add skip ci support for pull request title (#29774) Extends #28075 to support [skip ci] inside PR titles. Close #29265 --- custom/conf/app.example.ini | 2 +- .../config-cheat-sheet.en-us.md | 2 +- services/actions/notifier_helper.go | 10 ++-- tests/integration/actions_trigger_test.go | 51 ++++++++++++++++--- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 17d6cd3a35..b4b4f3a8a2 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -2608,7 +2608,7 @@ LEVEL = Info ;ENDLESS_TASK_TIMEOUT = 3h ;; Timeout to cancel the jobs which have waiting status, but haven't been picked by a runner for a long time ;ABANDONED_JOB_TIMEOUT = 24h -;; Strings committers can place inside a commit message to skip executing the corresponding actions workflow +;; Strings committers can place inside a commit message or PR title to skip executing the corresponding actions workflow ;SKIP_WORKFLOW_STRINGS = [skip ci],[ci skip],[no ci],[skip actions],[actions skip] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index 43ec470ad0..04923acdcb 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -1406,7 +1406,7 @@ PROXY_HOSTS = *.github.com - `ZOMBIE_TASK_TIMEOUT`: **10m**: Timeout to stop the task which have running status, but haven't been updated for a long time - `ENDLESS_TASK_TIMEOUT`: **3h**: Timeout to stop the tasks which have running status and continuous updates, but don't end for a long time - `ABANDONED_JOB_TIMEOUT`: **24h**: Timeout to cancel the jobs which have waiting status, but haven't been picked by a runner for a long time -- `SKIP_WORKFLOW_STRINGS`: **[skip ci],[ci skip],[no ci],[skip actions],[actions skip]**: Strings committers can place inside a commit message to skip executing the corresponding actions workflow +- `SKIP_WORKFLOW_STRINGS`: **[skip ci],[ci skip],[no ci],[skip actions],[actions skip]**: Strings committers can place inside a commit message or PR title to skip executing the corresponding actions workflow `DEFAULT_ACTIONS_URL` indicates where the Gitea Actions runners should find the actions with relative path. For example, `uses: actions/checkout@v4` means `https://github.com/actions/checkout@v4` since the value of `DEFAULT_ACTIONS_URL` is `github`. diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index d84191dca2..fafb6ab40e 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -157,7 +157,7 @@ func notify(ctx context.Context, input *notifyInput) error { return fmt.Errorf("gitRepo.GetCommit: %w", err) } - if skipWorkflowsForCommit(input, commit) { + if skipWorkflows(input, commit) { return nil } @@ -223,8 +223,8 @@ func notify(ctx context.Context, input *notifyInput) error { return handleWorkflows(ctx, detectedWorkflows, commit, input, ref) } -func skipWorkflowsForCommit(input *notifyInput, commit *git.Commit) bool { - // skip workflow runs with a configured skip-ci string in commit message if the event is push or pull_request(_sync) +func skipWorkflows(input *notifyInput, commit *git.Commit) bool { + // skip workflow runs with a configured skip-ci string in commit message or pr title if the event is push or pull_request(_sync) // https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs skipWorkflowEvents := []webhook_module.HookEventType{ webhook_module.HookEventPush, @@ -233,6 +233,10 @@ func skipWorkflowsForCommit(input *notifyInput, commit *git.Commit) bool { } if slices.Contains(skipWorkflowEvents, input.Event) { for _, s := range setting.Actions.SkipWorkflowStrings { + if input.PullRequest != nil && strings.Contains(input.PullRequest.Issue.Title, s) { + log.Debug("repo %s: skipped run for pr %v because of %s string", input.Repo.RepoPath(), input.PullRequest.Issue.ID, s) + return true + } if strings.Contains(commit.CommitMessage, s) { log.Debug("repo %s with commit %s: skipped run because of %s string", input.Repo.RepoPath(), commit.ID, s) return true diff --git a/tests/integration/actions_trigger_test.go b/tests/integration/actions_trigger_test.go index 7744f33e57..9a8bfc5db6 100644 --- a/tests/integration/actions_trigger_test.go +++ b/tests/integration/actions_trigger_test.go @@ -20,6 +20,7 @@ import ( actions_module "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" files_service "code.gitea.io/gitea/services/repository/files" @@ -199,6 +200,7 @@ func TestPullRequestTargetEvent(t *testing.T) { func TestSkipCI(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user2") user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // create the repo @@ -209,7 +211,7 @@ func TestSkipCI(t *testing.T) { Gitignores: "Go", License: "MIT", Readme: "Default", - DefaultBranch: "main", + DefaultBranch: "master", IsPrivate: false, }) assert.NoError(t, err) @@ -228,12 +230,12 @@ func TestSkipCI(t *testing.T) { { Operation: "create", TreePath: ".gitea/workflows/pr.yml", - ContentReader: strings.NewReader("name: test\non:\n push:\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"), + ContentReader: strings.NewReader("name: test\non:\n push:\n branches: [master]\n pull_request:\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"), }, }, Message: "add workflow", - OldBranch: "main", - NewBranch: "main", + OldBranch: "master", + NewBranch: "master", Author: &files_service.IdentityOptions{ Name: user2.Name, Email: user2.Email, @@ -263,8 +265,8 @@ func TestSkipCI(t *testing.T) { }, }, Message: fmt.Sprintf("%s add bar", setting.Actions.SkipWorkflowStrings[0]), - OldBranch: "main", - NewBranch: "main", + OldBranch: "master", + NewBranch: "master", Author: &files_service.IdentityOptions{ Name: user2.Name, Email: user2.Email, @@ -283,5 +285,42 @@ func TestSkipCI(t *testing.T) { // the commit message contains a configured skip-ci string, so there is still only 1 record assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID})) + + // add file to new branch + addFileToBranchResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{ + Files: []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: "test-skip-ci", + ContentReader: strings.NewReader("test-skip-ci"), + }, + }, + Message: "add test file", + OldBranch: "master", + NewBranch: "test-skip-ci", + Author: &files_service.IdentityOptions{ + Name: user2.Name, + Email: user2.Email, + }, + Committer: &files_service.IdentityOptions{ + Name: user2.Name, + Email: user2.Email, + }, + Dates: &files_service.CommitDateOptions{ + Author: time.Now(), + Committer: time.Now(), + }, + }) + assert.NoError(t, err) + assert.NotEmpty(t, addFileToBranchResp) + + resp := testPullCreate(t, session, "user2", "skip-ci", true, "master", "test-skip-ci", "[skip ci] test-skip-ci") + + // check the redirected URL + url := test.RedirectURL(resp) + assert.Regexp(t, "^/user2/skip-ci/pulls/[0-9]*$", url) + + // the pr title contains a configured skip-ci string, so there is still only 1 record + assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID})) }) } From eb8c34fc367f324226625d39d0487f945269cd73 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 14 Mar 2024 05:30:10 +0100 Subject: [PATCH 027/553] Tweak actions view sticky (#29781) Add some space when the left side items are sticky due to scrolling the right side. image --- web_src/js/components/RepoActionView.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 9641431508..484a3677c5 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -524,7 +524,7 @@ export function initRepositoryActionView() { width: 30%; max-width: 400px; position: sticky; - top: 0; + top: 12px; max-height: 100vh; overflow-y: auto; } From 2033eb7c1138a06ea052e6c6e6e5799187519e16 Mon Sep 17 00:00:00 2001 From: sillyguodong <33891828+sillyguodong@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:59:52 +0800 Subject: [PATCH 028/553] Fix lint-swagger warning (#29787) Caused by: #23106 Fix: https://github.com/go-gitea/gitea/actions/runs/8274650046/job/22640335697 1. Delete `UserBadgeList` in `options.go`, because it wasn't used. (The struct defined in `options.go` is the struct used to parse the request body) 2. Move `BadgeList` struct under `routers/api/v1/swagger` folder which response should be defined in. --- modules/structs/user.go | 7 ------- routers/api/v1/swagger/options.go | 3 --- routers/api/v1/swagger/user.go | 7 +++++++ templates/swagger/v1_json.tmpl | 17 +---------------- 4 files changed, 8 insertions(+), 26 deletions(-) diff --git a/modules/structs/user.go b/modules/structs/user.go index c43558be5d..21ecc1479e 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -132,10 +132,3 @@ type UserBadgeOption struct { // example: ["badge1","badge2"] BadgeSlugs []string `json:"badge_slugs" binding:"Required"` } - -// BadgeList -// swagger:response BadgeList -type BadgeList struct { - // in:body - Body []Badge `json:"body"` -} diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index e03862d7b9..471e7d9c4e 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -193,7 +193,4 @@ type swaggerParameterBodies struct { // in:body UserBadgeOption api.UserBadgeOption - - // in:body - UserBadgeList api.BadgeList } diff --git a/routers/api/v1/swagger/user.go b/routers/api/v1/swagger/user.go index fb6d185ee7..e2ad511d2b 100644 --- a/routers/api/v1/swagger/user.go +++ b/routers/api/v1/swagger/user.go @@ -48,3 +48,10 @@ type swaggerResponseUserSettings struct { // in:body Body []api.UserSettings `json:"body"` } + +// BadgeList +// swagger:response BadgeList +type swaggerResponseBadgeList struct { + // in:body + Body []api.Badge `json:"body"` +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 221b34b7f8..f835df084d 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -17413,21 +17413,6 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, - "BadgeList": { - "description": "BadgeList", - "type": "object", - "properties": { - "body": { - "description": "in:body", - "type": "array", - "items": { - "$ref": "#/definitions/Badge" - }, - "x-go-name": "Body" - } - }, - "x-go-package": "code.gitea.io/gitea/modules/structs" - }, "Branch": { "description": "Branch represents a repository branch", "type": "object", @@ -24722,7 +24707,7 @@ "parameterBodies": { "description": "parameterBodies", "schema": { - "$ref": "#/definitions/BadgeList" + "$ref": "#/definitions/UserBadgeOption" } }, "redirect": { From 8d979f16928b11779c04c4b547dee3d748cadd51 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 14 Mar 2024 15:40:52 +0800 Subject: [PATCH 029/553] Fix missing translation on milestons (#29785) Caused by #26569 Fix #29778 --- templates/user/dashboard/milestones.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/user/dashboard/milestones.tmpl b/templates/user/dashboard/milestones.tmpl index 7cde02291b..fd684fcabf 100644 --- a/templates/user/dashboard/milestones.tmpl +++ b/templates/user/dashboard/milestones.tmpl @@ -62,8 +62,8 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}