From f67f57a4c28d1660dec6cadb7fd07bdeda98aa5b Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 23 Aug 2023 23:15:06 +0800 Subject: [PATCH 01/60] Handle "comment form combo editor init" more gracefully (#26688) Now Gitea exposes unhandled promise rejection messages as error message on the UI. The "comment form" was quite unclear before, so it should be handled more gracefully to avoid such error. --- web_src/js/features/repo-legacy.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 5991df6322..3b25c36538 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -54,9 +54,10 @@ export function initRepoCommentForm() { } if ($commentForm.find('.field.combo-editor-dropzone').length) { - // at the moment, if a form has multiple combo-markdown-editors, it must be a issue template form + // at the moment, if a form has multiple combo-markdown-editors, it must be an issue template form initIssueTemplateCommentEditors($commentForm); - } else { + } else if ($commentForm.find('.combo-markdown-editor').length) { + // it's quite unclear about the "comment form" elements, sometimes it's for issue comment, sometimes it's for file editor/uploader message initSingleCommentEditor($commentForm); } From 5104c887d32a19afd468da7e71f39831607f4c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=88=E7=AC=91=E9=A3=8E=E7=94=9F=E9=97=B4?= Date: Thu, 24 Aug 2023 09:00:11 +0800 Subject: [PATCH 02/60] Modify the content format of the Feishu webhook (#25106) close https://github.com/go-gitea/gitea/issues/24368 ## what my pull request does Since the official documentation states that custom bots do not support hyperlink functionality, simply adding it without making some formatting changes would result in an unappealing output. Therefore, I have modified the formatting of the output. Currently, it is only used for Feishu. --- [docs](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/im-v1/message/create_json#%E8%B6%85%E9%93%BE%E6%8E%A5%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E) image - Issue image - Issue Comment image - Assign image image - Merge image - PullRequest image --- services/webhook/feishu.go | 35 +++++++++++++----- services/webhook/feishu_test.go | 10 ++--- services/webhook/general.go | 63 ++++++++++++++++++++++++++++++++ services/webhook/general_test.go | 18 ++++++++- 4 files changed, 111 insertions(+), 15 deletions(-) diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go index 885cde3bc5..089f51952f 100644 --- a/services/webhook/feishu.go +++ b/services/webhook/feishu.go @@ -97,23 +97,40 @@ func (f *FeishuPayload) Push(p *api.PushPayload) (api.Payloader, error) { // Issue implements PayloadConvertor Issue method func (f *FeishuPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { - text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, true) - - return newFeishuTextPayload(issueTitle + "\r\n" + text + "\r\n\r\n" + attachmentText), nil + title, link, by, operator, result, assignees := getIssuesInfo(p) + var res api.Payloader + if assignees != "" { + if p.Action == api.HookIssueAssigned || p.Action == api.HookIssueUnassigned || p.Action == api.HookIssueMilestoned { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, result, assignees, p.Issue.Body)) + } else { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, assignees, p.Issue.Body)) + } + } else { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.Issue.Body)) + } + return res, nil } // IssueComment implements PayloadConvertor IssueComment method func (f *FeishuPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { - text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true) - - return newFeishuTextPayload(issueTitle + "\r\n" + text + "\r\n\r\n" + p.Comment.Body), nil + title, link, by, operator := getIssuesCommentInfo(p) + return newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.Comment.Body)), nil } // PullRequest implements PayloadConvertor PullRequest method func (f *FeishuPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { - text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, true) - - return newFeishuTextPayload(issueTitle + "\r\n" + text + "\r\n\r\n" + attachmentText), nil + title, link, by, operator, result, assignees := getPullRequestInfo(p) + var res api.Payloader + if assignees != "" { + if p.Action == api.HookIssueAssigned || p.Action == api.HookIssueUnassigned || p.Action == api.HookIssueMilestoned { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, result, assignees, p.PullRequest.Body)) + } else { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, assignees, p.PullRequest.Body)) + } + } else { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.PullRequest.Body)) + } + return res, nil } // Review implements PayloadConvertor Review method diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go index 84549c1fa5..a3182e82b0 100644 --- a/services/webhook/feishu_test.go +++ b/services/webhook/feishu_test.go @@ -72,7 +72,7 @@ func TestFeishuPayload(t *testing.T) { require.NotNil(t, pl) require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "#2 crash\r\n[test/repo] Issue opened: #2 crash by user1\r\n\r\nissue body", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Issue-test/repo #2]: opened\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\nAssignees: user1\n\nissue body", pl.(*FeishuPayload).Content.Text) p.Action = api.HookIssueClosed pl, err = d.Issue(p) @@ -80,7 +80,7 @@ func TestFeishuPayload(t *testing.T) { require.NotNil(t, pl) require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "#2 crash\r\n[test/repo] Issue closed: #2 crash by user1", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Issue-test/repo #2]: closed\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\nAssignees: user1\n\nissue body", pl.(*FeishuPayload).Content.Text) }) t.Run("IssueComment", func(t *testing.T) { @@ -92,7 +92,7 @@ func TestFeishuPayload(t *testing.T) { require.NotNil(t, pl) require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "#2 crash\r\n[test/repo] New comment on issue #2 crash by user1\r\n\r\nmore info needed", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Comment-test/repo #2]: created\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\n\nmore info needed", pl.(*FeishuPayload).Content.Text) }) t.Run("PullRequest", func(t *testing.T) { @@ -104,7 +104,7 @@ func TestFeishuPayload(t *testing.T) { require.NotNil(t, pl) require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "#12 Fix bug\r\n[test/repo] Pull request opened: #12 Fix bug by user1\r\n\r\nfixes bug #2", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[PullRequest-test/repo #12]: opened\nFix bug\nhttp://localhost:3000/test/repo/pulls/12\nPullRequest by user1\nOperator: user1\nAssignees: user1\n\nfixes bug #2", pl.(*FeishuPayload).Content.Text) }) t.Run("PullRequestComment", func(t *testing.T) { @@ -116,7 +116,7 @@ func TestFeishuPayload(t *testing.T) { require.NotNil(t, pl) require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "#12 Fix bug\r\n[test/repo] New comment on pull request #12 Fix bug by user1\r\n\r\nchanges requested", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Comment-test/repo #12]: created\nFix bug\nhttp://localhost:3000/test/repo/pulls/12\nPullRequest by user1\nOperator: user1\n\nchanges requested", pl.(*FeishuPayload).Content.Text) }) t.Run("Review", func(t *testing.T) { diff --git a/services/webhook/general.go b/services/webhook/general.go index b9cc3dc845..986467bc99 100644 --- a/services/webhook/general.go +++ b/services/webhook/general.go @@ -28,6 +28,69 @@ func htmlLinkFormatter(url, text string) string { return fmt.Sprintf(`%s`, html.EscapeString(url), html.EscapeString(text)) } +// getPullRequestInfo gets the information for a pull request +func getPullRequestInfo(p *api.PullRequestPayload) (title, link, by, operator, operateResult, assignees string) { + title = fmt.Sprintf("[PullRequest-%s #%d]: %s\n%s", p.Repository.FullName, p.PullRequest.Index, p.Action, p.PullRequest.Title) + assignList := p.PullRequest.Assignees + assignStringList := make([]string, len(assignList)) + + for i, user := range assignList { + assignStringList[i] = user.UserName + } + if p.Action == api.HookIssueAssigned { + operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName) + } else if p.Action == api.HookIssueUnassigned { + operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName) + } else if p.Action == api.HookIssueMilestoned { + operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID) + } + link = p.PullRequest.HTMLURL + by = fmt.Sprintf("PullRequest by %s", p.PullRequest.Poster.UserName) + if len(assignStringList) > 0 { + assignees = fmt.Sprintf("Assignees: %s", strings.Join(assignStringList, ", ")) + } + operator = fmt.Sprintf("Operator: %s", p.Sender.UserName) + return title, link, by, operator, operateResult, assignees +} + +// getIssuesInfo gets the information for an issue +func getIssuesInfo(p *api.IssuePayload) (issueTitle, link, by, operator, operateResult, assignees string) { + issueTitle = fmt.Sprintf("[Issue-%s #%d]: %s\n%s", p.Repository.FullName, p.Issue.Index, p.Action, p.Issue.Title) + assignList := p.Issue.Assignees + assignStringList := make([]string, len(assignList)) + + for i, user := range assignList { + assignStringList[i] = user.UserName + } + if p.Action == api.HookIssueAssigned { + operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName) + } else if p.Action == api.HookIssueUnassigned { + operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName) + } else if p.Action == api.HookIssueMilestoned { + operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID) + } + link = p.Issue.HTMLURL + by = fmt.Sprintf("Issue by %s", p.Issue.Poster.UserName) + if len(assignStringList) > 0 { + assignees = fmt.Sprintf("Assignees: %s", strings.Join(assignStringList, ", ")) + } + operator = fmt.Sprintf("Operator: %s", p.Sender.UserName) + return issueTitle, link, by, operator, operateResult, assignees +} + +// getIssuesCommentInfo gets the information for a comment +func getIssuesCommentInfo(p *api.IssueCommentPayload) (title, link, by, operator string) { + title = fmt.Sprintf("[Comment-%s #%d]: %s\n%s", p.Repository.FullName, p.Issue.Index, p.Action, p.Issue.Title) + link = p.Issue.HTMLURL + if p.IsPull { + by = fmt.Sprintf("PullRequest by %s", p.Issue.Poster.UserName) + } else { + by = fmt.Sprintf("Issue by %s", p.Issue.Poster.UserName) + } + operator = fmt.Sprintf("Operator: %s", p.Sender.UserName) + return title, link, by, operator +} + func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) { repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName) issueTitle := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title) diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go index ba58ca4f90..64bd72f5a0 100644 --- a/services/webhook/general_test.go +++ b/services/webhook/general_test.go @@ -123,6 +123,10 @@ func issueTestPayload() *api.IssuePayload { HTMLURL: "http://localhost:3000/test/repo/issues/2", Title: "crash", Body: "issue body", + Poster: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, Assignees: []*api.User{ { UserName: "user1", @@ -161,7 +165,11 @@ func issueCommentTestPayload() *api.IssueCommentPayload { URL: "http://localhost:3000/api/v1/repos/test/repo/issues/2", HTMLURL: "http://localhost:3000/test/repo/issues/2", Title: "crash", - Body: "this happened", + Poster: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + Body: "this happened", }, } } @@ -190,6 +198,10 @@ func pullRequestCommentTestPayload() *api.IssueCommentPayload { HTMLURL: "http://localhost:3000/test/repo/pulls/12", Title: "Fix bug", Body: "fixes bug #2", + Poster: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, }, IsPull: true, } @@ -254,6 +266,10 @@ func pullRequestTestPayload() *api.PullRequestPayload { Title: "Fix bug", Body: "fixes bug #2", Mergeable: true, + Poster: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, Assignees: []*api.User{ { UserName: "user1", From 7e30986667cc0c460a9fb38e32862e492fd4ca8d Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 24 Aug 2023 10:31:54 +0900 Subject: [PATCH 03/60] Remove ref name in PR commits page (#25876) The branch name display here is based on the repo's default branch which is not correct. ![image](https://github.com/go-gitea/gitea/assets/18380374/d899f6dc-b240-41ea-9a3e-ea0e103874ad) For example, if I changed the default branch, the branch name here will also be changed: ![image](https://github.com/go-gitea/gitea/assets/18380374/032ead94-2287-4158-a9e4-02e9fb74777d) ![image](https://github.com/go-gitea/gitea/assets/18380374/e1e6cbbc-31f8-40a2-b99a-508b5b2b3145) you can confirm this in : https://try.gitea.io/yp05327/testrepo/pulls/1/commits I think we do not need to display branch name here, as we already have the branch info above. ![image](https://github.com/go-gitea/gitea/assets/18380374/66f30a0c-3c2b-4d26-936d-bbe289f33be0) --- routers/web/repo/commit.go | 5 +---- templates/repo/commits_table.tmpl | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 7513f9360b..9a620f6d37 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -81,7 +81,6 @@ func Commits(ctx *context.Context) { ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["CommitCount"] = commitsCount - ctx.Data["RefName"] = ctx.Repo.RefName pager := context.NewPagination(int(commitsCount), pageSize, page, 5) pager.SetDefaultParams(ctx) @@ -157,7 +156,7 @@ func Graph(ctx *context.Context) { ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["CommitCount"] = commitsCount - ctx.Data["RefName"] = ctx.Repo.RefName + paginator := context.NewPagination(int(graphCommitsCount), setting.UI.GraphMaxCommitNum, page, 5) paginator.AddParam(ctx, "mode", "Mode") paginator.AddParam(ctx, "hide-pr-refs", "HidePRRefs") @@ -203,7 +202,6 @@ func SearchCommits(ctx *context.Context) { } ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name - ctx.Data["RefName"] = ctx.Repo.RefName ctx.HTML(http.StatusOK, tplCommits) } @@ -247,7 +245,6 @@ func FileHistory(ctx *context.Context) { ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["FileName"] = fileName ctx.Data["CommitCount"] = commitsCount - ctx.Data["RefName"] = ctx.Repo.RefName pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5) pager.SetDefaultParams(ctx) diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl index 415f3da0c0..0670635baf 100644 --- a/templates/repo/commits_table.tmpl +++ b/templates/repo/commits_table.tmpl @@ -1,11 +1,11 @@

{{if or .PageIsCommits (gt .CommitCount 0)}} - {{.CommitCount}} {{.locale.Tr "repo.commits.commits"}} {{if .RefName}}({{.RefName}}){{end}} + {{.CommitCount}} {{.locale.Tr "repo.commits.commits"}} {{else if .IsNothingToCompare}} - {{.locale.Tr "repo.commits.nothing_to_compare"}} {{if .RefName}}({{.RefName}}){{end}} + {{.locale.Tr "repo.commits.nothing_to_compare"}} {{else}} - {{.locale.Tr "repo.commits.no_commits" $.BaseBranch $.HeadBranch}} {{if .RefName}}({{.RefName}}){{end}} + {{.locale.Tr "repo.commits.no_commits" $.BaseBranch $.HeadBranch}} {{end}}
From b62c8e7765a371600a300f62da96483a1ae0c731 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Thu, 24 Aug 2023 10:07:00 +0800 Subject: [PATCH 04/60] feat(API): update and delete secret for managing organization secrets (#26660) - Add `UpdateSecret` function to modify org or user repo secret - Add `DeleteSecret` function to delete secret from an organization - Add `UpdateSecretOption` struct for updating secret options - Add `UpdateOrgSecret` function to update a secret in an organization - Add `DeleteOrgSecret` function to delete a secret in an organization GitHub API 1. Update Org Secret: https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-an-organization-secret 2. Delete Org Secret: https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#delete-an-organization-secret --------- Signed-off-by: Bo-Yi Wu --- models/secret/secret.go | 67 ++++++++++++++++++++ modules/structs/secret.go | 9 +++ routers/api/v1/api.go | 3 + routers/api/v1/org/action.go | 91 +++++++++++++++++++++++++++ routers/api/v1/swagger/options.go | 3 + templates/swagger/v1_json.tmpl | 100 +++++++++++++++++++++++++++++- 6 files changed, 272 insertions(+), 1 deletion(-) diff --git a/models/secret/secret.go b/models/secret/secret.go index c9c95e82d3..410cb3770e 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -6,12 +6,14 @@ package secret import ( "context" "errors" + "fmt" "strings" "code.gitea.io/gitea/models/db" secret_module "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "xorm.io/builder" ) @@ -26,6 +28,25 @@ type Secret struct { CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` } +// ErrSecretNotFound represents a "secret not found" error. +type ErrSecretNotFound struct { + Name string +} + +// IsErrSecretNotFound checks if an error is a ErrSecretNotFound. +func IsErrSecretNotFound(err error) bool { + _, ok := err.(ErrSecretNotFound) + return ok +} + +func (err ErrSecretNotFound) Error() string { + return fmt.Sprintf("secret was not found [name: %s]", err.Name) +} + +func (err ErrSecretNotFound) Unwrap() error { + return util.ErrNotExist +} + // newSecret Creates a new already encrypted secret func newSecret(ownerID, repoID int64, name, data string) *Secret { return &Secret{ @@ -93,3 +114,49 @@ func FindSecrets(ctx context.Context, opts FindSecretsOptions) ([]*Secret, error func CountSecrets(ctx context.Context, opts *FindSecretsOptions) (int64, error) { return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Secret)) } + +// UpdateSecret changes org or user reop secret. +func UpdateSecret(ctx context.Context, orgID, repoID int64, name, data string) error { + sc := new(Secret) + name = strings.ToUpper(name) + has, err := db.GetEngine(ctx). + Where("owner_id=?", orgID). + And("repo_id=?", repoID). + And("name=?", name). + Get(sc) + if err != nil { + return err + } else if !has { + return ErrSecretNotFound{Name: name} + } + + encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data) + if err != nil { + return err + } + + sc.Data = encrypted + _, err = db.GetEngine(ctx).ID(sc.ID).Cols("data").Update(sc) + return err +} + +// DeleteSecret deletes secret from an organization. +func DeleteSecret(ctx context.Context, orgID, repoID int64, name string) error { + sc := new(Secret) + has, err := db.GetEngine(ctx). + Where("owner_id=?", orgID). + And("repo_id=?", repoID). + And("name=?", strings.ToUpper(name)). + Get(sc) + if err != nil { + return err + } else if !has { + return ErrSecretNotFound{Name: name} + } + + if _, err := db.GetEngine(ctx).ID(sc.ID).Delete(new(Secret)); err != nil { + return fmt.Errorf("Delete: %w", err) + } + + return nil +} diff --git a/modules/structs/secret.go b/modules/structs/secret.go index c707eb2278..52221b51f0 100644 --- a/modules/structs/secret.go +++ b/modules/structs/secret.go @@ -25,3 +25,12 @@ type CreateSecretOption struct { // Data of the secret to create Data string `json:"data" binding:"Required"` } + +// UpdateSecretOption options when updating secret +// swagger:model +type UpdateSecretOption struct { + // Data of the secret to update + // + // required: true + Data string `json:"data" binding:"Required"` +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 9613bd610d..2d644507d5 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1301,6 +1301,9 @@ func Routes() *web.Route { m.Group("/actions/secrets", func() { m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets) m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateSecretOption{}), org.CreateOrgSecret) + m.Combo("/{secretname}"). + Put(reqToken(), reqOrgOwnership(), bind(api.UpdateSecretOption{}), org.UpdateOrgSecret). + Delete(reqToken(), reqOrgOwnership(), org.DeleteOrgSecret) }) m.Group("/public_members", func() { m.Get("", org.ListPublicMembers) diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index 7659191946..9697a11363 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -103,6 +103,10 @@ func CreateOrgSecret(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" opt := web.GetForm(ctx).(*api.CreateSecretOption) + if err := actions.NameRegexMatch(opt.Name); err != nil { + ctx.Error(http.StatusBadRequest, "CreateOrgSecret", err) + return + } s, err := secret_model.InsertEncryptedSecret( ctx, ctx.Org.Organization.ID, 0, opt.Name, actions.ReserveLineBreakForTextarea(opt.Data), ) @@ -113,3 +117,90 @@ func CreateOrgSecret(ctx *context.APIContext) { ctx.JSON(http.StatusCreated, convert.ToSecret(s)) } + +// UpdateOrgSecret update one secret of the organization +func UpdateOrgSecret(ctx *context.APIContext) { + // swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret + // --- + // summary: Update a secret value in an organization + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of organization + // type: string + // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/UpdateSecretOption" + // responses: + // "204": + // description: update one secret of the organization + // "403": + // "$ref": "#/responses/forbidden" + secretName := ctx.Params(":secretname") + opt := web.GetForm(ctx).(*api.UpdateSecretOption) + err := secret_model.UpdateSecret( + ctx, ctx.Org.Organization.ID, 0, secretName, opt.Data, + ) + if secret_model.IsErrSecretNotFound(err) { + ctx.NotFound(err) + return + } + if err != nil { + ctx.Error(http.StatusInternalServerError, "UpdateSecret", err) + return + } + + ctx.Status(http.StatusNoContent) +} + +// DeleteOrgSecret delete one secret of the organization +func DeleteOrgSecret(ctx *context.APIContext) { + // swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret + // --- + // summary: Delete a secret in an organization + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of organization + // type: string + // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true + // responses: + // "204": + // description: delete one secret of the organization + // "403": + // "$ref": "#/responses/forbidden" + secretName := ctx.Params(":secretname") + err := secret_model.DeleteSecret( + ctx, ctx.Org.Organization.ID, 0, secretName, + ) + if secret_model.IsErrSecretNotFound(err) { + ctx.NotFound(err) + return + } + if err != nil { + ctx.Error(http.StatusInternalServerError, "DeleteSecret", err) + return + } + + ctx.Status(http.StatusNoContent) +} diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 8e7e6ec3df..e41ee66776 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -190,4 +190,7 @@ type swaggerParameterBodies struct { // in:body CreateSecretOption api.CreateSecretOption + + // in:body + UpdateSecretOption api.UpdateSecretOption } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 5e75f6f8b4..aff4490899 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -1631,6 +1631,89 @@ } } }, + "/orgs/{org}/actions/secrets/{secretname}": { + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "organization" + ], + "summary": "Update a secret value in an organization", + "operationId": "updateOrgSecret", + "parameters": [ + { + "type": "string", + "description": "name of organization", + "name": "org", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the secret", + "name": "secretname", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/UpdateSecretOption" + } + } + ], + "responses": { + "204": { + "description": "update one secret of the organization" + }, + "403": { + "$ref": "#/responses/forbidden" + } + } + }, + "delete": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "organization" + ], + "summary": "Delete a secret in an organization", + "operationId": "deleteOrgSecret", + "parameters": [ + { + "type": "string", + "description": "name of organization", + "name": "org", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the secret", + "name": "secretname", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "delete one secret of the organization" + }, + "403": { + "$ref": "#/responses/forbidden" + } + } + } + }, "/orgs/{org}/activities/feeds": { "get": { "produces": [ @@ -21891,6 +21974,21 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "UpdateSecretOption": { + "description": "UpdateSecretOption options when updating secret", + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "description": "Data of the secret to update", + "type": "string", + "x-go-name": "Data" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "UpdateUserAvatarOption": { "description": "UpdateUserAvatarUserOption options when updating the user avatar", "type": "object", @@ -23207,7 +23305,7 @@ "parameterBodies": { "description": "parameterBodies", "schema": { - "$ref": "#/definitions/CreateSecretOption" + "$ref": "#/definitions/UpdateSecretOption" } }, "redirect": { From 0d55f64e6cd3de2e1e5c0ee795605823efb14231 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 24 Aug 2023 11:06:51 +0800 Subject: [PATCH 05/60] chore(actions): support cron schedule task (#26655) Replace #22751 1. only support the default branch in the repository setting. 2. autoload schedule data from the schedule table after starting the service. 3. support specific syntax like `@yearly`, `@monthly`, `@weekly`, `@daily`, `@hourly` ## How to use See the [GitHub Actions document](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule) for getting more detailed information. ```yaml on: schedule: - cron: '30 5 * * 1,3' - cron: '30 5 * * 2,4' jobs: test_schedule: runs-on: ubuntu-latest steps: - name: Not on Monday or Wednesday if: github.event.schedule != '30 5 * * 1,3' run: echo "This step will be skipped on Monday and Wednesday" - name: Every time run: echo "This step will always run" ``` Signed-off-by: Bo-Yi.Wu --------- Co-authored-by: Jason Song Co-authored-by: techknowlogick Co-authored-by: wxiaoguang Co-authored-by: Lunny Xiao --- go.mod | 2 +- models/actions/schedule.go | 120 ++++++++++++++++++++++++ models/actions/schedule_list.go | 94 +++++++++++++++++++ models/actions/schedule_spec.go | 50 ++++++++++ models/actions/schedule_spec_list.go | 106 +++++++++++++++++++++ models/migrations/migrations.go | 2 + models/migrations/v1_21/v273.go | 45 +++++++++ models/repo.go | 2 + modules/actions/workflows.go | 23 ++++- options/locale/locale_en-US.ini | 1 + services/actions/notifier_helper.go | 108 ++++++++++++++++++++- services/actions/schedule_tasks.go | 135 +++++++++++++++++++++++++++ services/cron/tasks_actions.go | 14 +++ 13 files changed, 693 insertions(+), 9 deletions(-) create mode 100644 models/actions/schedule.go create mode 100644 models/actions/schedule_list.go create mode 100644 models/actions/schedule_spec.go create mode 100644 models/actions/schedule_spec_list.go create mode 100644 models/migrations/v1_21/v273.go create mode 100644 services/actions/schedule_tasks.go diff --git a/go.mod b/go.mod index 968c0663e0..1996d29a84 100644 --- a/go.mod +++ b/go.mod @@ -90,6 +90,7 @@ require ( github.com/prometheus/client_golang v1.16.0 github.com/quasoft/websspi v1.1.2 github.com/redis/go-redis/v9 v9.0.5 + github.com/robfig/cron/v3 v3.0.1 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sassoftware/go-rpmutils v0.2.0 github.com/sergi/go-diff v1.3.1 @@ -254,7 +255,6 @@ require ( github.com/rhysd/actionlint v1.6.25 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron v1.2.0 // indirect - github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/models/actions/schedule.go b/models/actions/schedule.go new file mode 100644 index 0000000000..b0bc40dadc --- /dev/null +++ b/models/actions/schedule.go @@ -0,0 +1,120 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + "time" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/timeutil" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/robfig/cron/v3" +) + +// ActionSchedule represents a schedule of a workflow file +type ActionSchedule struct { + ID int64 + Title string + Specs []string + RepoID int64 `xorm:"index"` + Repo *repo_model.Repository `xorm:"-"` + OwnerID int64 `xorm:"index"` + WorkflowID string + TriggerUserID int64 + TriggerUser *user_model.User `xorm:"-"` + Ref string + CommitSHA string + Event webhook_module.HookEventType + EventPayload string `xorm:"LONGTEXT"` + Content []byte + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` +} + +func init() { + db.RegisterModel(new(ActionSchedule)) +} + +// GetSchedulesMapByIDs returns the schedules by given id slice. +func GetSchedulesMapByIDs(ids []int64) (map[int64]*ActionSchedule, error) { + schedules := make(map[int64]*ActionSchedule, len(ids)) + return schedules, db.GetEngine(db.DefaultContext).In("id", ids).Find(&schedules) +} + +// GetReposMapByIDs returns the repos by given id slice. +func GetReposMapByIDs(ids []int64) (map[int64]*repo_model.Repository, error) { + repos := make(map[int64]*repo_model.Repository, len(ids)) + return repos, db.GetEngine(db.DefaultContext).In("id", ids).Find(&repos) +} + +var cronParser = cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor) + +// CreateScheduleTask creates new schedule task. +func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error { + // Return early if there are no rows to insert + if len(rows) == 0 { + return nil + } + + // Begin transaction + ctx, committer, err := db.TxContext(ctx) + if err != nil { + return err + } + defer committer.Close() + + // Loop through each schedule row + for _, row := range rows { + // Create new schedule row + if err = db.Insert(ctx, row); err != nil { + return err + } + + // Loop through each schedule spec and create a new spec row + now := time.Now() + + for _, spec := range row.Specs { + // Parse the spec and check for errors + schedule, err := cronParser.Parse(spec) + if err != nil { + continue // skip to the next spec if there's an error + } + + // Insert the new schedule spec row + if err = db.Insert(ctx, &ActionScheduleSpec{ + RepoID: row.RepoID, + ScheduleID: row.ID, + Spec: spec, + Next: timeutil.TimeStamp(schedule.Next(now).Unix()), + }); err != nil { + return err + } + } + } + + // Commit transaction + return committer.Commit() +} + +func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error { + ctx, committer, err := db.TxContext(ctx) + if err != nil { + return err + } + defer committer.Close() + + if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil { + return err + } + + if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil { + return err + } + + return committer.Commit() +} diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go new file mode 100644 index 0000000000..e873c05ec3 --- /dev/null +++ b/models/actions/schedule_list.go @@ -0,0 +1,94 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" + + "xorm.io/builder" +) + +type ScheduleList []*ActionSchedule + +// GetUserIDs returns a slice of user's id +func (schedules ScheduleList) GetUserIDs() []int64 { + ids := make(container.Set[int64], len(schedules)) + for _, schedule := range schedules { + ids.Add(schedule.TriggerUserID) + } + return ids.Values() +} + +func (schedules ScheduleList) GetRepoIDs() []int64 { + ids := make(container.Set[int64], len(schedules)) + for _, schedule := range schedules { + ids.Add(schedule.RepoID) + } + return ids.Values() +} + +func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error { + userIDs := schedules.GetUserIDs() + users := make(map[int64]*user_model.User, len(userIDs)) + if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil { + return err + } + for _, schedule := range schedules { + if schedule.TriggerUserID == user_model.ActionsUserID { + schedule.TriggerUser = user_model.NewActionsUser() + } else { + schedule.TriggerUser = users[schedule.TriggerUserID] + } + } + return nil +} + +func (schedules ScheduleList) LoadRepos() error { + repoIDs := schedules.GetRepoIDs() + repos, err := repo_model.GetRepositoriesMapByIDs(repoIDs) + if err != nil { + return err + } + for _, schedule := range schedules { + schedule.Repo = repos[schedule.RepoID] + } + return nil +} + +type FindScheduleOptions struct { + db.ListOptions + RepoID int64 + OwnerID int64 +} + +func (opts FindScheduleOptions) toConds() builder.Cond { + cond := builder.NewCond() + if opts.RepoID > 0 { + cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + } + if opts.OwnerID > 0 { + cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) + } + + return cond +} + +func FindSchedules(ctx context.Context, opts FindScheduleOptions) (ScheduleList, int64, error) { + e := db.GetEngine(ctx).Where(opts.toConds()) + if !opts.ListAll && opts.PageSize > 0 && opts.Page >= 1 { + e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) + } + var schedules ScheduleList + total, err := e.Desc("id").FindAndCount(&schedules) + return schedules, total, err +} + +func CountSchedules(ctx context.Context, opts FindScheduleOptions) (int64, error) { + return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionSchedule)) +} diff --git a/models/actions/schedule_spec.go b/models/actions/schedule_spec.go new file mode 100644 index 0000000000..91240459a0 --- /dev/null +++ b/models/actions/schedule_spec.go @@ -0,0 +1,50 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/timeutil" + + "github.com/robfig/cron/v3" +) + +// ActionScheduleSpec represents a schedule spec of a workflow file +type ActionScheduleSpec struct { + ID int64 + RepoID int64 `xorm:"index"` + Repo *repo_model.Repository `xorm:"-"` + ScheduleID int64 `xorm:"index"` + Schedule *ActionSchedule `xorm:"-"` + + // Next time the job will run, or the zero time if Cron has not been + // started or this entry's schedule is unsatisfiable + Next timeutil.TimeStamp `xorm:"index"` + // Prev is the last time this job was run, or the zero time if never. + Prev timeutil.TimeStamp + Spec string + + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` +} + +func (s *ActionScheduleSpec) Parse() (cron.Schedule, error) { + return cronParser.Parse(s.Spec) +} + +func init() { + db.RegisterModel(new(ActionScheduleSpec)) +} + +func UpdateScheduleSpec(ctx context.Context, spec *ActionScheduleSpec, cols ...string) error { + sess := db.GetEngine(ctx).ID(spec.ID) + if len(cols) > 0 { + sess.Cols(cols...) + } + _, err := sess.Update(spec) + return err +} diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go new file mode 100644 index 0000000000..d379490b4e --- /dev/null +++ b/models/actions/schedule_spec_list.go @@ -0,0 +1,106 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/container" + + "xorm.io/builder" +) + +type SpecList []*ActionScheduleSpec + +func (specs SpecList) GetScheduleIDs() []int64 { + ids := make(container.Set[int64], len(specs)) + for _, spec := range specs { + ids.Add(spec.ScheduleID) + } + return ids.Values() +} + +func (specs SpecList) LoadSchedules() error { + scheduleIDs := specs.GetScheduleIDs() + schedules, err := GetSchedulesMapByIDs(scheduleIDs) + if err != nil { + return err + } + for _, spec := range specs { + spec.Schedule = schedules[spec.ScheduleID] + } + + repoIDs := specs.GetRepoIDs() + repos, err := GetReposMapByIDs(repoIDs) + if err != nil { + return err + } + for _, spec := range specs { + spec.Repo = repos[spec.RepoID] + } + + return nil +} + +func (specs SpecList) GetRepoIDs() []int64 { + ids := make(container.Set[int64], len(specs)) + for _, spec := range specs { + ids.Add(spec.RepoID) + } + return ids.Values() +} + +func (specs SpecList) LoadRepos() error { + repoIDs := specs.GetRepoIDs() + repos, err := repo_model.GetRepositoriesMapByIDs(repoIDs) + if err != nil { + return err + } + for _, spec := range specs { + spec.Repo = repos[spec.RepoID] + } + return nil +} + +type FindSpecOptions struct { + db.ListOptions + RepoID int64 + Next int64 +} + +func (opts FindSpecOptions) toConds() builder.Cond { + cond := builder.NewCond() + if opts.RepoID > 0 { + cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + } + + if opts.Next > 0 { + cond = cond.And(builder.Lte{"next": opts.Next}) + } + + return cond +} + +func FindSpecs(ctx context.Context, opts FindSpecOptions) (SpecList, int64, error) { + e := db.GetEngine(ctx).Where(opts.toConds()) + if opts.PageSize > 0 && opts.Page >= 1 { + e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) + } + var specs SpecList + total, err := e.Desc("id").FindAndCount(&specs) + if err != nil { + return nil, 0, err + } + + if err := specs.LoadSchedules(); err != nil { + return nil, 0, err + } + return specs, total, nil +} + +func CountSpecs(ctx context.Context, opts FindSpecOptions) (int64, error) { + return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionScheduleSpec)) +} diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 87c597b573..9f4acda236 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -526,6 +526,8 @@ var migrations = []Migration{ NewMigration("Allow archiving labels", v1_21.AddArchivedUnixColumInLabelTable), // v272 -> v273 NewMigration("Add Version to ActionRun table", v1_21.AddVersionToActionRunTable), + // v273 -> v274 + NewMigration("Add Action Schedule Table", v1_21.AddActionScheduleTable), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_21/v273.go b/models/migrations/v1_21/v273.go new file mode 100644 index 0000000000..61c79f4a76 --- /dev/null +++ b/models/migrations/v1_21/v273.go @@ -0,0 +1,45 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_21 //nolint +import ( + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +func AddActionScheduleTable(x *xorm.Engine) error { + type ActionSchedule struct { + ID int64 + Title string + Specs []string + RepoID int64 `xorm:"index"` + OwnerID int64 `xorm:"index"` + WorkflowID string + TriggerUserID int64 + Ref string + CommitSHA string + Event string + EventPayload string `xorm:"LONGTEXT"` + Content []byte + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` + } + + type ActionScheduleSpec struct { + ID int64 + RepoID int64 `xorm:"index"` + ScheduleID int64 `xorm:"index"` + Spec string + Next timeutil.TimeStamp `xorm:"index"` + Prev timeutil.TimeStamp + + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` + } + + return x.Sync( + new(ActionSchedule), + new(ActionScheduleSpec), + ) +} diff --git a/models/repo.go b/models/repo.go index 7579d2ad73..74a88d4c48 100644 --- a/models/repo.go +++ b/models/repo.go @@ -170,6 +170,8 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { &actions_model.ActionRunJob{RepoID: repoID}, &actions_model.ActionRun{RepoID: repoID}, &actions_model.ActionRunner{RepoID: repoID}, + &actions_model.ActionScheduleSpec{RepoID: repoID}, + &actions_model.ActionSchedule{RepoID: repoID}, &actions_model.ActionArtifact{RepoID: repoID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index de340a74ec..408fdb8f8e 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -95,18 +95,25 @@ func GetEventsFromContent(content []byte) ([]*jobparser.Event, error) { return events, nil } -func DetectWorkflows(gitRepo *git.Repository, commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader) ([]*DetectedWorkflow, error) { +func DetectWorkflows( + gitRepo *git.Repository, + commit *git.Commit, + triggedEvent webhook_module.HookEventType, + payload api.Payloader, +) ([]*DetectedWorkflow, []*DetectedWorkflow, error) { entries, err := ListWorkflows(commit) if err != nil { - return nil, err + return nil, nil, err } workflows := make([]*DetectedWorkflow, 0, len(entries)) + schedules := make([]*DetectedWorkflow, 0, len(entries)) for _, entry := range entries { content, err := GetContentFromEntry(entry) if err != nil { - return nil, err + return nil, nil, err } + events, err := GetEventsFromContent(content) if err != nil { log.Warn("ignore invalid workflow %q: %v", entry.Name(), err) @@ -114,6 +121,14 @@ func DetectWorkflows(gitRepo *git.Repository, commit *git.Commit, triggedEvent w } for _, evt := range events { log.Trace("detect workflow %q for event %#v matching %q", entry.Name(), evt, triggedEvent) + if evt.IsSchedule() { + dwf := &DetectedWorkflow{ + EntryName: entry.Name(), + TriggerEvent: evt.Name, + Content: content, + } + schedules = append(schedules, dwf) + } if detectMatched(gitRepo, commit, triggedEvent, payload, evt) { dwf := &DetectedWorkflow{ EntryName: entry.Name(), @@ -125,7 +140,7 @@ func DetectWorkflows(gitRepo *git.Repository, commit *git.Commit, triggedEvent w } } - return workflows, nil + return workflows, schedules, nil } func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 587a2b14bc..f08d2b7eae 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2756,6 +2756,7 @@ dashboard.gc_lfs = Garbage collect LFS meta objects dashboard.stop_zombie_tasks = Stop zombie tasks dashboard.stop_endless_tasks = Stop endless tasks dashboard.cancel_abandoned_jobs = Cancel abandoned jobs +dashboard.start_schedule_tasks = Start schedule tasks dashboard.sync_branch.started = Branches Sync started dashboard.rebuild_issue_indexer = Rebuild issue indexer diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 75c99ff19c..ff00e48c64 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -4,6 +4,7 @@ package actions import ( + "bytes" "context" "fmt" "strings" @@ -24,6 +25,7 @@ import ( "code.gitea.io/gitea/services/convert" "github.com/nektos/act/pkg/jobparser" + "github.com/nektos/act/pkg/model" ) var methodCtxKey struct{} @@ -143,15 +145,15 @@ func notify(ctx context.Context, input *notifyInput) error { } var detectedWorkflows []*actions_module.DetectedWorkflow - workflows, err := actions_module.DetectWorkflows(gitRepo, commit, input.Event, input.Payload) + actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig() + workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit, input.Event, input.Payload) if err != nil { return fmt.Errorf("DetectWorkflows: %w", err) } + if len(workflows) == 0 { log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID) } else { - actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig() - for _, wf := range workflows { if actionsConfig.IsWorkflowDisabled(wf.EntryName) { log.Trace("repo %s has disable workflows %s", input.Repo.RepoPath(), wf.EntryName) @@ -171,7 +173,7 @@ func notify(ctx context.Context, input *notifyInput) error { if err != nil { return fmt.Errorf("gitRepo.GetCommit: %w", err) } - baseWorkflows, err := actions_module.DetectWorkflows(gitRepo, baseCommit, input.Event, input.Payload) + baseWorkflows, _, err := actions_module.DetectWorkflows(gitRepo, baseCommit, input.Event, input.Payload) if err != nil { return fmt.Errorf("DetectWorkflows: %w", err) } @@ -186,7 +188,22 @@ func notify(ctx context.Context, input *notifyInput) error { } } + if err := handleSchedules(ctx, schedules, commit, input); err != nil { + return err + } + + return handleWorkflows(ctx, detectedWorkflows, commit, input, ref) +} + +func handleWorkflows( + ctx context.Context, + detectedWorkflows []*actions_module.DetectedWorkflow, + commit *git.Commit, + input *notifyInput, + ref string, +) error { if len(detectedWorkflows) == 0 { + log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID) return nil } @@ -350,3 +367,86 @@ func ifNeedApproval(ctx context.Context, run *actions_model.ActionRun, repo *rep log.Trace("need approval because it's the first time user %d triggered actions", user.ID) return true, nil } + +func handleSchedules( + ctx context.Context, + detectedWorkflows []*actions_module.DetectedWorkflow, + commit *git.Commit, + input *notifyInput, +) error { + if len(detectedWorkflows) == 0 { + log.Trace("repo %s with commit %s couldn't find schedules", input.Repo.RepoPath(), commit.ID) + return nil + } + + branch, err := commit.GetBranchName() + if err != nil { + return err + } + if branch != input.Repo.DefaultBranch { + log.Trace("commit branch is not default branch in repo") + return nil + } + + rows, _, err := actions_model.FindSchedules(ctx, actions_model.FindScheduleOptions{RepoID: input.Repo.ID}) + if err != nil { + log.Error("FindCrons: %v", err) + return err + } + + if len(rows) > 0 { + if err := actions_model.DeleteScheduleTaskByRepo(ctx, input.Repo.ID); err != nil { + log.Error("DeleteCronTaskByRepo: %v", err) + } + } + + p, err := json.Marshal(input.Payload) + if err != nil { + return fmt.Errorf("json.Marshal: %w", err) + } + + crons := make([]*actions_model.ActionSchedule, 0, len(detectedWorkflows)) + for _, dwf := range detectedWorkflows { + // Check cron job condition. Only working in default branch + workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content)) + if err != nil { + log.Error("ReadWorkflow: %v", err) + continue + } + schedules := workflow.OnSchedule() + if len(schedules) == 0 { + log.Warn("no schedule event") + continue + } + + run := &actions_model.ActionSchedule{ + Title: strings.SplitN(commit.CommitMessage, "\n", 2)[0], + RepoID: input.Repo.ID, + OwnerID: input.Repo.OwnerID, + WorkflowID: dwf.EntryName, + TriggerUserID: input.Doer.ID, + Ref: input.Ref, + CommitSHA: commit.ID.String(), + Event: input.Event, + EventPayload: string(p), + Specs: schedules, + Content: dwf.Content, + } + + // cancel running jobs if the event is push + if run.Event == webhook_module.HookEventPush { + // cancel running jobs of the same workflow + if err := actions_model.CancelRunningJobs( + ctx, + run.RepoID, + run.Ref, + run.WorkflowID, + ); err != nil { + log.Error("CancelRunningJobs: %v", err) + } + } + crons = append(crons, run) + } + + return actions_model.CreateScheduleTask(ctx, crons) +} diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go new file mode 100644 index 0000000000..87131e0aab --- /dev/null +++ b/services/actions/schedule_tasks.go @@ -0,0 +1,135 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + "fmt" + "time" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/timeutil" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/nektos/act/pkg/jobparser" +) + +// StartScheduleTasks start the task +func StartScheduleTasks(ctx context.Context) error { + return startTasks(ctx) +} + +// startTasks retrieves specifications in pages, creates a schedule task for each specification, +// and updates the specification's next run time and previous run time. +// The function returns an error if there's an issue with finding or updating the specifications. +func startTasks(ctx context.Context) error { + // Set the page size + pageSize := 50 + + // Retrieve specs in pages until all specs have been retrieved + now := time.Now() + for page := 1; ; page++ { + // Retrieve the specs for the current page + specs, _, err := actions_model.FindSpecs(ctx, actions_model.FindSpecOptions{ + ListOptions: db.ListOptions{ + Page: page, + PageSize: pageSize, + }, + Next: now.Unix(), + }) + if err != nil { + return fmt.Errorf("find specs: %w", err) + } + + // Loop through each spec and create a schedule task for it + for _, row := range specs { + // cancel running jobs if the event is push + if row.Schedule.Event == webhook_module.HookEventPush { + // cancel running jobs of the same workflow + if err := actions_model.CancelRunningJobs( + ctx, + row.RepoID, + row.Schedule.Ref, + row.Schedule.WorkflowID, + ); err != nil { + log.Error("CancelRunningJobs: %v", err) + } + } + + if err := CreateScheduleTask(ctx, row.Schedule); err != nil { + log.Error("CreateScheduleTask: %v", err) + return err + } + + // Parse the spec + schedule, err := row.Parse() + if err != nil { + log.Error("Parse: %v", err) + return err + } + + // Update the spec's next run time and previous run time + row.Prev = row.Next + row.Next = timeutil.TimeStamp(schedule.Next(now.Add(1 * time.Minute)).Unix()) + if err := actions_model.UpdateScheduleSpec(ctx, row, "prev", "next"); err != nil { + log.Error("UpdateScheduleSpec: %v", err) + return err + } + } + + // Stop if all specs have been retrieved + if len(specs) < pageSize { + break + } + } + + return nil +} + +// CreateScheduleTask creates a scheduled task from a cron action schedule. +// It creates an action run based on the schedule, inserts it into the database, and creates commit statuses for each job. +func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) error { + // Create a new action run based on the schedule + run := &actions_model.ActionRun{ + Title: cron.Title, + RepoID: cron.RepoID, + OwnerID: cron.OwnerID, + WorkflowID: cron.WorkflowID, + TriggerUserID: cron.TriggerUserID, + Ref: cron.Ref, + CommitSHA: cron.CommitSHA, + Event: cron.Event, + EventPayload: cron.EventPayload, + Status: actions_model.StatusWaiting, + } + + // Parse the workflow specification from the cron schedule + workflows, err := jobparser.Parse(cron.Content) + if err != nil { + return err + } + + // Insert the action run and its associated jobs into the database + if err := actions_model.InsertRun(ctx, run, workflows); err != nil { + return err + } + + // Retrieve the jobs for the newly created action run + jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: run.ID}) + if err != nil { + return err + } + + // Create commit statuses for each job + for _, job := range jobs { + if err := createCommitStatus(ctx, job); err != nil { + return err + } + } + + // Return nil if no errors occurred + return nil +} diff --git a/services/cron/tasks_actions.go b/services/cron/tasks_actions.go index 30e8749a5e..0875792503 100644 --- a/services/cron/tasks_actions.go +++ b/services/cron/tasks_actions.go @@ -18,6 +18,7 @@ func initActionsTasks() { registerStopZombieTasks() registerStopEndlessTasks() registerCancelAbandonedJobs() + registerScheduleTasks() } func registerStopZombieTasks() { @@ -49,3 +50,16 @@ func registerCancelAbandonedJobs() { return actions_service.CancelAbandonedJobs(ctx) }) } + +// registerScheduleTasks registers a scheduled task that runs every minute to start any due schedule tasks. +func registerScheduleTasks() { + // Register the task with a unique name, enabled status, and schedule for every minute. + RegisterTaskFatal("start_schedule_tasks", &BaseConfig{ + Enabled: true, + RunAtStart: false, + Schedule: "@every 1m", + }, func(ctx context.Context, _ *user_model.User, cfg Config) error { + // Call the function to start schedule tasks and pass the context. + return actions_service.StartScheduleTasks(ctx) + }) +} From d2e4039def61d9cc9952be462216001125327270 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 24 Aug 2023 14:06:17 +0900 Subject: [PATCH 06/60] Add `member`, `collaborator`, `contributor`, and `first-time contributor` roles and tooltips (#26658) GitHub like role descriptor ![image](https://github.com/go-gitea/gitea/assets/18380374/ceaed92c-6749-47b3-89e8-0e0e7ae65321) ![image](https://github.com/go-gitea/gitea/assets/18380374/8193ec34-cbf0-47f9-b0de-10dbddd66970) ![image](https://github.com/go-gitea/gitea/assets/18380374/56c7ed85-6177-425e-9f2f-926e99770782) --------- Co-authored-by: delvh Co-authored-by: wxiaoguang Co-authored-by: Lunny Xiao --- models/issues/comment.go | 47 +++++----- models/issues/pull_list.go | 13 +++ options/locale/locale_en-US.ini | 15 +++- routers/web/repo/issue.go | 87 ++++++++++++------- .../repo/issue/view_content/show_role.tmpl | 17 ++-- 5 files changed, 106 insertions(+), 73 deletions(-) diff --git a/models/issues/comment.go b/models/issues/comment.go index e781931261..17e579b455 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -23,6 +23,7 @@ import ( "code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" "xorm.io/builder" @@ -181,40 +182,32 @@ func (t CommentType) HasAttachmentSupport() bool { return false } -// RoleDescriptor defines comment tag type -type RoleDescriptor int +// RoleInRepo presents the user's participation in the repo +type RoleInRepo string + +// RoleDescriptor defines comment "role" tags +type RoleDescriptor struct { + IsPoster bool + RoleInRepo RoleInRepo +} // Enumerate all the role tags. const ( - RoleDescriptorNone RoleDescriptor = iota - RoleDescriptorPoster - RoleDescriptorWriter - RoleDescriptorOwner + RoleRepoOwner RoleInRepo = "owner" + RoleRepoMember RoleInRepo = "member" + RoleRepoCollaborator RoleInRepo = "collaborator" + RoleRepoFirstTimeContributor RoleInRepo = "first_time_contributor" + RoleRepoContributor RoleInRepo = "contributor" ) -// WithRole enable a specific tag on the RoleDescriptor. -func (rd RoleDescriptor) WithRole(role RoleDescriptor) RoleDescriptor { - return rd | (1 << role) +// LocaleString returns the locale string name of the role +func (r RoleInRepo) LocaleString(lang translation.Locale) string { + return lang.Tr("repo.issues.role." + string(r)) } -func stringToRoleDescriptor(role string) RoleDescriptor { - switch role { - case "Poster": - return RoleDescriptorPoster - case "Writer": - return RoleDescriptorWriter - case "Owner": - return RoleDescriptorOwner - default: - return RoleDescriptorNone - } -} - -// HasRole returns if a certain role is enabled on the RoleDescriptor. -func (rd RoleDescriptor) HasRole(role string) bool { - roleDescriptor := stringToRoleDescriptor(role) - bitValue := rd & (1 << roleDescriptor) - return (bitValue > 0) +// LocaleHelper returns the locale tooltip of the role +func (r RoleInRepo) LocaleHelper(lang translation.Locale) string { + return lang.Tr("repo.issues.role." + string(r) + "_helper") } // Comment represents a comment in commit and issue page. diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index 3b2416900b..c4506ef150 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -199,3 +199,16 @@ func (prs PullRequestList) GetIssueIDs() []int64 { } return issueIDs } + +// HasMergedPullRequestInRepo returns whether the user(poster) has merged pull-request in the repo +func HasMergedPullRequestInRepo(ctx context.Context, repoID, posterID int64) (bool, error) { + return db.GetEngine(ctx). + Join("INNER", "pull_request", "pull_request.issue_id = issue.id"). + Where("repo_id=?", repoID). + And("poster_id=?", posterID). + And("is_pull=?", true). + And("pull_request.has_merged=?", true). + Select("issue.id"). + Limit(1). + Get(new(Issue)) +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index f08d2b7eae..e32399dd89 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1480,9 +1480,18 @@ issues.ref_reopening_from = `referenced a pull request %[4]s tha issues.ref_closed_from = `closed this issue %[4]s %[2]s` issues.ref_reopened_from = `reopened this issue %[4]s %[2]s` issues.ref_from = `from %[1]s` -issues.poster = Poster -issues.collaborator = Collaborator -issues.owner = Owner +issues.author = Author +issues.author_helper = This user is the author. +issues.role.owner = Owner +issues.role.owner_helper = This user is the owner of this repository. +issues.role.member = Member +issues.role.member_helper = This user is a member of the organization owning this repository. +issues.role.collaborator = Collaborator +issues.role.collaborator_helper = This user has been invited to collaborate on the repository. +issues.role.first_time_contributor = First-time contributor +issues.role.first_time_contributor_helper = This is the first contribution of this user to the repository. +issues.role.contributor = Contributor +issues.role.contributor_helper = This user has previously committed to the repository. issues.re_request_review=Re-request review issues.is_stale = There have been changes to this PR since this review issues.remove_request_review=Remove review request diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b04802e452..9a2add1452 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1228,47 +1228,70 @@ func NewIssuePost(ctx *context.Context) { } } -// roleDescriptor returns the Role Descriptor for a comment in/with the given repo, poster and issue +// roleDescriptor returns the role descriptor for a comment in/with the given repo, poster and issue func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *user_model.User, issue *issues_model.Issue, hasOriginalAuthor bool) (issues_model.RoleDescriptor, error) { + roleDescriptor := issues_model.RoleDescriptor{} + if hasOriginalAuthor { - return issues_model.RoleDescriptorNone, nil + return roleDescriptor, nil } perm, err := access_model.GetUserRepoPermission(ctx, repo, poster) if err != nil { - return issues_model.RoleDescriptorNone, err - } - - // By default the poster has no roles on the comment. - roleDescriptor := issues_model.RoleDescriptorNone - - // Check if the poster is owner of the repo. - if perm.IsOwner() { - // If the poster isn't a admin, enable the owner role. - if !poster.IsAdmin { - roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorOwner) - } else { - - // Otherwise check if poster is the real repo admin. - ok, err := access_model.IsUserRealRepoAdmin(repo, poster) - if err != nil { - return issues_model.RoleDescriptorNone, err - } - if ok { - roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorOwner) - } - } - } - - // Is the poster can write issues or pulls to the repo, enable the Writer role. - // Only enable this if the poster doesn't have the owner role already. - if !roleDescriptor.HasRole("Owner") && perm.CanWriteIssuesOrPulls(issue.IsPull) { - roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorWriter) + return roleDescriptor, err } // If the poster is the actual poster of the issue, enable Poster role. - if issue.IsPoster(poster.ID) { - roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorPoster) + roleDescriptor.IsPoster = issue.IsPoster(poster.ID) + + // Check if the poster is owner of the repo. + if perm.IsOwner() { + // If the poster isn't an admin, enable the owner role. + if !poster.IsAdmin { + roleDescriptor.RoleInRepo = issues_model.RoleRepoOwner + return roleDescriptor, nil + } + + // Otherwise check if poster is the real repo admin. + ok, err := access_model.IsUserRealRepoAdmin(repo, poster) + if err != nil { + return roleDescriptor, err + } + if ok { + roleDescriptor.RoleInRepo = issues_model.RoleRepoOwner + return roleDescriptor, nil + } + } + + // If repo is organization, check Member role + if err := repo.LoadOwner(ctx); err != nil { + return roleDescriptor, err + } + if repo.Owner.IsOrganization() { + if isMember, err := organization.IsOrganizationMember(ctx, repo.Owner.ID, poster.ID); err != nil { + return roleDescriptor, err + } else if isMember { + roleDescriptor.RoleInRepo = issues_model.RoleRepoMember + return roleDescriptor, nil + } + } + + // If the poster is the collaborator of the repo + if isCollaborator, err := repo_model.IsCollaborator(ctx, repo.ID, poster.ID); err != nil { + return roleDescriptor, err + } else if isCollaborator { + roleDescriptor.RoleInRepo = issues_model.RoleRepoCollaborator + return roleDescriptor, nil + } + + hasMergedPR, err := issues_model.HasMergedPullRequestInRepo(ctx, repo.ID, poster.ID) + if err != nil { + return roleDescriptor, err + } else if hasMergedPR { + roleDescriptor.RoleInRepo = issues_model.RoleRepoContributor + } else { + // only display first time contributor in the first opening pull request + roleDescriptor.RoleInRepo = issues_model.RoleRepoFirstTimeContributor } return roleDescriptor, nil diff --git a/templates/repo/issue/view_content/show_role.tmpl b/templates/repo/issue/view_content/show_role.tmpl index f85f43bd66..40c8b67fa9 100644 --- a/templates/repo/issue/view_content/show_role.tmpl +++ b/templates/repo/issue/view_content/show_role.tmpl @@ -1,15 +1,10 @@ -{{if and (.ShowRole.HasRole "Poster") (not .IgnorePoster)}} -
- {{ctx.Locale.Tr "repo.issues.poster"}} +{{if and .ShowRole.IsPoster (not .IgnorePoster)}} +
+ {{ctx.Locale.Tr "repo.issues.author"}}
{{end}} -{{if (.ShowRole.HasRole "Writer")}} -
- {{ctx.Locale.Tr "repo.issues.collaborator"}} -
-{{end}} -{{if (.ShowRole.HasRole "Owner")}} -
- {{ctx.Locale.Tr "repo.issues.owner"}} +{{if .ShowRole.RoleInRepo}} +
+ {{.ShowRole.RoleInRepo.LocaleString ctx.Locale}}
{{end}} From 86ee5b4b1b6acdba89912a3c89c39f9948e67f07 Mon Sep 17 00:00:00 2001 From: Infinoid Date: Thu, 24 Aug 2023 01:36:04 -0400 Subject: [PATCH 07/60] PATCH branch-protection updates check list even when checks are disabled (#26351) Fixes: #26333. Previously, this endpoint only updates the `StatusCheckContexts` field when `EnableStatusCheck==true`, which makes it impossible to clear the array otherwise. This patch uses slice `nil`-ness to decide whether to update the list of checks. The field is ignored when either the client explicitly passes in a null, or just omits the field from the json ([which causes `json.Unmarshal` to leave the struct field unchanged](https://go.dev/play/p/Z2XHOILuB1Q)). I think this is a better measure of intent than whether the `EnableStatusCheck` flag was set, because it matches the semantics of other field types. Also adds a test case. I noticed that [`testAPIEditBranchProtection` only checks the branch name](https://github.com/go-gitea/gitea/blob/c1c83dbaec840871c1247f4bc3f875309b0de6bb/tests/integration/api_branch_test.go#L68) and no other fields, so I added some extra `GET` calls and specific checks to make sure the fields are changing properly. I added those checks the existing integration test; is that the right place for it? --- routers/api/v1/repo/branch.go | 3 ++- tests/integration/api_branch_test.go | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 577776dadd..cdc176b8e4 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -768,7 +768,8 @@ func EditBranchProtection(ctx *context.APIContext) { if form.EnableStatusCheck != nil { protectBranch.EnableStatusCheck = *form.EnableStatusCheck } - if protectBranch.EnableStatusCheck { + + if form.StatusCheckContexts != nil { protectBranch.StatusCheckContexts = form.StatusCheckContexts } diff --git a/tests/integration/api_branch_test.go b/tests/integration/api_branch_test.go index dd81ec22dd..bc026c117f 100644 --- a/tests/integration/api_branch_test.go +++ b/tests/integration/api_branch_test.go @@ -31,7 +31,7 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) { assert.True(t, branch.UserCanMerge) } -func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { +func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) *api.BranchProtection { token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadRepository) req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) resp := MakeRequest(t, req, expectedHTTPStatus) @@ -40,7 +40,9 @@ func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPSta var branchProtection api.BranchProtection DecodeJSON(t, resp, &branchProtection) assert.EqualValues(t, branchName, branchProtection.RuleName) + return &branchProtection } + return nil } func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { @@ -186,6 +188,24 @@ func TestAPIBranchProtection(t *testing.T) { EnablePush: true, }, http.StatusOK) + // enable status checks, require the "test1" check to pass + testAPIEditBranchProtection(t, "master", &api.BranchProtection{ + EnableStatusCheck: true, + StatusCheckContexts: []string{"test1"}, + }, http.StatusOK) + bp := testAPIGetBranchProtection(t, "master", http.StatusOK) + assert.Equal(t, true, bp.EnableStatusCheck) + assert.Equal(t, []string{"test1"}, bp.StatusCheckContexts) + + // disable status checks, clear the list of required checks + testAPIEditBranchProtection(t, "master", &api.BranchProtection{ + EnableStatusCheck: false, + StatusCheckContexts: []string{}, + }, http.StatusOK) + bp = testAPIGetBranchProtection(t, "master", http.StatusOK) + assert.Equal(t, false, bp.EnableStatusCheck) + assert.Equal(t, []string{}, bp.StatusCheckContexts) + testAPIDeleteBranchProtection(t, "master", http.StatusNoContent) // Test branch deletion From b21b63c61ac1038a1597acee45084896f5e39a3a Mon Sep 17 00:00:00 2001 From: Laurent Cahour Date: Thu, 24 Aug 2023 12:03:49 +0200 Subject: [PATCH 08/60] Add merge files files to GetCommitFileStatus (#20515) Hi, We'd like to add merge files files to GetCommitFileStatus fucntions so API returns the list of all the files associated to a merged pull request commit, like GitHub API does. The list of affectedFiles for an API commit is fetched from toCommit() function in routers/api/v1/repo/commits.go, and API was returning no file in case of a pull request with no conflict, or just files associated to the confict resolution, but NOT the full list of merged files. This would lead to situations where a CI polling a repo for changes could miss some file changes due to API returning an empty / partial list in case of such merged pull requests. (Hope this makes sense :) ) NOTE: I'd like to add a unittest in integrations/api_repo_git_commits_test.go but failed to understand how to add my own test bare repo so I can make a test on a merged pull request commit to check for affectedFiles. Is there a merged pull request in there that I could use maybe? Could someone please direct me to the relevant ressources with informations on how to do that please? Thanks for your time, Laurent. --------- Co-authored-by: Thomas Desveaux --- modules/git/commit.go | 2 +- modules/git/commit_test.go | 23 ++++++++++++++++++ modules/git/tests/repos/repo6_merge/HEAD | 1 + .../02/2f4ce6214973e018f02bf363bf8a2e3691f699 | Bin 0 -> 280 bytes .../05/45879290cc368a8becebc4aa34002c52d5fecc | Bin 0 -> 152 bytes .../1e/5d0a65fe099ef12d24b28f783896e4b8172576 | Bin 0 -> 88 bytes .../37/d35c7ed39e4e16d0b579a5b995b7e30b0e9411 | 2 ++ .../38/ec3e0cdc88bde01014bda4a5dd9fc835f41439 | 2 ++ .../66/7e0fbc6bc02c2285d17f542e89b23c0fa5482b | Bin 0 -> 122 bytes .../9f/d90b1d524c0fea776ed5e6476da02ea1740597 | Bin 0 -> 26 bytes .../ae/4b035e7c4afbc000576cee3f713ea0c2f1e1e2 | 5 ++++ .../ba/2906d0666cf726c7eaadd2cd3db615dedfdf3a | Bin 0 -> 20 bytes .../c1/a95c2eff8151c6d1437a0d5d3322a73ff38fb8 | Bin 0 -> 120 bytes .../cc/d1d4d594029e68c388ecef5aa3063fa1055831 | Bin 0 -> 26 bytes .../cd/fc1aaf7a149151cb7bff639fafe05668d4bbd2 | Bin 0 -> 120 bytes .../d1/792641396ff7630d35fbb0b74b86b0c71bca77 | Bin 0 -> 145 bytes .../ec/d11d8da0f25eaa99f64a37a82da98685f381e2 | Bin 0 -> 33 bytes .../fa/49b077972391ad58037050f2a75f74e3671e92 | Bin 0 -> 24 bytes .../tests/repos/repo6_merge/refs/heads/main | 1 + .../repo6_merge/refs/heads/merge/add_file | 1 + .../repo6_merge/refs/heads/merge/modify_file | 1 + .../repo6_merge/refs/heads/merge/remove_file | 1 + 22 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 modules/git/tests/repos/repo6_merge/HEAD create mode 100644 modules/git/tests/repos/repo6_merge/objects/02/2f4ce6214973e018f02bf363bf8a2e3691f699 create mode 100644 modules/git/tests/repos/repo6_merge/objects/05/45879290cc368a8becebc4aa34002c52d5fecc create mode 100644 modules/git/tests/repos/repo6_merge/objects/1e/5d0a65fe099ef12d24b28f783896e4b8172576 create mode 100644 modules/git/tests/repos/repo6_merge/objects/37/d35c7ed39e4e16d0b579a5b995b7e30b0e9411 create mode 100644 modules/git/tests/repos/repo6_merge/objects/38/ec3e0cdc88bde01014bda4a5dd9fc835f41439 create mode 100644 modules/git/tests/repos/repo6_merge/objects/66/7e0fbc6bc02c2285d17f542e89b23c0fa5482b create mode 100644 modules/git/tests/repos/repo6_merge/objects/9f/d90b1d524c0fea776ed5e6476da02ea1740597 create mode 100644 modules/git/tests/repos/repo6_merge/objects/ae/4b035e7c4afbc000576cee3f713ea0c2f1e1e2 create mode 100644 modules/git/tests/repos/repo6_merge/objects/ba/2906d0666cf726c7eaadd2cd3db615dedfdf3a create mode 100644 modules/git/tests/repos/repo6_merge/objects/c1/a95c2eff8151c6d1437a0d5d3322a73ff38fb8 create mode 100644 modules/git/tests/repos/repo6_merge/objects/cc/d1d4d594029e68c388ecef5aa3063fa1055831 create mode 100644 modules/git/tests/repos/repo6_merge/objects/cd/fc1aaf7a149151cb7bff639fafe05668d4bbd2 create mode 100644 modules/git/tests/repos/repo6_merge/objects/d1/792641396ff7630d35fbb0b74b86b0c71bca77 create mode 100644 modules/git/tests/repos/repo6_merge/objects/ec/d11d8da0f25eaa99f64a37a82da98685f381e2 create mode 100644 modules/git/tests/repos/repo6_merge/objects/fa/49b077972391ad58037050f2a75f74e3671e92 create mode 100644 modules/git/tests/repos/repo6_merge/refs/heads/main create mode 100644 modules/git/tests/repos/repo6_merge/refs/heads/merge/add_file create mode 100644 modules/git/tests/repos/repo6_merge/refs/heads/merge/modify_file create mode 100644 modules/git/tests/repos/repo6_merge/refs/heads/merge/remove_file diff --git a/modules/git/commit.go b/modules/git/commit.go index c44882d886..b09be25ba0 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -495,7 +495,7 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi }() stderr := new(bytes.Buffer) - err := NewCommand(ctx, "log", "--name-status", "-c", "--pretty=format:", "--parents", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(&RunOpts{ + err := NewCommand(ctx, "log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(&RunOpts{ Dir: repoPath, Stdout: w, Stderr: stderr, diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go index acf4beb029..ac586fdf09 100644 --- a/modules/git/commit_test.go +++ b/modules/git/commit_test.go @@ -255,3 +255,26 @@ func TestParseCommitFileStatus(t *testing.T) { assert.Equal(t, kase.modified, fileStatus.Modified) } } + +func TestGetCommitFileStatusMerges(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo6_merge") + + commitFileStatus, err := GetCommitFileStatus(DefaultContext, bareRepo1Path, "022f4ce6214973e018f02bf363bf8a2e3691f699") + assert.NoError(t, err) + + expected := CommitFileStatus{ + []string{ + "add_file.txt", + }, + []string{ + "to_remove.txt", + }, + []string{ + "to_modify.txt", + }, + } + + assert.Equal(t, commitFileStatus.Added, expected.Added) + assert.Equal(t, commitFileStatus.Removed, expected.Removed) + assert.Equal(t, commitFileStatus.Modified, expected.Modified) +} diff --git a/modules/git/tests/repos/repo6_merge/HEAD b/modules/git/tests/repos/repo6_merge/HEAD new file mode 100644 index 0000000000..b870d82622 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/modules/git/tests/repos/repo6_merge/objects/02/2f4ce6214973e018f02bf363bf8a2e3691f699 b/modules/git/tests/repos/repo6_merge/objects/02/2f4ce6214973e018f02bf363bf8a2e3691f699 new file mode 100644 index 0000000000000000000000000000000000000000..0778a1c88c74c272f3e597e6a2f5fabe07c52d43 GIT binary patch literal 280 zcmV+z0q6dB0gaGNP6IIvg;~!jvg?AMI*Ah}5<*;nGoaXU+7ZkQng%3pPlu`-R&3tp z`=0z{S*B_11~J|ARn^2u3XLVQfD`9ZsFqqlrzx}Z0<8p0j)#x3>fDW|>VuG0pB2@{ z3LGexz&Qsd^}wJRt-bn1k--Ng!E!zW0D+>Y`bO4k0ddW$Rd@A$Qt=8>B_*y37A$ft zSVFD2N%EngMW3(!d2MzPt#` e8{c(z_XlkKFTF5R=K2RuOC8(y1rI-BV~#m*q=*mz literal 0 HcmV?d00001 diff --git a/modules/git/tests/repos/repo6_merge/objects/05/45879290cc368a8becebc4aa34002c52d5fecc b/modules/git/tests/repos/repo6_merge/objects/05/45879290cc368a8becebc4aa34002c52d5fecc new file mode 100644 index 0000000000000000000000000000000000000000..c71794f3291ea726569ff279e1668d3ca58c694f GIT binary patch literal 152 zcmV;J0B8Sr0V^p=O;s>7wO}wbFfcPQQAkWliBHSSN!2T*HCtMa_7&)jnt=q)T^oels% GV?#3{$4L(W literal 0 HcmV?d00001 diff --git a/modules/git/tests/repos/repo6_merge/objects/1e/5d0a65fe099ef12d24b28f783896e4b8172576 b/modules/git/tests/repos/repo6_merge/objects/1e/5d0a65fe099ef12d24b28f783896e4b8172576 new file mode 100644 index 0000000000000000000000000000000000000000..365f368d35bc627422f9d91945304ddeaf5336ec GIT binary patch literal 88 zcmV-e0H^AXD~D{Ff%bx$W6@5(<`YcVc4a~b|Ee2yV~(rYcHL(-6neP u{(UQ`nv(qZ-29Zxv`VnTGZ(L1ox(IP<8a5D_fd=4>=&{|7y>!#1;p]xuyIwN4 \ No newline at end of file diff --git a/modules/git/tests/repos/repo6_merge/objects/38/ec3e0cdc88bde01014bda4a5dd9fc835f41439 b/modules/git/tests/repos/repo6_merge/objects/38/ec3e0cdc88bde01014bda4a5dd9fc835f41439 new file mode 100644 index 0000000000..582d98ce30 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge/objects/38/ec3e0cdc88bde01014bda4a5dd9fc835f41439 @@ -0,0 +1,2 @@ +xM +0a9M2 U =yv/ 0:@$UѬ.[> lIsx 8'TRĤS,$x. ֚=n[נus!+9BGvfm ur> \ No newline at end of file diff --git a/modules/git/tests/repos/repo6_merge/objects/66/7e0fbc6bc02c2285d17f542e89b23c0fa5482b b/modules/git/tests/repos/repo6_merge/objects/66/7e0fbc6bc02c2285d17f542e89b23c0fa5482b new file mode 100644 index 0000000000000000000000000000000000000000..d7faff6bf2494bda69c5357c728dc7fa54e786e4 GIT binary patch literal 122 zcmV-=0EPc}0V^p=O;s>7G-oh0FfcPQQAkWliBHSSN!2T& \ No newline at end of file diff --git a/modules/git/tests/repos/repo6_merge/objects/ba/2906d0666cf726c7eaadd2cd3db615dedfdf3a b/modules/git/tests/repos/repo6_merge/objects/ba/2906d0666cf726c7eaadd2cd3db615dedfdf3a new file mode 100644 index 0000000000000000000000000000000000000000..edef2a7f0cb64247cd41bab23f6df7e63d599083 GIT binary patch literal 20 ccmb7v|unaFfcPQQOHfq%+o8WC}G&8$#x+v=eyeRS8FewwcRFq z@BV!&sG5@e_}u)I%(P0d!Z#OXdl!6)TQ&2Wm-!0am2It`8y_JQ7NzFqm!*Og&cDem a8|1_Psyy%NGxyvDdJ9Wfrvm__RW!bw20MlT literal 0 HcmV?d00001 diff --git a/modules/git/tests/repos/repo6_merge/objects/cc/d1d4d594029e68c388ecef5aa3063fa1055831 b/modules/git/tests/repos/repo6_merge/objects/cc/d1d4d594029e68c388ecef5aa3063fa1055831 new file mode 100644 index 0000000000000000000000000000000000000000..5449d726cd885825f9bde49b951215c8800bffd5 GIT binary patch literal 26 icmb7v|unaFfcPQQOHfq%+o8WC}G&8$#x+v=eyeRS8FewwcRFq z@BV!&sG5@e_}u)I%(P0d!ZR1IT%E!+FXM2>oA*(R+3XjxMi?R#7NzFqm!*Og&cDem a8|1_Psyy%NGxyvDdJ9Wfrvm_M1T#m|qdTYo literal 0 HcmV?d00001 diff --git a/modules/git/tests/repos/repo6_merge/objects/d1/792641396ff7630d35fbb0b74b86b0c71bca77 b/modules/git/tests/repos/repo6_merge/objects/d1/792641396ff7630d35fbb0b74b86b0c71bca77 new file mode 100644 index 0000000000000000000000000000000000000000..867e4c0b866fd796704426d6a221b2d68959b3ba GIT binary patch literal 145 zcmV;C0B-+y0gcW}3IZ_}wRp zijp0XDQ~@PJE*)A&eQAUG`RqU&y7SSnguJX{0|=G&M&X literal 0 HcmV?d00001 diff --git a/modules/git/tests/repos/repo6_merge/objects/ec/d11d8da0f25eaa99f64a37a82da98685f381e2 b/modules/git/tests/repos/repo6_merge/objects/ec/d11d8da0f25eaa99f64a37a82da98685f381e2 new file mode 100644 index 0000000000000000000000000000000000000000..52d300cdf184df35f4e5437d784a790ca37c2013 GIT binary patch literal 33 pcmb Date: Thu, 24 Aug 2023 12:36:10 +0200 Subject: [PATCH 09/60] add Upload URL to release API (#26663) - Resolves https://codeberg.org/forgejo/forgejo/issues/580 - Return a `upload_field` to any release API response, which points to the API URL for uploading new assets. - Adds unit test. - Adds integration testing to verify URL is returned correctly and that upload endpoint actually works --------- Co-authored-by: Gusted --- models/repo/release.go | 5 ++++ modules/structs/release.go | 1 + services/convert/release.go | 1 + services/convert/release_test.go | 28 ++++++++++++++++++ templates/swagger/v1_json.tmpl | 4 +++ tests/integration/api_releases_test.go | 40 ++++++++++++++++++++++++++ 6 files changed, 79 insertions(+) create mode 100644 services/convert/release_test.go diff --git a/models/repo/release.go b/models/repo/release.go index a00585111e..191475d541 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -133,6 +133,11 @@ func (r *Release) HTMLURL() string { return r.Repo.HTMLURL() + "/releases/tag/" + util.PathEscapeSegments(r.TagName) } +// APIUploadURL the api url to upload assets to a release. release must have attributes loaded +func (r *Release) APIUploadURL() string { + return r.APIURL() + "/assets" +} + // Link the relative url for a release on the web UI. release must have attributes loaded func (r *Release) Link() string { return r.Repo.Link() + "/releases/tag/" + util.PathEscapeSegments(r.TagName) diff --git a/modules/structs/release.go b/modules/structs/release.go index 3fe40389b1..c7378645c2 100644 --- a/modules/structs/release.go +++ b/modules/structs/release.go @@ -18,6 +18,7 @@ type Release struct { HTMLURL string `json:"html_url"` TarURL string `json:"tarball_url"` ZipURL string `json:"zipball_url"` + UploadURL string `json:"upload_url"` IsDraft bool `json:"draft"` IsPrerelease bool `json:"prerelease"` // swagger:strfmt date-time diff --git a/services/convert/release.go b/services/convert/release.go index d8aa46d432..bfff53e62f 100644 --- a/services/convert/release.go +++ b/services/convert/release.go @@ -22,6 +22,7 @@ func ToAPIRelease(ctx context.Context, repo *repo_model.Repository, r *repo_mode HTMLURL: r.HTMLURL(), TarURL: r.TarURL(), ZipURL: r.ZipURL(), + UploadURL: r.APIUploadURL(), IsDraft: r.IsDraft, IsPrerelease: r.IsPrerelease, CreatedAt: r.CreatedUnix.AsTime(), diff --git a/services/convert/release_test.go b/services/convert/release_test.go new file mode 100644 index 0000000000..201b27e16d --- /dev/null +++ b/services/convert/release_test.go @@ -0,0 +1,28 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package convert + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestRelease_ToRelease(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + release1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{ID: 1}) + release1.LoadAttributes(db.DefaultContext) + + apiRelease := ToAPIRelease(db.DefaultContext, repo1, release1) + assert.NotNil(t, apiRelease) + assert.EqualValues(t, 1, apiRelease.ID) + assert.EqualValues(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1", apiRelease.URL) + assert.EqualValues(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1/assets", apiRelease.UploadURL) +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index aff4490899..ca4e1c4606 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -21090,6 +21090,10 @@ "type": "string", "x-go-name": "Target" }, + "upload_url": { + "type": "string", + "x-go-name": "UploadURL" + }, "url": { "type": "string", "x-go-name": "URL" diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go index 7f43939083..526842d5ac 100644 --- a/tests/integration/api_releases_test.go +++ b/tests/integration/api_releases_test.go @@ -4,9 +4,13 @@ package integration import ( + "bytes" "fmt" + "io" + "mime/multipart" "net/http" "net/url" + "strings" "testing" auth_model "code.gitea.io/gitea/models/auth" @@ -38,12 +42,15 @@ func TestAPIListReleases(t *testing.T) { case 1: assert.False(t, release.IsDraft) assert.False(t, release.IsPrerelease) + assert.True(t, strings.HasSuffix(release.UploadURL, "/api/v1/repos/user2/repo1/releases/1/assets"), release.UploadURL) case 4: assert.True(t, release.IsDraft) assert.False(t, release.IsPrerelease) + assert.True(t, strings.HasSuffix(release.UploadURL, "/api/v1/repos/user2/repo1/releases/4/assets"), release.UploadURL) case 5: assert.False(t, release.IsDraft) assert.True(t, release.IsPrerelease) + assert.True(t, strings.HasSuffix(release.UploadURL, "/api/v1/repos/user2/repo1/releases/5/assets"), release.UploadURL) default: assert.NoError(t, fmt.Errorf("unexpected release: %v", release)) } @@ -248,3 +255,36 @@ func TestAPIDeleteReleaseByTagName(t *testing.T) { req = NewRequestf(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/tags/release-tag?token=%s", owner.Name, repo.Name, token)) _ = MakeRequest(t, req, http.StatusNoContent) } + +func TestAPIUploadAssetRelease(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + session := loginUser(t, owner.LowerName) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + r := createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") + + filename := "image.png" + buff := generateImg() + body := &bytes.Buffer{} + + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("attachment", filename) + assert.NoError(t, err) + _, err = io.Copy(part, &buff) + assert.NoError(t, err) + err = writer.Close() + assert.NoError(t, err) + + req := NewRequestWithBody(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets?name=test-asset&token=%s", owner.Name, repo.Name, r.ID, token), body) + req.Header.Add("Content-Type", writer.FormDataContentType()) + resp := MakeRequest(t, req, http.StatusCreated) + + var attachment *api.Attachment + DecodeJSON(t, resp, &attachment) + + assert.EqualValues(t, "test-asset", attachment.Name) + assert.EqualValues(t, 104, attachment.Size) +} From 4de224469774a502f560cbf62060a03016c97167 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 24 Aug 2023 19:09:36 +0800 Subject: [PATCH 10/60] Make issue template field template access correct template data (#26698) Regression of #23092, the `{{$field := .}}` was missing during that refactoring. --- templates/repo/issue/fields/checkboxes.tmpl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/repo/issue/fields/checkboxes.tmpl b/templates/repo/issue/fields/checkboxes.tmpl index 80835b649a..035ad8e539 100644 --- a/templates/repo/issue/fields/checkboxes.tmpl +++ b/templates/repo/issue/fields/checkboxes.tmpl @@ -1,10 +1,9 @@
{{template "repo/issue/fields/header" .}} - {{$field := .}} {{range $i, $opt := .item.Attributes.options}}
- +
From 09faf43ef822ca4dbdfb2a2714ad43a782acf6e8 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 24 Aug 2023 20:13:23 +0800 Subject: [PATCH 11/60] Improve Image Diff UI (#26696) 1. Use `is-loading` instead of `ui loader` 2. Introduce class name `image-diff-tabs`, instead of searching `gt-hidden`, which is fragile 3. Align the UI elements, see the screenshots. --- templates/repo/diff/image_diff.tmpl | 7 ++----- web_src/css/features/imagediff.css | 13 +++++++++++-- web_src/js/features/imagediff.js | 7 +++---- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/templates/repo/diff/image_diff.tmpl b/templates/repo/diff/image_diff.tmpl index ec14ef9b1a..bd461d2800 100644 --- a/templates/repo/diff/image_diff.tmpl +++ b/templates/repo/diff/image_diff.tmpl @@ -11,7 +11,7 @@ {{end}}
-
+
{{if .blobBase}} @@ -63,10 +63,8 @@
+
-
- -
@@ -74,7 +72,6 @@
{{end}}
-
diff --git a/web_src/css/features/imagediff.css b/web_src/css/features/imagediff.css index a7bce3f471..ad3165e8d8 100644 --- a/web_src/css/features/imagediff.css +++ b/web_src/css/features/imagediff.css @@ -1,6 +1,14 @@ +.image-diff-tabs { + min-height: 60px; + +} +.image-diff-tabs.is-loading .tab { + display: none; +} + .image-diff-container { text-align: center; - padding: 1em 0; + padding: 0.5em 0 1em; } .image-diff-container img { @@ -31,6 +39,7 @@ .image-diff-container .diff-swipe { margin: auto; + padding: 1em 0; } .image-diff-container .diff-swipe .swipe-frame { @@ -89,7 +98,7 @@ } .image-diff-container .diff-overlay .overlay-frame { - margin: 0 auto; + margin: 1em auto 0; position: relative; } diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js index 2e7baab79f..23b048f295 100644 --- a/web_src/js/features/imagediff.js +++ b/web_src/js/features/imagediff.js @@ -130,8 +130,7 @@ export function initImageDiff() { initOverlay(createContext($imageAfter[2], $imageBefore[2])); } - $container.find('> .gt-hidden').removeClass('gt-hidden'); - hideElem($container.find('.ui.loader')); + $container.find('> .image-diff-tabs').removeClass('is-loading'); } function initSideBySide(sizes) { @@ -205,7 +204,7 @@ export function initImageDiff() { }); $container.find('.diff-swipe').css({ width: sizes.max.width * factor + 2, - height: sizes.max.height * factor + 4 + height: sizes.max.height * factor + 30 /* extra height for inner "position: absolute" elements */, }); $container.find('.swipe-bar').on('mousedown', function(e) { e.preventDefault(); @@ -261,7 +260,7 @@ export function initImageDiff() { // the "css(width, height)" is somewhat hacky and not easy to understand, it could be improved in the future sizes.image2.parent().parent().css({ width: sizes.max.width * factor + 2, - height: sizes.max.height * factor + 2 + 20 /* extra height for inner "position: absolute" elements */, + height: sizes.max.height * factor + 2, }); const $range = $container.find("input[type='range']"); From 8ac83043f5bd53cf6094fe026f27004dcd3b67c5 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 24 Aug 2023 22:21:41 +0800 Subject: [PATCH 12/60] Use "small-loading-icon" insead of "btn-octicon is-loading" (#26710) The "btn-octicon is-loading" was introduced by #21842 , it is only used by the "Copy Content" button, but the "btn-octicon" selector would affect too many uncertain elements. Now there is a general "small-loading-icon" class, so the "btn-octicon is-loading" could be removed. --- web_src/css/modules/animations.css | 7 ------- web_src/js/features/copycontent.js | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css index 2f69117928..ea1409687a 100644 --- a/web_src/css/modules/animations.css +++ b/web_src/css/modules/animations.css @@ -57,13 +57,6 @@ form.single-button-form.is-loading .button { background: transparent; } -/* TODO: not needed, use "is-loading small-loading-icon" instead */ -.btn-octicon.is-loading::after { - border-width: 2px; - height: 1.25rem; - width: 1.25rem; -} - /* TODO: not needed, use "is-loading small-loading-icon" instead */ code.language-math.is-loading::after { padding: 0; diff --git a/web_src/js/features/copycontent.js b/web_src/js/features/copycontent.js index 621d6dab73..c1419a524b 100644 --- a/web_src/js/features/copycontent.js +++ b/web_src/js/features/copycontent.js @@ -18,7 +18,7 @@ export function initCopyContent() { // the text to copy is not in the DOM or it is an image which should be // fetched to copy in full resolution if (link) { - btn.classList.add('is-loading'); + btn.classList.add('is-loading', 'small-loading-icon'); try { const res = await fetch(link, {credentials: 'include', redirect: 'follow'}); const contentType = res.headers.get('content-type'); @@ -32,7 +32,7 @@ export function initCopyContent() { } catch { return showTemporaryTooltip(btn, i18n.copy_error); } finally { - btn.classList.remove('is-loading'); + btn.classList.remove('is-loading', 'small-loading-icon'); } } else { // text, read from DOM const lineEls = document.querySelectorAll('.file-view .lines-code'); From 390ec619f3fe04ad6c91c52813bc17d9963d5322 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 24 Aug 2023 23:46:30 +0800 Subject: [PATCH 13/60] Fix review bar misalignment (#26711) --- templates/repo/diff/box.tmpl | 2 +- web_src/css/repo.css | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index 7b936cb817..0b5bf86371 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -20,7 +20,7 @@
{{end}}
-
+
{{if and .PageIsPullFiles $.SignedUserID (not .IsArchived) (not .DiffNotAvailable)}}
-
+
{{else if eq .CaptchaType "mcaptcha"}} -
- {{.locale.Tr "captcha"}} -
-
+
+ +
+
{{else if eq .CaptchaType "cfturnstile"}} -
+
{{end}}{{end}} diff --git a/web_src/css/form.css b/web_src/css/form.css index 9527ef9d46..1bd1920955 100644 --- a/web_src/css/form.css +++ b/web_src/css/form.css @@ -179,8 +179,7 @@ textarea:focus, #create-page-form form .header { padding-left: 280px !important; } - #create-page-form form .inline.field > label, - #create-page-form form .inline.field.captcha-field > span { + #create-page-form form .inline.field > label { text-align: right; width: 250px !important; word-wrap: break-word; @@ -206,6 +205,13 @@ textarea:focus, } } +.m-captcha-style { + width: 100%; + height: 5em; + vertical-align: middle; + display: inline-block; +} + @media (min-width: 768px) { .g-recaptcha-style, .h-captcha-style { @@ -219,6 +225,9 @@ textarea:focus, width: 302px !important; height: 76px !important; } + .m-captcha-style { + width: 50%; + } } @media (max-height: 575px) { @@ -271,13 +280,7 @@ textarea:focus, .user.reset.password form .inline.field > label, .user.link-account form .inline.field > label, .user.signin form .inline.field > label, - .user.signup form .inline.field > label, - .user.activate form .inline.field.captcha-field > span, - .user.forgot.password form .inline.field.captcha-field > span, - .user.reset.password form .inline.field.captcha-field > span, - .user.link-account form .inline.field.captcha-field > span, - .user.signin form .inline.field.captcha-field > span, - .user.signup form .inline.field.captcha-field > span { + .user.signup form .inline.field > label { text-align: right; width: 250px !important; word-wrap: break-word; @@ -421,10 +424,7 @@ textarea:focus, } .repository.new.repo form .inline.field > label, .repository.new.migrate form .inline.field > label, - .repository.new.fork form .inline.field > label, - .repository.new.repo form .inline.field.captcha-field > span, - .repository.new.migrate form .inline.field.captcha-field > span, - .repository.new.fork form .inline.field.captcha-field > span { + .repository.new.fork form .inline.field > label { text-align: right; width: 250px !important; word-wrap: break-word; diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css index 35b5d3c97e..17a89ebfb7 100644 --- a/web_src/css/helpers.css +++ b/web_src/css/helpers.css @@ -273,12 +273,6 @@ Gitea's private styles use `g-` prefix. .gt-font-17 { font-size: 17px !important } .gt-font-18 { font-size: 18px !important } -@media (max-width: 767.98px) { - .gt-db-small { display: block !important; } - .gt-w-100-small { width: 100% !important; } - .gt-js-small { justify-content: flex-start !important; } -} - /* gt-hidden must be placed after all other "display: xxx !important" classes to win the chance do not use: diff --git a/web_src/css/org.css b/web_src/css/org.css index 4bb3b0cd57..061d30bef2 100644 --- a/web_src/css/org.css +++ b/web_src/css/org.css @@ -13,8 +13,7 @@ #create-page-form form .header { padding-left: 280px !important; } - #create-page-form form .inline.field > label, - #create-page-form form .inline.field.captcha-field > span { + #create-page-form form .inline.field > label { text-align: right; width: 250px !important; word-wrap: break-word; @@ -59,8 +58,7 @@ .organization.new.org form .header { padding-left: 280px !important; } - .organization.new.org form .inline.field > label, - .organization.new.org form .inline.field.captcha-field > span { + .organization.new.org form .inline.field > label { text-align: right; width: 250px !important; word-wrap: break-word; From 412e5c0946fc5b83456685bc2fb1aed682228f57 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 25 Aug 2023 19:07:42 +0800 Subject: [PATCH 16/60] Make web context initialize correctly for different cases (#26726) The web context (modules/context.Context) is quite complex, it's difficult for the callers to initialize correctly. This PR introduces a `NewWebContext` function, to make sure the web context have the same behavior for different cases. --- modules/context/context.go | 41 +++++++++++++++---------- modules/context/package.go | 6 ++-- modules/test/context_tests.go | 16 +++++----- routers/install/install.go | 10 +----- routers/web/repo/actions/actions.go | 2 +- routers/web/repo/helper.go | 7 ++--- routers/web/repo/helper_test.go | 7 ++--- routers/web/repo/issue.go | 6 ++-- routers/web/repo/pull.go | 2 +- routers/web/repo/release.go | 4 +-- services/markup/processorhelper_test.go | 3 +- 11 files changed, 50 insertions(+), 54 deletions(-) diff --git a/modules/context/context.go b/modules/context/context.go index 98c1a9bdb6..47ad310b09 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -107,6 +107,29 @@ func GetValidateContext(req *http.Request) (ctx *ValidateContext) { return ctx } +func NewTemplateContextForWeb(ctx *Context) TemplateContext { + tmplCtx := NewTemplateContext(ctx) + tmplCtx["Locale"] = ctx.Base.Locale + tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx) + return tmplCtx +} + +func NewWebContext(base *Base, render Render, session session.Store) *Context { + ctx := &Context{ + Base: base, + Render: render, + Session: session, + + Cache: mc.GetCache(), + Link: setting.AppSubURL + strings.TrimSuffix(base.Req.URL.EscapedPath(), "/"), + Repo: &Repository{PullRequest: &PullRequest{}}, + Org: &Organization{}, + } + ctx.TemplateContext = NewTemplateContextForWeb(ctx) + ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}} + return ctx +} + // Contexter initializes a classic context for a request. func Contexter() func(next http.Handler) http.Handler { rnd := templates.HTMLRenderer() @@ -127,21 +150,8 @@ func Contexter() func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { base, baseCleanUp := NewBaseContext(resp, req) - ctx := &Context{ - Base: base, - Cache: mc.GetCache(), - Link: setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/"), - Render: rnd, - Session: session.GetSession(req), - Repo: &Repository{PullRequest: &PullRequest{}}, - Org: &Organization{}, - } defer baseCleanUp() - - // TODO: "install.go" also shares the same logic, which should be refactored to a general function - ctx.TemplateContext = NewTemplateContext(ctx) - ctx.TemplateContext["Locale"] = ctx.Locale - ctx.TemplateContext["AvatarUtils"] = templates.NewAvatarUtils(ctx) + ctx := NewWebContext(base, rnd, session.GetSession(req)) ctx.Data.MergeFrom(middleware.CommonTemplateContextData()) ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this @@ -172,8 +182,7 @@ func Contexter() func(next http.Handler) http.Handler { } } - // prepare an empty Flash message for current request - ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}} + // if there are new messages in the ctx.Flash, write them into cookie ctx.Resp.Before(func(resp ResponseWriter) { if val := ctx.Flash.Encode(); val != "" { middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0) diff --git a/modules/context/package.go b/modules/context/package.go index be50e0a991..c0813fb2da 100644 --- a/modules/context/package.go +++ b/modules/context/package.go @@ -154,12 +154,10 @@ func PackageContexter() func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { base, baseCleanUp := NewBaseContext(resp, req) - ctx := &Context{ - Base: base, - Render: renderer, // it is still needed when rendering 500 page in a package handler - } defer baseCleanUp() + // it is still needed when rendering 500 page in a package handler + ctx := NewWebContext(base, renderer, nil) ctx.Base.AppendContextValue(WebContextKey, ctx) next.ServeHTTP(ctx.Resp, ctx.Req) }) diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go index 92d7f8b22b..83e6117bcf 100644 --- a/modules/test/context_tests.go +++ b/modules/test/context_tests.go @@ -45,14 +45,12 @@ func MockContext(t *testing.T, reqPath string) (*context.Context, *httptest.Resp resp := httptest.NewRecorder() req := mockRequest(t, reqPath) base, baseCleanUp := context.NewBaseContext(resp, req) + _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later base.Data = middleware.GetContextData(req.Context()) base.Locale = &translation.MockLocale{} - ctx := &context.Context{ - Base: base, - Render: &mockRender{}, - Flash: &middleware.Flash{Values: url.Values{}}, - } - _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later + + ctx := context.NewWebContext(base, &MockRender{}, nil) + ctx.Flash = &middleware.Flash{Values: url.Values{}} chiCtx := chi.NewRouteContext() ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx) @@ -148,13 +146,13 @@ func LoadGitRepo(t *testing.T, ctx *context.Context) { assert.NoError(t, err) } -type mockRender struct{} +type MockRender struct{} -func (tr *mockRender) TemplateLookup(tmpl string, _ gocontext.Context) (templates.TemplateExecutor, error) { +func (tr *MockRender) TemplateLookup(tmpl string, _ gocontext.Context) (templates.TemplateExecutor, error) { return nil, nil } -func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ any, _ gocontext.Context) error { +func (tr *MockRender) HTML(w io.Writer, status int, _ string, _ any, _ gocontext.Context) error { if resp, ok := w.(http.ResponseWriter); ok { resp.WriteHeader(status) } diff --git a/routers/install/install.go b/routers/install/install.go index 99ea7b0738..66a9e1f002 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -60,17 +60,9 @@ func Contexter() func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { base, baseCleanUp := context.NewBaseContext(resp, req) - ctx := &context.Context{ - Base: base, - Flash: &middleware.Flash{}, - Render: rnd, - Session: session.GetSession(req), - } defer baseCleanUp() - ctx.TemplateContext = context.NewTemplateContext(ctx) - ctx.TemplateContext["Locale"] = ctx.Locale - + ctx := context.NewWebContext(base, rnd, session.GetSession(req)) ctx.AppendContextValue(context.WebContextKey, ctx) ctx.Data.MergeFrom(middleware.CommonTemplateContextData()) ctx.Data.MergeFrom(middleware.ContextData{ diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index b0f4b6f897..6284d21463 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -191,7 +191,7 @@ func List(ctx *context.Context) { ctx.Error(http.StatusInternalServerError, err.Error()) return } - ctx.Data["Actors"] = repo.MakeSelfOnTop(ctx, actors) + ctx.Data["Actors"] = repo.MakeSelfOnTop(ctx.Doer, actors) ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx) diff --git a/routers/web/repo/helper.go b/routers/web/repo/helper.go index fb5ada1bdb..f8cdefdc8e 100644 --- a/routers/web/repo/helper.go +++ b/routers/web/repo/helper.go @@ -7,16 +7,15 @@ import ( "sort" "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" ) -func MakeSelfOnTop(ctx *context.Context, users []*user.User) []*user.User { - if ctx.Doer != nil { +func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User { + if doer != nil { sort.Slice(users, func(i, j int) bool { if users[i].ID == users[j].ID { return false } - return users[i].ID == ctx.Doer.ID // if users[i] is self, put it before others, so less=true + return users[i].ID == doer.ID // if users[i] is self, put it before others, so less=true }) } return users diff --git a/routers/web/repo/helper_test.go b/routers/web/repo/helper_test.go index 226e2e81f4..978758e77f 100644 --- a/routers/web/repo/helper_test.go +++ b/routers/web/repo/helper_test.go @@ -7,21 +7,20 @@ import ( "testing" "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "github.com/stretchr/testify/assert" ) func TestMakeSelfOnTop(t *testing.T) { - users := MakeSelfOnTop(&context.Context{}, []*user.User{{ID: 2}, {ID: 1}}) + users := MakeSelfOnTop(nil, []*user.User{{ID: 2}, {ID: 1}}) assert.Len(t, users, 2) assert.EqualValues(t, 2, users[0].ID) - users = MakeSelfOnTop(&context.Context{Doer: &user.User{ID: 1}}, []*user.User{{ID: 2}, {ID: 1}}) + users = MakeSelfOnTop(&user.User{ID: 1}, []*user.User{{ID: 2}, {ID: 1}}) assert.Len(t, users, 2) assert.EqualValues(t, 1, users[0].ID) - users = MakeSelfOnTop(&context.Context{Doer: &user.User{ID: 2}}, []*user.User{{ID: 2}, {ID: 1}}) + users = MakeSelfOnTop(&user.User{ID: 2}, []*user.User{{ID: 2}, {ID: 1}}) assert.Len(t, users, 2) assert.EqualValues(t, 2, users[0].ID) } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 9a2add1452..b8b5a2dff2 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -331,7 +331,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti ctx.ServerError("GetRepoAssignees", err) return } - ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) + ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers) handleTeamMentions(ctx) if ctx.Written() { @@ -535,7 +535,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R ctx.ServerError("GetRepoAssignees", err) return } - ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) + ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers) handleTeamMentions(ctx) } @@ -3625,7 +3625,7 @@ func issuePosters(ctx *context.Context, isPullList bool) { } } - posters = MakeSelfOnTop(ctx, posters) + posters = MakeSelfOnTop(ctx.Doer, posters) resp := &userSearchResponse{} resp.Results = make([]*userSearchInfo, len(posters)) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index e3854779fe..e697a0d5b6 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -956,7 +956,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi ctx.ServerError("GetRepoAssignees", err) return } - ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) + ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers) handleTeamMentions(ctx) if ctx.Written() { diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 957cf56972..138df45857 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -349,7 +349,7 @@ func NewRelease(ctx *context.Context) { ctx.ServerError("GetRepoAssignees", err) return } - ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) + ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers) upload.AddUploadContext(ctx, "release") @@ -538,7 +538,7 @@ func EditRelease(ctx *context.Context) { ctx.ServerError("GetRepoAssignees", err) return } - ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) + ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers) ctx.HTML(http.StatusOK, tplReleaseNew) } diff --git a/services/markup/processorhelper_test.go b/services/markup/processorhelper_test.go index 2f48e03b22..d83e10903f 100644 --- a/services/markup/processorhelper_test.go +++ b/services/markup/processorhelper_test.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/user" gitea_context "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" ) @@ -41,7 +42,7 @@ func TestProcessorHelper(t *testing.T) { assert.NoError(t, err) base, baseCleanUp := gitea_context.NewBaseContext(httptest.NewRecorder(), req) defer baseCleanUp() - giteaCtx := &gitea_context.Context{Base: base} + giteaCtx := gitea_context.NewWebContext(base, &test.MockRender{}, nil) assert.True(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPublic)) assert.False(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPrivate)) From 7b05d66e600f465d283a63f125e6202eb8faac30 Mon Sep 17 00:00:00 2001 From: Viktor Suprun Date: Fri, 25 Aug 2023 22:15:21 +1100 Subject: [PATCH 17/60] Fixed text overflow in dropdown menu (#26694) Fixes #26622 ![image](https://github.com/go-gitea/gitea/assets/683358/168b7e4d-97ba-4b5f-a5f5-33ee67e8b4be) Co-authored-by: Giteabot --- web_src/css/repo.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 414bd1d201..7a24f46702 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -162,6 +162,11 @@ left: 0; } +.repository .filter.menu .ui.dropdown .menu .item { + overflow: hidden; + text-overflow: ellipsis; +} + .repository .select-label .desc { padding-left: 23px; } From 21b8ec29aaa141981e7ca49855e7f25822efec05 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 25 Aug 2023 15:47:27 +0200 Subject: [PATCH 18/60] Add `eslint-plugin-vue-scoped-css` (#26720) Adds [eslint-plugin-vue-scoped-css](https://github.com/future-architect/eslint-plugin-vue-scoped-css) and fixes discovered issues which are: - 1 unused selector - 3 selectors with `.full.height` parent in a `
{{template "base/footer" .}} diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css index 7b3ec360f6..cac824d87d 100644 --- a/web_src/css/modules/animations.css +++ b/web_src/css/modules/animations.css @@ -99,8 +99,9 @@ code.language-math.is-loading::after { animation: pulse 2s linear; } -.ui.modal { +.ui.modal, +.ui.dimmer.transition { animation-name: fadein; - animation-duration: 300ms; + animation-duration: 100ms; animation-timing-function: ease-in-out; } diff --git a/web_src/css/modules/modal.css b/web_src/css/modules/modal.css index 5824efb30c..31b0b5d6b9 100644 --- a/web_src/css/modules/modal.css +++ b/web_src/css/modules/modal.css @@ -30,29 +30,41 @@ box-shadow: 1px 3px 3px 0 var(--color-shadow), 1px 3px 15px 2px var(--color-shadow); } -/* Gitea sometimes use a form in a modal dialog, then the "positive" button could submit the form directly */ +/* Gitea sometimes use a form in a modal dialog, then the "positive" button could submit the form directly +Fomantic UI only supports the layout:
+However, Gitea uses the following layouts: +*
+*
+*
+* ... +These inconsistent layouts should be refactored to simple ones. +*/ .ui.modal > .content, -.ui.modal > form > .content { +.ui.modal form > .content { padding: 1.5em; background: var(--color-body); } .ui.modal > .actions, -.ui.modal > form > .actions { +.ui.modal .content + .actions { background: var(--color-secondary-bg); border-color: var(--color-secondary); - - /* these styles are from Fomantic UI */ padding: 1rem; text-align: right; } +.ui.modal .content > .actions { + padding-top: 1em; /* if the "actions" is in the "content", some paddings are already added by the "content" */ + text-align: right; +} + /* positive/negative action buttons */ .ui.modal .actions > .ui.button { display: inline-flex; align-items: center; padding: 10px 12px 10px 10px; + margin-right: 0; } .ui.modal .actions > .ui.button.danger { From aeeeac8edfe857e24a92a74735c0484643861822 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Tue, 29 Aug 2023 00:22:17 +0000 Subject: [PATCH 39/60] [skip ci] Updated translations via Crowdin --- options/locale/locale_tr-TR.ini | 172 ++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 5a5838546c..d0250b5b34 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -4,6 +4,7 @@ explore=Keşfet help=Yardım logo=Logo sign_in=Giriş Yap +sign_in_with_provider=%s ile oturum aç sign_in_or=veya sign_out=Çıkış Yap sign_up=Kaydol @@ -79,6 +80,7 @@ milestones=Kilometre Taşları ok=Tamam cancel=İptal +retry=Yeniden Dene rerun=Yeniden çalıştır rerun_all=Tüm görevleri yeniden çalıştır save=Kaydet @@ -91,6 +93,7 @@ edit=Düzenle enabled=Aktifleştirilmiş disabled=Devre Dışı +locked=Kilitli copy=Kopyala copy_url=URL'yi kopyala @@ -128,7 +131,9 @@ concept_user_organization=Organizasyon show_timestamps=Zaman damgalarını göster show_log_seconds=Saniyeleri göster show_full_screen=Tam ekran göster +download_logs=Günlükleri indir +confirm_delete_selected=Tüm seçili öğeleri gerçekten silmek istiyor musunuz? name=İsim value=Değer @@ -167,6 +172,7 @@ string.desc=Z - A [error] occurred=Bir hata oluştu +report_message=Bunun bir Gitea hatası olduğunu düşünüyorsanız, lütfen GitHub sayfasında sorunu arayın veya gerekiyorsa yeni bir sorun oluşturun. missing_csrf=Hatalı İstek: CSRF anahtarı yok invalid_csrf=Hatalı İstek: geçersiz CSRF erişim anahtarı not_found=Hedef bulunamadı. @@ -220,6 +226,7 @@ repo_path_helper=Tüm uzak Git depoları bu dizine kaydedilecektir. lfs_path=Git LFS Kök Yolu lfs_path_helper=Git LFS tarafından izlenen dosyalar bu dizinde saklanacaktır. LFS'yi devre dışı bırakmak için boş bırakın. run_user=Şu Kullanıcı Olarak Çalıştır +run_user_helper=Gitea'nin çalışacağı işletim sistemi kullanıcı adı. Bu kullanıcının depo kök yoluna erişiminin olması gerektiğini unutmayın. domain=Sunucu Alan Adı domain_helper=Sunucu için alan adı veya ana bilgisayar adresi. ssh_port=SSH Sunucu Portu @@ -291,6 +298,8 @@ invalid_password_algorithm=Hatalı parola hash algoritması password_algorithm_helper=Parola hash algoritmasını ayarlayın. Algoritmalar değişen gereksinimlere ve güce sahiptirler. argon2 algoritması iyi özelliklere sahip olmasına rağmen fazla miktarda bellek kullanır ve küçük sistemler için uygun olmayabilir. enable_update_checker=Güncelleme Denetleyicisini Etkinleştir enable_update_checker_helper=Düzenli olarak gitea.io'ya bağlanarak yeni yayınlanan sürümleri denetler. +env_config_keys=Ortam Yapılandırma +env_config_keys_prompt=Aşağıdaki ortam değişkenleri de yapılandırma dosyanıza eklenecektir: [home] uname_holder=Kullanıcı Adı veya E-Posta Adresi @@ -353,6 +362,7 @@ remember_me=Bu Aygıtı hatırla forgot_password_title=Şifremi unuttum forgot_password=Şifrenizi mi unuttunuz? sign_up_now=Bir hesaba mı ihtiyacınız var? Hemen kaydolun. +sign_up_successful=Hesap başarılı bir şekilde oluşturuldu. Hoşgeldiniz! confirmation_mail_sent_prompt=Yeni onay e-postası %s adresine gönderildi. Lütfen gelen kutunuzu bir sonraki %s e kadar kontrol edip kayıt işlemini tamamlayın. must_change_password=Parolanızı güncelleyin allow_password_change=Kullanıcıyı parola değiştirmeye zorla (önerilen) @@ -360,6 +370,7 @@ reset_password_mail_sent_prompt=%s adresine bir onay e-postası gönderil active_your_account=Hesabınızı Etkinleştirin account_activated=Hesap etkinleştirildi prohibit_login=Oturum Açma Yasağı +prohibit_login_desc=Hesabınız ile oturum açmanız yasaklanmış, lütfen site yöneticinizle iletişime geçin. resent_limit_prompt=Zaten bir etkinleştirme e-postası talep ettiniz. Lütfen 3 dakika bekleyip tekrar deneyin. has_unconfirmed_mail=Merhaba %s, doğrulanmamış bir e-posta adresin var (%s). Bir doğrulama e-postası almadıysanız ya da yenisine ihtiyacınız varsa lütfen aşağıdaki düğmeye tıklayın. resend_mail=Etkinleştirme e-postasını tekrar almak için buraya tıklayın @@ -369,6 +380,7 @@ reset_password=Hesap Kurtarma invalid_code=Doğrulama kodunuz geçersiz veya süresi dolmuş. invalid_password=Parolanız hesap oluşturulurken kullanılan parolayla eşleşmiyor. reset_password_helper=Hesabı Kurtar +reset_password_wrong_user=%s olarak oturum açmışsınız, ancak hesap kurtarma bağlantısı %s için password_too_short=Parolanız en az %d karakter uzunluğunda olmalıdır. non_local_account=Yerel olmayan kullanıcılar parolalarını Gitea web arayüzünden güncelleyemezler. verify=Doğrula @@ -393,6 +405,7 @@ openid_connect_title=Mevcut olan bir hesaba bağlan openid_connect_desc=Seçilen OpenID URI'si bilinmiyor. Burada yeni bir hesapla ilişkilendir. openid_register_title=Yeni hesap oluştur openid_register_desc=Seçilen OpenID URI'si bilinmiyor. Burada yeni bir hesapla ilişkilendir. +openid_signin_desc=OpenID URI'nizi girin. Örneğin: alice.openid.example.org veya https://openid.example.org/alice. disable_forgot_password_mail=E posta ayarlanmadığından hesap kurtarma devre dışı. Site yöneticinizle iletişime geçin. disable_forgot_password_mail_admin=Hesap kurtarma sadece e posta ayarlıyken kullanılabilir. Hesap kurtarmayı etkinleştirmek için lütfen e posta ayarlayın. email_domain_blacklisted=Bu e-posta adresinizle kayıt olamazsınız. @@ -402,7 +415,9 @@ authorize_application_created_by=Bu uygulama %s tarafından oluşturuldu. authorize_application_description=Erişime izin verirseniz, özel depolar ve organizasyonlar da dahil olmak üzere tüm hesap bilgilerinize erişebilir ve yazabilir. authorize_title=Hesabınıza erişmesi için "%s" yetkilendirilsin mi? authorization_failed=Yetkilendirme başarısız oldu +authorization_failed_desc=Geçersiz bir istek tespit ettiğimiz için yetkilendirme başarısız oldu. Lütfen izin vermeye çalıştığınız uygulamanın sağlayıcısı ile iletişim kurun. sspi_auth_failed=SSPI kimlik doğrulaması başarısız oldu +password_pwned=Seçtiğiniz parola, daha önce herkese açık veri ihlallerinde açığa çıkan bir çalınan parola listesindedir. Lütfen farklı bir parola ile tekrar deneyin ve başka yerlerde de bu parolayı değiştirmeyi düşünün. password_pwned_err=HaveIBeenPwned'e yapılan istek tamamlanamadı [mail] @@ -417,6 +432,7 @@ activate_account.text_1=Merhaba %[1]s, %[2]s kaydınızı yaptırdığın activate_account.text_2=Hesabınızı etkinleştirmek için lütfen %s içinde linke tıklayın: activate_email=E-posta adresinizi doğrulayın +activate_email.title=%s, lütfen e-posta adresinizi doğrulayın activate_email.text=E posta adresinizi doğrulamak için lütfen %s içinde linke tıklayın: register_notify=Gitea'ya Hoş Geldiniz @@ -587,6 +603,8 @@ user_bio=Biyografi disabled_public_activity=Bu kullanıcı, etkinliğin herkese görünür olmasını devre dışı bıraktı. email_visibility.limited=E-posta adresiniz giriş yapmış tüm kullanıcılar tarafından görünür email_visibility.private=E-posta adresiniz sadece siz veya yöneticiler tarafından görünür +show_on_map=Bu yeri harita üzerinde göster +settings=Kullanıcı Ayarları form.name_reserved=`"%s" kullanıcı adı rezerve edilmiş.` form.name_pattern_not_allowed=Kullanıcı adında "%s" deseni kullanılamaz. @@ -608,9 +626,13 @@ delete=Hesabı Sil twofa=İki Aşamalı Doğrulama account_link=Bağlı Hesaplar organization=Organizasyonlar +uid=UID webauthn=Güvenlik Anahtarları public_profile=Herkese Açık Profil +biography_placeholder=Bize kendiniz hakkında birşeyler söyleyin! (Markdown kullanabilirsiniz) +location_placeholder=Yaklaşık konumunuzu başkalarıyla paylaşın +profile_desc=Profilinizin başkalarına nasıl gösterildiğini yönetin. Ana e-posta adresiniz bildirimler, parola kurtarma ve web tabanlı Git işlemleri için kullanılacaktır. password_username_disabled=Yerel olmayan kullanıcılara kullanıcı adlarını değiştirme izni verilmemiştir. Daha fazla bilgi edinmek için lütfen site yöneticisi ile iletişime geçiniz. full_name=Ad Soyad website=Web Sitesi @@ -622,6 +644,8 @@ update_language_not_found=`"%s" dili mevcut değil.` update_language_success=Dil güncellendi. update_profile_success=Profil resminiz güncellendi. change_username=Kullanıcı adınız değiştirildi. +change_username_prompt=Not: Kullanıcı adınızı değiştirirseniz, hesap URL'niz de değişecektir. +change_username_redirect_prompt=Eski kullanıcı adı, başkası tarafından kullanılana kadar yönlendirilecektir. continue=Devam Et cancel=İptal language=Dil @@ -646,6 +670,7 @@ comment_type_group_project=Proje comment_type_group_issue_ref=Konu referansı saved_successfully=Ayarlarınız başarılı bir şekilde kaydedildi. privacy=Gizlilik +keep_activity_private=Etkinliği profil sayfasında gizle keep_activity_private_popup=Etkinliği yalnızca siz ve yöneticiler için görünür hale getirir lookup_avatar_by_mail=Avatarı E-posta Adresine Göre Ara @@ -662,6 +687,7 @@ update_user_avatar_success=Kullanıcının avatarı güncellendi. change_password=Parolayı Güncelle old_password=Mevcut Parola new_password=Yeni Parola +retype_new_password=Yeni Parolayı Onayla password_incorrect=Mevcut parola hatalı. change_password_success=Parolanız güncelleştirildi. Şu andan itibaren yeni parolanızı kullanarak oturum açın. password_change_disabled=Yerel olmayan kullanıcılar parolalarını Gitea web arayüzünden güncelleyemezler. @@ -670,6 +696,7 @@ emails=E-Posta Adresleri manage_emails=E-posta Adreslerini Yönet manage_themes=Varsayılan temayı seç manage_openid=OpenID Adreslerini Yönet +email_desc=Ana e-posta adresiniz bildirimler, parola kurtarma ve gizlenmemişse eğer web tabanlı Git işlemleri için kullanılacaktır. theme_desc=Bu, sitedeki varsayılan temanız olacak. primary=Birincil activated=Aktifleştirildi @@ -677,6 +704,7 @@ requires_activation=Etkinleştirme gerekiyor primary_email=Birincil Yap activate_email=Etkinleştirme Gönder activations_pending=Bekleyen Etkinleştirmeler +can_not_add_email_activations_pending=Bekleyen etkinleştirme var, yeni bir e-posta adresi eklemek istiyorsanız birkaç dakika içinde tekrar deneyin. delete_email=Kaldır email_deletion=E-posta Adresini Kaldır email_deletion_desc=E-posta adresi ve ilgili bilgiler hesabınızdan kaldırılacak. Bu e-posta adresi tarafından yapılan işlemeler değişmeden kalacaktır. Devam edilsin mi? @@ -695,6 +723,7 @@ add_email_success=Yeni e-posta adresi eklendi. email_preference_set_success=E-posta tercihi başarıyla ayarlandı. add_openid_success=Yeni OpenID adresi eklendi. keep_email_private=E-posta Adresini Gizle +keep_email_private_popup=Bu, e-posta adresinizi profilde, değişiklik isteği yaptığınızda veya web arayüzünde dosya düzenlediğinizde gizleyecektir. İtilen işlemeler değişmeyecektir. openid_desc=OpenID, kimlik doğrulama işlemini harici bir sağlayıcıya devretmenize olanak sağlar. manage_ssh_keys=SSH Anahtarlarını Yönet @@ -773,7 +802,9 @@ ssh_disabled=SSH devre dışı bırakıldı ssh_signonly=SSH şu an devre dışı, dolayısıyla bu anahtarlar sadece işlem imza doğrulama için kullanılıyor. ssh_externally_managed=Bu SSH anahtarı, bu kullanıcı için harici olarak yönetiliyor manage_social=Bağlanmış Sosyal Hesapları Yönet +social_desc=Bu sosyal hesaplar oturum açmanız için kullanılabilir. Hepsini tanıdığınızdan emin olun. unbind=Bağlantıyı Kaldır +unbind_success=Sosyal hesap başarılı bir şekilde kaldırıldı. manage_access_token=Erişim Jetonlarını Yönet generate_new_token=Yeni Erişim Anahtarı Üret @@ -794,6 +825,8 @@ permissions_access_all=Tümü (herkese açık, özel ve sınırlı) select_permissions=İzinleri seçin permission_no_access=Erişim Yok permission_read=Okunmuş +permission_write=Okuma ve Yazma +access_token_desc=Seçili token izinleri, yetkilendirmeyi ilgili API yollarıyla sınırlandıracaktır. Daha fazla bilgi için belgeleri okuyun. at_least_one_permission=Bir token oluşturmak için en azından bir izin seçmelisiniz permissions_list=İzinler: @@ -805,6 +838,8 @@ remove_oauth2_application_desc=Bir OAuth2 uygulamasının kaldırılması, imzal remove_oauth2_application_success=Uygulama silindi. create_oauth2_application=Yeni bir OAuth2 Uygulaması Oluştur create_oauth2_application_button=Uygulama Oluştur +create_oauth2_application_success=Yeni bir OAuth2 uygulamasını başarıyla oluşturdunuz. +update_oauth2_application_success=OAuth2 uygulamasını başarıyla güncellediniz. oauth2_application_name=Uygulama Adı oauth2_confidential_client=Güvenli İstemci. Web uygulamaları gibi sırları güvende tutan uygulamalar için bunu seçin. Masaüstü ve mobil uygulamaları da içeren doğal uygulamalar için seçmeyin. oauth2_redirect_uris=Yönlendirme URI'leri. Lütfen her bir URI'yi yeni bir satıra yazın. @@ -813,19 +848,25 @@ oauth2_client_id=İstemci Kimliği oauth2_client_secret=İstemci Gizliliği oauth2_regenerate_secret=Gizliliği Yeniden Oluştur oauth2_regenerate_secret_hint=Gizliliğini mi kaybettin? +oauth2_client_secret_hint=Bu sayfadan ayrıldıktan veya yeniledikten sonra gizliliği göremeyeceksiniz. Kaydettiğinizden emin olun. oauth2_application_edit=Düzenle oauth2_application_create_description=OAuth2 uygulamaları, üçüncü taraf uygulamanıza bu durumda kullanıcı hesaplarına erişim sağlar. +oauth2_application_remove_description=Bir OAuth2 uygulamasının kaldırılması, bu sunucudaki yetkili kullanıcı hesaplarına erişmesini önler. Devam edilsin mi? +oauth2_application_locked=Gitea kimi OAuth2 uygulamalarının başlangıçta ön kaydını, yapılandırmada etkinleştirilmişse yapabilir. Beklenmeyen davranışı önlemek için bunlar ne düzenlenmeli ne de kaldırılmalı. Daha fazla bilgi için OAuth2 belgesine bakın. authorized_oauth2_applications=Yetkili OAuth2 Uygulamaları +authorized_oauth2_applications_description=Kişisel Gitea hesabınıza bu üçüncü parti uygulamalara erişim izni verdiniz. Lütfen artık ihtiyaç duyulmayan uygulamalara erişimi iptal edin. revoke_key=İptal Et revoke_oauth2_grant=Erişimi İptal Et revoke_oauth2_grant_description=Bu üçüncü taraf uygulamasına erişimin iptal edilmesi bu uygulamanın verilerinize erişmesini önleyecektir. Emin misiniz? +revoke_oauth2_grant_success=Erişim başarıyla kaldırıldı. twofa_desc=İki faktörlü kimlik doğrulama, hesabınızın güvenliğini artırır. twofa_is_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmiş. twofa_not_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmemiş. twofa_disable=İki Aşamalı Doğrulamayı Devre Dışı Bırak twofa_scratch_token_regenerate=Geçici Kodu Yeniden Üret +twofa_scratch_token_regenerated=Geçici kodunuz şimdi %s. Güvenli bir yerde saklayın, tekrar gösterilmeyecektir. twofa_enroll=İki Faktörlü Kimlik Doğrulamaya Kaydolun twofa_disable_note=Gerekirse iki faktörlü kimlik doğrulamayı devre dışı bırakabilirsiniz. twofa_disable_desc=İki faktörlü kimlik doğrulamayı devre dışı bırakmak hesabınızı daha az güvenli hale getirir. Devam edilsin mi? @@ -852,8 +893,10 @@ remove_account_link=Bağlantılı Hesabı Kaldır remove_account_link_desc=Bağlantılı bir hesabı kaldırmak, onunla Gitea hesabınıza erişimi iptal edecektir. Devam edilsin mi? remove_account_link_success=Bağlantılı hesap kaldırıldı. +hooks.desc=Sahip olduğunuz tüm depolar için tetiklenecek web istemcileri ekle. orgs_none=Herhangi bir organizasyonun bir üyesi değilsiniz. +repos_none=Herhangi bir depoya sahip değilsiniz. delete_account=Hesabınızı Silin delete_prompt=Bu işlem kullanıcı hesabınızı kalıcı olarak siler. Bu işlem GERİ ALINAMAZ. @@ -872,9 +915,12 @@ visibility=Kullanıcı görünürlüğü visibility.public=Herkese Açık visibility.public_tooltip=Herkes tarafından görünür visibility.limited=Sınırlı +visibility.limited_tooltip=Sadece oturum açmış kullanıcılar tarafından görünür visibility.private=Özel +visibility.private_tooltip=Sadece katıldığınız organizasyonların üyeleri tarafından görünür [repo] +new_repo_helper=Bir depo, sürüm geçmişi dahil tüm proje dosyalarını içerir. Zaten başka bir yerde mi barındırıyorsunuz? Depoyu taşıyın. owner=Sahibi owner_helper=Bazı organizasyonlar, en çok depo sayısı sınırı nedeniyle açılır menüde görünmeyebilir. repo_name=Depo İsmi @@ -886,6 +932,7 @@ template_helper=Depoyu şablon yap template_description=Şablon depoları, kullanıcıların aynı dizin yapısı, dosyaları ve isteğe bağlı ayarlarla yeni depoları oluşturmasına izin verir. visibility=Görünürlük visibility_description=Yalnızca sahibi veya haklara sahip organizasyon üyeleri onu görebilecek. +visibility_helper=Depoyu özel yap visibility_helper_forced=Site yöneticiniz, yeni depoları gizli olmaya zorluyor. visibility_fork_helper=(Bunu değiştirmek tüm çatallamaları etkileyecektir.) clone_helper=Klonlama konusunda yardıma mı ihtiyacınız var? Yardım adresini ziyaret edin. @@ -894,6 +941,7 @@ fork_from=Buradan Çatalla already_forked=%s deposunu zaten çatalladınız fork_to_different_account=Başka bir hesaba çatalla fork_visibility_helper=Çatallanmış bir deponun görünürlüğü değiştirilemez. +fork_no_valid_owners=Geçerli bir sahibi olmadığı için bu depo çatallanamaz. use_template=Bu şablonu kullan clone_in_vsc=VS Code'ta klonla download_zip=ZIP indir @@ -930,6 +978,8 @@ mirror_interval_invalid=Yansı süre aralığı geçerli değil. mirror_sync_on_commit=İşlemeler gönderildiğinde senkronize et mirror_address=URL'den Klonla mirror_address_desc=Yetkilendirme bölümüne gerekli tüm kimlik bilgilerini girin. +mirror_address_url_invalid=Sağlanan URL geçersiz. URL'nin tüm bileşenleri doğru olarak girilmelidir. +mirror_address_protocol_invalid=Sağlanan URL geçersiz. Yalnızca http(s):// veya git:// konumları yansı olabilir. mirror_lfs=Büyük Dosya Depolama (LFS) mirror_lfs_desc=LFS verisinin yansılamasını etkinleştir. mirror_lfs_endpoint=LFS Uç Noktası @@ -961,6 +1011,8 @@ transfer.accept=Aktarımı Kabul Et transfer.accept_desc=`"%s" tarafına aktar` transfer.reject=Aktarımı Reddet transfer.reject_desc=`"%s" tarafına aktarımı iptal et` +transfer.no_permission_to_accept=Bu aktarımı kabul etme izniniz yok. +transfer.no_permission_to_reject=Bu aktarımı reddetme izniniz yok. desc.private=Özel desc.public=Genel @@ -981,6 +1033,8 @@ template.issue_labels=Konu Etiketleri template.one_item=En az bir şablon öğesi seçmelisiniz template.invalid=Bir şablon deposu seçmelisiniz +archive.title=Bu depo arşivlendi. Dosyaları görüntüleyebilir ve klonlayabilirsiniz ama işleme gönderemez veya konu veya değişiklik isteği açamazsınız. +archive.title_date=Bu depo %s tarihinde arşivlendi. Dosyaları görüntüleyebilir ve klonlayabilirsiniz ama işleme gönderemez veya konu veya değişiklik isteği açamazsınız. archive.issue.nocomment=Bu depo arşivlendi. Konular bölümünde yorum yapamazsınız. archive.pull.nocomment=Bu depo arşivlendi. Değişiklik istekleri bölümünde yorum yapamazsınız. @@ -997,6 +1051,7 @@ migrate_options_lfs=LFS dosyalarını taşı migrate_options_lfs_endpoint.label=LFS Uç Noktası migrate_options_lfs_endpoint.description=Taşıma, LFS sunucusunu belirlemek için Git uzak sunucusunu kullanmaya çalışacak. Eğer LFS veri deposu başka yerdeyse özel bir uç nokta da belirtebilirsiniz. migrate_options_lfs_endpoint.description.local=Yerel bir sunucu yolu da destekleniyor. +migrate_options_lfs_endpoint.placeholder=Boş bırakılırsa, uç nokta klon URL'sinden türetilecektir migrate_items=Göç Öğeleri migrate_items_wiki=Wiki migrate_items_milestones=Kilometre Taşları @@ -1099,6 +1154,10 @@ file_view_rendered=Oluşturulanları Görüntüle file_view_raw=Ham Görünüm file_permalink=Kalıcı Bağlantı file_too_large=Bu dosya görüntülemek için çok büyük. +invisible_runes_header=`Bu dosya görünmez Evrensel Kodlu karakter içeriyor` +invisible_runes_description=`Bu dosya, insanlar tarafından ayırt edilemeyen ama bir bilgisayar tarafından farklı bir şekilde işlenebilecek görünmez evrensel kodlu karakter içeriyor. Eğer bunu kasıtlı olarak yaptıysanız bu uyarıyı yok sayabilirsiniz. Gizli karakterleri göstermek için Kaçış Karakterli düğmesine tıklayın.` +ambiguous_runes_header=`Bu dosya muğlak Evrensel Kodlu karakter içeriyor` +ambiguous_runes_description=`Bu dosya, başka karakterlerle karıştırılabilecek evrensel kodlu karakter içeriyor. Eğer bunu kasıtlı olarak yaptıysanız bu uyarıyı yok sayabilirsiniz. Gizli karakterleri göstermek için Kaçış Karakterli düğmesine tıklayın.` invisible_runes_line=`Bu satırda görünmez evrensel kodlu karakter var` ambiguous_runes_line=`Bu satırda muğlak evrensel kodlu karakter var` ambiguous_character=`%[1]c [U+%04[1]X], %[2]c [U+%04[2]X] ile karıştırılabilir` @@ -1111,11 +1170,15 @@ video_not_supported_in_browser=Tarayıcınız HTML5 'video' etiketini desteklemi audio_not_supported_in_browser=Tarayıcınız HTML5 'audio' etiketini desteklemiyor. stored_lfs=Git LFS ile depolandı symbolic_link=Sembolik Bağlantı +executable_file=Çalıştırılabilir Dosya commit_graph=İşleme Grafiği commit_graph.select=Dalları seç commit_graph.hide_pr_refs=Değişiklik İsteklerini Gizle commit_graph.monochrome=Siyah Beyaz commit_graph.color=Renk +commit.contained_in=Bu işleme şunda yer alıyor: +commit.contained_in_default_branch=Bu işleme varsayılan dalın bir parçasıdır +commit.load_referencing_branches_and_tags=Bu işlemeyi kullanan dal ve etiketleri yükle blame=Suçlama download_file=Dosya indir normal_view=Normal Görünüm @@ -1330,6 +1393,7 @@ issues.delete_branch_at=`%s dalı silindi %s` issues.filter_label=Etiket issues.filter_label_exclude=`Etiketleri hariç tutmak için alt + tıkla/enter kullanın` issues.filter_label_no_select=Tüm etiketler +issues.filter_label_select_no_label=Etiket yok issues.filter_milestone=Kilometre Taşı issues.filter_milestone_all=Tüm kilometre taşları issues.filter_milestone_none=Kilometre taşı yok @@ -1363,6 +1427,7 @@ issues.filter_sort.moststars=En çok yıldızlılar issues.filter_sort.feweststars=En az yıldızlılar issues.filter_sort.mostforks=En çok çatallananlar issues.filter_sort.fewestforks=En az çatallananlar +issues.keyword_search_unavailable=Anahtar kelime ile arama şu an mevcut değil. Lütfen site yöneticisiyle iletişime geçin. issues.action_open=Açık issues.action_close=Kapat issues.action_label=Etiket @@ -1383,6 +1448,7 @@ issues.next=Sonraki issues.open_title=Açık issues.closed_title=Kapalı issues.draft_title=Taslak +issues.num_comments_1=%d yorum issues.num_comments=%d yorum issues.commented_at=`%s yorum yaptı` issues.delete_comment_confirm=Bu yorumu silmek istediğinizden emin misiniz? @@ -1391,6 +1457,7 @@ issues.context.quote_reply=Alıntı Cevapla issues.context.reference_issue=Yeni konuda referans issues.context.edit=Düzenle issues.context.delete=Sil +issues.no_content=Herhangi bir açıklama sağlanmadı. issues.close=Konuyu Kapat issues.comment_pull_merged_at=%[1]s işlemesi, %[2]s dalına birleştirildi %[3]s issues.comment_manually_pull_merged_at=%[1]s işlemesi, %[2]s dalına elle birleştirildi %[3]s @@ -1425,6 +1492,8 @@ issues.label_title=Etiket adı issues.label_description=Etiket açıklaması issues.label_color=Etiket rengi issues.label_exclusive=Özel +issues.label_archive=Etiketi Arşivle +issues.label_archive_tooltip=Arşivlenmiş etiketler, bir konuya etiket uygulanırken etiket aramasından çıkarılıyor. Bir konudaki mevcut etiketler bundan etkilenmiyor, böylece eskimiş etiketleri bilgi kaybına uğramadan kullanımdan kaldırabiliyorsunuz. issues.label_exclusive_desc=Kapsam/öğe etiketini, diğer kapsam/ etiketleriyle ayrışık olacak şekilde adlandırın. issues.label_exclusive_warning=Çakışan kapsamlı etiketler, bir konu veya değişiklik isteği etiketleri düzenlenirken kaldırılacaktır. issues.label_count=%d etiket @@ -1479,6 +1548,7 @@ issues.tracking_already_started=`başka bir konuda zaten zaman issues.stop_tracking=Zamanlayıcıyı Bitir issues.stop_tracking_history=`%s çalışmayı durdurdu` issues.cancel_tracking=Yoksay +issues.cancel_tracking_history=`%s zaman takibini iptal etti` issues.add_time=El ile Zaman Ekle issues.del_time=Bu zaman kaydını sil issues.add_time_short=Zaman Ekle @@ -1502,6 +1572,7 @@ issues.due_date_form=yyyy-aa-gg issues.due_date_form_add=Bitiş tarihi ekle issues.due_date_form_edit=Düzenle issues.due_date_form_remove=Kaldır +issues.due_date_not_writer=Bir konunun bitiş tarihini güncellemek için bu depoda yazma iznine ihtiyacınız var. issues.due_date_not_set=Bitiş tarihi atanmadı. issues.due_date_added=bitiş tarihini %s olarak %s ekledi issues.due_date_modified=bitiş tarihini %[2]s iken %[1]s olarak %[3]s değiştirdi @@ -1557,6 +1628,9 @@ issues.review.pending.tooltip=Bu yorum başkaları tarafından görünmüyor. Be issues.review.review=Gözden Geçir issues.review.reviewers=Gözden Geçirenler issues.review.outdated=Eskimiş +issues.review.outdated_description=Bu yorum yapıldığından beri içerik değişti +issues.review.option.show_outdated_comments=Zaman aşımına uğramış yorumları göster +issues.review.option.hide_outdated_comments=Zaman aşımına uğramış yorumları gizle issues.review.show_outdated=Eskiyi göster issues.review.hide_outdated=Eskiyi gizle issues.review.show_resolved=Çözülenleri göster @@ -1596,6 +1670,13 @@ pulls.switch_comparison_type=Karşılaştırma türünü değiştir pulls.switch_head_and_base=Ana ve temeli değiştir pulls.filter_branch=Dal filtrele pulls.no_results=Sonuç bulunamadı. +pulls.show_all_commits=Tüm işlemeleri göster +pulls.show_changes_since_your_last_review=Son incelemenizden sonraki değişiklikleri göster +pulls.showing_only_single_commit=Sadece %[1]s işlemesindeki değişiklikler gösteriliyor +pulls.showing_specified_commit_range=%[1]s..%[2]s arasındaki değişiklikler gösteriliyor +pulls.select_commit_hold_shift_for_range=İşleme seç. Bir aralık seçmek için Shift'e basılı tutup tıklayın +pulls.review_only_possible_for_full_diff=İnceleme sadece tam fark görüntülemede mümkündür +pulls.filter_changes_by_commit=İşleme ile süz pulls.nothing_to_compare=Bu dallar eşit. Değişiklik isteği oluşturmaya gerek yok. pulls.nothing_to_compare_and_allow_empty_pr=Bu dallar eşittir. Bu Dİ boş olacak. pulls.has_pull_request=`Bu dallar arasında zaten bir değişiklik isteği var: %[2]s#%[3]d` @@ -1627,6 +1708,12 @@ pulls.is_empty=Bu daldaki değişiklikler zaten hedef dalda mevcut. Bu boş bir pulls.required_status_check_failed=Bazı gerekli denetimler başarılı olmadı. pulls.required_status_check_missing=Gerekli bazı kontroller eksik. pulls.required_status_check_administrator=Yönetici olarak, bu değişiklik isteğini yine de birleştirebilirsiniz. +pulls.blocked_by_approvals=Bu değişiklik isteğinin henüz yeterli onayı yok. %d onay var, %d onay gerekiyor. +pulls.blocked_by_rejection=Bu değişiklik isteğinde resmi bir inceleyici tarafından istenen değişiklikler var. +pulls.blocked_by_official_review_requests=Bu değişiklik isteğinde resmi inceleme istekleri var. +pulls.blocked_by_outdated_branch=Bu değişiklik isteği eskidiği için engellendi. +pulls.blocked_by_changed_protected_files_1=Bu değişiklik isteği, korumalı bir dosyayı değiştirdiği için engellendi: +pulls.blocked_by_changed_protected_files_n=Bu değişiklik isteği, korumalı dosyaları değiştirdiği için engellendi: pulls.can_auto_merge_desc=Bu değişiklik isteği otomatik olarak birleştirilebilir. pulls.cannot_auto_merge_desc=Bu değişiklik isteği, çakışmalar nedeniyle otomatik olarak birleştirilemiyor. pulls.cannot_auto_merge_helper=Çakışmaları çözmek için el ile birleştirin. @@ -1701,7 +1788,9 @@ pulls.auto_merge_canceled_schedule_comment=`bu değişiklik isteğinin, tüm den pulls.delete.title=Bu değişiklik isteği silinsin mi? pulls.delete.text=Bu değişiklik isteğini gerçekten silmek istiyor musunuz? (Bu işlem tüm içeriği kalıcı olarak silecektir. Arşivde tutma niyetiniz varsa silmek yerine kapatmayı düşünün) +pulls.recently_pushed_new_branches=%[1]s dalına ittiniz %[2]s +pull.deleted_branch=(silindi): %s milestones.new=Yeni Kilometre Taşı milestones.closed=Kapalı %s @@ -1709,6 +1798,7 @@ milestones.update_ago=%s tarihinde güncellendi milestones.no_due_date=Bitiş tarihi yok milestones.open=Aç milestones.close=Kapat +milestones.new_subheader=Kilometre taşları konuları yönetmenize ve gelişmelerini takip etmenize yardımcı olur. milestones.completeness=%d%% Tamamlandı milestones.create=Kilometre Taşı Oluştur milestones.title=Başlık @@ -1732,6 +1822,19 @@ milestones.filter_sort.most_complete=En çok tamamlama milestones.filter_sort.most_issues=En çok konu milestones.filter_sort.least_issues=En az konu +signing.will_sign=Bu işleme "%s" anahtarıyla imzalanacak. +signing.wont_sign.error=İşlemenin imzalanıp imzalanamayacağını kontrol ederken bir hata oluştu. +signing.wont_sign.nokey=Bu işlemeyi imzalamak için anahtar yok. +signing.wont_sign.never=İşlemeler asla imzalanmaz. +signing.wont_sign.always=İşlemeler her zaman imzalanır. +signing.wont_sign.pubkey=Hesabınızla ilişkilendirilmiş bir ortak anahtarınız olmadığı için işleme imzalanmayacak. +signing.wont_sign.twofa=İşlemelerin imzalanması için iki aşamalı kimlik doğrulamayı etkinleştirmelisiniz. +signing.wont_sign.parentsigned=Üst işleme imzalanmadığı için bu işleme imzalanmayacak. +signing.wont_sign.basesigned=Temel işleme imzalanmadığı için birleştirme imzalanmayacak. +signing.wont_sign.headsigned=Ana işleme imzalanmadığı için birleştirme imzalanmayacak. +signing.wont_sign.commitssigned=İlişkili tüm işlemeler imzalanmadığı için birleştirme imzalanmayacak. +signing.wont_sign.approved=Değişiklik İsteği onaylanmadığı için birleştirme imzalanmayacak. +signing.wont_sign.not_signed_in=Oturum açmadınız. ext_wiki=Harici Vikiye Erişim ext_wiki.desc=Harici bir wiki'ye bağlantı. @@ -1861,7 +1964,9 @@ settings.mirror_settings.docs.disabled_push_mirror.info=Gönderme yansıları si settings.mirror_settings.docs.no_new_mirrors=Deponuz değişiklikleri başka bir depoyla yansılıyor. Şu an başka bir yansı oluşturamayacağınızı unutmayın. settings.mirror_settings.docs.can_still_use=Her ne kadar mevcut yansıları değiştiremiyor veya yeni yansı oluşturamıyor olsanız da, hala mevcut yansıyı kullanabilirsiniz. settings.mirror_settings.docs.pull_mirror_instructions=Çekme yansıyı kurmak için, lütfen şuraya danışın: +settings.mirror_settings.docs.more_information_if_disabled=İtme ve çekme yansıları hakkında daha fazla bilgiye şuradan ulaşabilirsiniz: settings.mirror_settings.docs.doc_link_title=Depoların yansısını nasıl oluştururum? +settings.mirror_settings.docs.doc_link_pull_section=belgelerin "uzak bir depodan çekmek" bölümü. settings.mirror_settings.docs.pulling_remote_title=Uzak bir depodan çekmek settings.mirror_settings.mirrored_repository=Yansıtılmış depo settings.mirror_settings.direction=Yön @@ -1871,6 +1976,7 @@ settings.mirror_settings.last_update=Son güncelleme settings.mirror_settings.push_mirror.none=Yapılandırılmış yansı gönderimi yok settings.mirror_settings.push_mirror.remote_url=Git Uzak Depo URL'si settings.mirror_settings.push_mirror.add=Yansı Gönderimi Ekle +settings.mirror_settings.push_mirror.edit_sync_time=Yansı eşzamanlama aralığını düzenle settings.sync_mirror=Şimdi Eşitle settings.mirror_sync_in_progress=Yansı senkronizasyonu devam ediyor. Bir dakika sonra tekrar kontrol edin. @@ -1940,6 +2046,7 @@ settings.transfer.rejected=Depo aktarımı reddedildi. settings.transfer.success=Depo aktarımı başarıyla tamamlandı. settings.transfer_abort=Aktarımı iptal et settings.transfer_abort_invalid=Var olmayan bir depo aktarımını iptal edemezsiniz. +settings.transfer_abort_success=%s tarafına yapılan depo aktarımı başarıyla iptal edildi. settings.transfer_desc=Bu depoyu bir kullanıcıya veya yönetici haklarına sahip olduğunuz bir organizasyona aktarın. settings.transfer_form_title=Onaylamak için depo adını girin: settings.transfer_in_progress=Şu anda devam etmekte olan bir aktarım mevcut. Eğer bu depoyu başka bir kullanıcıya aktarmak istiyorsanız mevcut aktarımı iptal edin. @@ -2210,16 +2317,23 @@ settings.tags.protection.none=Korumalı etiket yok. settings.tags.protection.pattern.description=Birden çok etiketi eşleştirmek için tek bir ad, glob deseni veya normal ifade kullanabilirsiniz. Daha fazlası için korumalı etiketler rehberini okuyun. settings.bot_token=Bot Jetonu settings.chat_id=Sohbet Kimliği +settings.thread_id=İş Parçacığı ID settings.matrix.homeserver_url=Ev sunucusu URL'si settings.matrix.room_id=Oda Kimliği settings.matrix.message_type=Mesaj Türü settings.archive.button=Depoyu Arşivle settings.archive.header=Bu Depoyu Arşivle +settings.archive.text=Depoyu arşivlemek onu tamamen salt okunur yapacaktır. Panoda gizlenecektir. Hiç kimse (siz bile) yeni işleme yapamayacak veya yeni konu veya değişiklik isteği açamayacak. settings.archive.success=Depo başarıyla arşivlendi. settings.archive.error=Depoyu arşivlemeye çalışırken bir hata oluştu. Daha fazla ayrıntı için günlüğe bakın. settings.archive.error_ismirror=Yansılanmış bir depoyu arşivleyemezsiniz. settings.archive.branchsettings_unavailable=Depo arşivlenirse dal ayarları kullanılamaz. settings.archive.tagsettings_unavailable=Depo arşivlenmişse etiket ayarları kullanılamaz. +settings.unarchive.button=Depoyu Arşivden Çıkar +settings.unarchive.header=Bu Depoyu Arşivden Çıkar +settings.unarchive.text=Depoyu arşivden çıkarmak, yeni sorunların ve değişiklik isteklerinin yanı sıra işleme ve itme yeteneğini de geri kazandıracaktır. +settings.unarchive.success=Depo başarıyla arşivden çıkarıldı. +settings.unarchive.error=Depoyu arşivden çıkarmaya çalışırken bir hata oluştu. Daha fazla ayrıntı için günlüğe bakın. settings.update_avatar_success=Depo resmi güncellendi. settings.lfs=LFS settings.lfs_filelist=Bu depoda barındırılan LFS dosyaları @@ -2286,6 +2400,7 @@ diff.show_more=Daha Fazla Göster diff.load=Fark Yükle diff.generated=üretilen diff.vendored=sağlanmış +diff.comment.add_line_comment=Satır yorum ekle diff.comment.placeholder=Yorum Yap diff.comment.markdown_info=Markdown ile şekillendirme desteklenir. diff.comment.add_single_comment=Bir yorum ekle @@ -2342,6 +2457,7 @@ release.edit_release=Sürümü Güncelle release.delete_release=Sürümü Sil release.delete_tag=Etiketi Sil release.deletion=Sürümü Sil +release.deletion_desc=Bir sürümü silmek onu sadece Gitea'dan kaldırır. Git etiketini, deponuzun içeriğini veya geçmişini etkilemez. Devam edilsin mi? release.deletion_success=Sürüm silindi. release.deletion_tag_desc=Bu etiket depodan silinecek. Depo içeriği ve geçmişi değişmeden kalır. Devam edilsin mi? release.deletion_tag_success=Etiket silindi. @@ -2362,6 +2478,7 @@ branch.already_exists=`"%s" isimli bir dal zaten mevcut.` branch.delete_head=Sil branch.delete=`"%s" Dalını Sil` branch.delete_html=Bölüm Sil +branch.delete_desc=Bir dalı silmek kalıcıdır. Her ne kadar silinen dal tamamen kaldırılana kadar çok kısa bir süre yaşamını sürdürse de, çoğu durumda bu işlem GERİ ALINAMAZ. Devam edilsin mi? branch.deletion_success=`"%s" dalı silindi.` branch.deletion_failed=`"%s" dalı silinemedi.` branch.delete_branch_has_new_commits=`"%s" dalı silinemedi çünkü birleştirme sonrasında yeni işlemeler eklendi.` @@ -2401,6 +2518,7 @@ tag.create_success=`"%s" etiketi oluşturuldu.` topic.manage_topics=Konuları Yönet topic.done=Bitti topic.count_prompt=25'ten fazla konu seçemezsiniz +topic.format_prompt=Konular bir harf veya rakamla başlamalı, kısa çizgi ('-') ve nokta ('.') içerebilir ve en fazla 35 karakter uzunluğunda olabilir. Harfler küçük harf olmalıdır. find_file.go_to_file=Dosyaya git find_file.no_matching=Eşleşen dosya bulunamadı @@ -2439,6 +2557,7 @@ form.create_org_not_allowed=Organizasyon oluşturmanıza izin verilmiyor. settings=Ayarlar settings.options=Organizasyon settings.full_name=Tam İsim +settings.email=İletişim E-posta settings.website=Web Sitesi settings.location=Lokasyon settings.permission=İzinler @@ -2452,6 +2571,7 @@ settings.visibility.private_shortname=Özel settings.update_settings=Ayarları Güncelle settings.update_setting_success=Organizasyon ayarları güncellendi. +settings.change_orgname_prompt=Not: Organizasyon adını değiştirmek organizasyonunuzun URL'sini de değiştirecek ve eski ismi serbest bıracaktır. settings.change_orgname_redirect_prompt=Eski ad, talep edilene kadar yeniden yönlendirilecektir. settings.update_avatar_success=Organizasyonun resmi güncellendi. settings.delete=Organizasyonu Sil @@ -2527,15 +2647,19 @@ teams.all_repositories_helper=Takımın tüm depolara erişimi vardır. Bunu se teams.all_repositories_read_permission_desc=Bu takım tüm depolara Okuma erişimi sağlar: üyeler depoları görüntüleyebilir ve kopyalayabilir. teams.all_repositories_write_permission_desc=Bu takım tüm depolara Yazma erişimi sağlar: üyeler depolardan okuyabilir ve depolara itebilir. teams.all_repositories_admin_permission_desc=Bu takım tüm depolara Yönetici erişimi sağlar: üyeler depolardan okuyabilir, itebilir ve katkıcıları ekleyebilir. +teams.invite.title=%s takımına (Organizasyon: %s) katılmaya davet edildiniz. teams.invite.by=%s tarafından davet edildi teams.invite.description=Takıma katılmak için aşağıdaki düğmeye tıklayın. [admin] dashboard=Pano +identity_access=Kimlik ve Erişim users=Kullanıcı Hesapları organizations=Organizasyonlar +assets=Kod Varlıkları repositories=Depolar hooks=Web İstemcileri +integrations=Bütünleştirmeler authentication=Yetkilendirme Kaynakları emails=Kullanıcı E-postaları config=Yapılandırma @@ -2544,6 +2668,7 @@ monitor=İzleme first_page=İlk last_page=Son total=Toplam: %d +settings=Yönetici Ayarları dashboard.new_version_hint=Gitea %s şimdi hazır, %s çalıştırıyorsunuz. Ayrıntılar için blog'a bakabilirsiniz. dashboard.statistic=Özet @@ -2556,11 +2681,13 @@ dashboard.clean_unbind_oauth=Bağsız OAuth bağlantılarını temizle dashboard.clean_unbind_oauth_success=Tüm bağsız OAuth bağlantıları silindi. dashboard.task.started=Görev Başlatıldı: %[1]s dashboard.task.process=Görev: %[1]s +dashboard.task.cancelled=Görev: %[1]s iptal edildi: %[3]s dashboard.task.error=Görevde Hata: %[1]s: %[3]s dashboard.task.finished=Görev: %[1]s %[2]s tarafından başlatıldı ve bitti dashboard.task.unknown=Bilinmeyen görev: %[1]s dashboard.cron.started=Cron Başlatıldı: %[1]s dashboard.cron.process=Cron: %[1]s +dashboard.cron.cancelled=Cron: %[1]s iptal edildi: %[3]s dashboard.cron.error=Cron Hatası: %s: %[3]s dashboard.cron.finished=Cron: %[1]s bitti dashboard.delete_inactive_accounts=Etkinleştirilmemiş tüm hesapları sil @@ -2570,6 +2697,7 @@ dashboard.delete_repo_archives.started=Tüm depo arşivlerini silme görevi baş dashboard.delete_missing_repos=Git dosyaları eksik olan tüm depoları sil dashboard.delete_missing_repos.started=Git dosyaları eksik olan tüm depoları silme görevi başladı. dashboard.delete_generated_repository_avatars=Oluşturulan depo resimlerini sil +dashboard.sync_repo_branches=Eşzamanlama git verisinden veritabanlarına dalları kaçırdı dashboard.update_mirrors=Yansıları Güncelle dashboard.repo_health_check=Tüm depoların sağlığını denetle dashboard.check_repo_stats=Tüm depo istatistiklerini denetle @@ -2623,6 +2751,7 @@ dashboard.gc_lfs=LFS üst nesnelerin atıklarını temizle dashboard.stop_zombie_tasks=Zombi görevleri durdur dashboard.stop_endless_tasks=Daimi görevleri durdur dashboard.cancel_abandoned_jobs=Terkedilmiş görevleri iptal et +dashboard.sync_branch.started=Dal Eşzamanlaması başladı users.user_manage_panel=Kullanıcı Hesap Yönetimi users.new_account=Yeni Kullanıcı Hesabı @@ -2708,10 +2837,12 @@ repos.stars=Yıldızlar repos.forks=Çatallar repos.issues=Konular repos.size=Boyut +repos.lfs_size=LFS Boyutu packages.package_manage_panel=Paket Yönetimi packages.total_size=Toplam Boyut: %s packages.unreferenced_size=Referanssız Boyut: %s +packages.cleanup=Süresi dolmuş veriyi temizle packages.owner=Sahibi packages.creator=Oluşturan packages.name=İsim @@ -2820,6 +2951,7 @@ auths.sspi_default_language=Varsayılan kullanıcı dili auths.sspi_default_language_helper=SSPI kimlik doğrulama yöntemi tarafından otomatik olarak oluşturulan kullanıcılar için varsayılan dil. Dili otomatik olarak algılamayı tercih ederseniz boş bırakın. auths.tips=İpuçları auths.tips.oauth2.general=OAuth2 Kimlik Doğrulama +auths.tips.oauth2.general.tip=Yeni bir OAuth2 kimlik doğrulama kaydederken, geri çağırma/yönlendirme URL'si şu olmalıdır: auths.tip.oauth2_provider=OAuth2 Sağlayıcısı auths.tip.bitbucket=https://bitbucket.org/account/user//oauth-consumers/new adında yeni bir OAuth tüketicisi kaydedin ve 'Hesap' - 'Oku' iznini ekleyin auths.tip.nextcloud=Aşağıdaki "Ayarlar -> Güvenlik -> OAuth 2.0 istemcisi" menüsünü kullanarak örneğinize yeni bir OAuth tüketicisi kaydedin @@ -2861,6 +2993,7 @@ config.disable_router_log=Yönlendirici Log'larını Devre Dışı Bırak config.run_user=Şu Kullanıcı Olarak Çalıştır config.run_mode=Çalıştırma Modu config.git_version=Git Sürümü +config.app_data_path=Uygulama Veri Yolu config.repo_root_path=Depo Kök Yolu config.lfs_root_path=LFS Kök Dizini config.log_file_root_path=Günlük Dosyası Yolu @@ -3136,6 +3269,7 @@ desc=Depo paketlerini yönet. empty=Henüz hiçbir paket yok. empty.documentation=Paket kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz. empty.repo=Bir paket yüklediniz ama burada gösterilmiyor mu? Paket ayarlarına gidin ve bu depoya bağlantı verin. +registry.documentation=%s kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz. filter.type=Tür filter.type.all=Tümü filter.no_result=Filtreniz herhangi bir sonuç döndürmedi. @@ -3225,6 +3359,8 @@ pub.install=Paketi Dart ile kurmak için, şu komutu çalıştırın: pypi.requires=Gereken Python pypi.install=Paketi pip ile kurmak için, şu komutu çalıştırın: rpm.registry=Bu kütüğü komut satırını kullanarak kurun: +rpm.distros.redhat=RedHat tabanlı dağıtımlarda +rpm.distros.suse=SUSE tabanlı dağıtımlarda rpm.install=Paketi kurmak için, aşağıdaki komutu çalıştırın: rubygems.install=Paketi gem ile kurmak için, şu komutu çalıştırın: rubygems.install2=veya paketi Gemfile dosyasına ekleyin: @@ -3249,14 +3385,17 @@ settings.delete.success=Paket silindi. settings.delete.error=Paket silinemedi. owner.settings.cargo.title=Cargo Kayıt Dizini owner.settings.cargo.initialize=Dizini İlkle +owner.settings.cargo.initialize.description=Cargo kütüğünü kullanmak için özel bir Git depo indeksi gerekiyor. Bu seçeneği kullanmak depoyu (yeniden) oluşturacak ve otomatik olarak yapılandıracaktır. owner.settings.cargo.initialize.error=Cargo dizini oluşturma başarısız oldu: %v owner.settings.cargo.initialize.success=Cargo dizini başarıyla oluşturuldu. owner.settings.cargo.rebuild=Dizini yeniden oluştur +owner.settings.cargo.rebuild.description=İndeks, depolanmış Cargo paketleriyle eşzamanlanmamışsa yeniden oluşturma yararlı olabilir. owner.settings.cargo.rebuild.error=Cargo dizinini yeniden oluşturma başarısız oldu: %v owner.settings.cargo.rebuild.success=Cargo dizini başarıyla yeniden oluşturuldu. owner.settings.cleanuprules.title=Temizleme Kurallarını Yönet owner.settings.cleanuprules.add=Temizleme Kuralı Ekle owner.settings.cleanuprules.edit=Temizleme Kuralı Düzenle +owner.settings.cleanuprules.none=Temizleme kuralı yok. Lütfen belgelere danışın. owner.settings.cleanuprules.preview=Temizleme Kuralı Önizleme owner.settings.cleanuprules.preview.overview=%d paketin kaldırılması planlandı. owner.settings.cleanuprules.preview.none=Temizleme kuralı herhangi bir paketle eşleşmiyor. @@ -3275,6 +3414,7 @@ owner.settings.cleanuprules.success.update=Temizleme kuralı güncellendi. owner.settings.cleanuprules.success.delete=Temizleme kuralı silindi. owner.settings.chef.title=Chef Kütüğü owner.settings.chef.keypair=Anahtar çifti üret +owner.settings.chef.keypair.description=Chef kütüğünde kimlik doğrulaması için bir anahtar çifti gereklidir. Eğer daha önce bir anahtar çifti ürettiyseniz, yeni bir anahtar çifti üretmek eski anahtar çiftini ıskartaya çıkartacaktır. [secrets] secrets=Gizlilikler @@ -3301,6 +3441,7 @@ status.waiting=Bekleniyor status.running=Çalışıyor status.success=Başarılı status.failure=Başarısız +status.cancelled=İptal edildi status.skipped=Atlandı status.blocked=Engellendi @@ -3317,6 +3458,7 @@ runners.labels=Etiketler runners.last_online=Son Görülme Zamanı runners.runner_title=Çalıştırıcı runners.task_list=Bu çalıştırıcıdaki son görevler +runners.task_list.no_tasks=Henüz bir görev yok. runners.task_list.run=Çalıştır runners.task_list.status=Durum runners.task_list.repository=Depo @@ -3341,13 +3483,38 @@ runners.reset_registration_token_success=Çalıştırıcı kayıt belirteci baş runs.all_workflows=Tüm İş Akışları runs.commit=İşle +runs.pushed_by=iten runs.invalid_workflow_helper=İş akışı yapılandırma dosyası geçersiz. Lütfen yapılandırma dosyanızı denetleyin: %s runs.no_matching_runner_helper=Eşleşen çalıştırıcı yok: %s +runs.actor=Aktör runs.status=Durum +runs.actors_no_select=Tüm aktörler +runs.status_no_select=Tüm durumlar +runs.no_results=Eşleşen sonuç yok. +runs.no_runs=İş akışı henüz hiç çalıştırılmadı. +workflow.disable=İş Akışını Devre Dışı Bırak +workflow.disable_success='%s' iş akışı başarıyla devre dışı bırakıldı. +workflow.enable=İş Akışını Etkinleştir +workflow.enable_success='%s' iş akışı başarıyla etkinleştirildi. need_approval_desc=Değişiklik isteği çatalında iş akışı çalıştırmak için onay gerekiyor. +variables=Değişkenler +variables.management=Değişken Yönetimi +variables.creation=Değişken Ekle +variables.none=Henüz hiçbir değişken yok. +variables.deletion=Değişkeni kaldır +variables.deletion.description=Bir değişkeni kaldırma kalıcıdır ve geri alınamaz. Devam edilsin mi? +variables.description=Değişkenler belirli işlemlere aktarılacaktır, bunun dışında okunamaz. +variables.id_not_exist=%d kimlikli değişken mevcut değil. +variables.edit=Değişkeni Düzenle +variables.deletion.failed=Değişken kaldırılamadı. +variables.deletion.success=Değişken kaldırıldı. +variables.creation.failed=Değişken eklenemedi. +variables.creation.success=`"%s" değişkeni eklendi.` +variables.update.failed=Değişken düzenlenemedi. +variables.update.success=Değişken düzenlendi. [projects] type-1.display_name=Kişisel Proje @@ -3355,5 +3522,10 @@ type-2.display_name=Depo Projesi type-3.display_name=Organizasyon Projesi [git.filemode] +changed_filemode=%[1]s → %[2]s +directory=Dizin +normal_file=Normal dosya +executable_file=Çalıştırılabilir dosya symbolic_link=Sembolik Bağlantı +submodule=Alt modül From 60ca3d04a4aea43258798bae7579acdabdef82eb Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 29 Aug 2023 10:11:23 +0900 Subject: [PATCH 40/60] Add fix incorrect can_create_org_repo for org owner team (#26683) Related to: #8312 #26491 In migration v109, we only added a new column `CanCreateOrgRepo` in Team table, but not initial the value of it. This may cause bug like #26491. --------- Co-authored-by: wxiaoguang --- modules/doctor/fix8312.go | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 modules/doctor/fix8312.go diff --git a/modules/doctor/fix8312.go b/modules/doctor/fix8312.go new file mode 100644 index 0000000000..8de3113663 --- /dev/null +++ b/modules/doctor/fix8312.go @@ -0,0 +1,61 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package doctor + +import ( + "context" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" + org_model "code.gitea.io/gitea/models/organization" + "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/modules/log" + + "xorm.io/builder" +) + +func fixOwnerTeamCreateOrgRepo(ctx context.Context, logger log.Logger, autofix bool) error { + count := 0 + + err := db.Iterate( + ctx, + builder.Eq{"authorize": perm.AccessModeOwner, "can_create_org_repo": false}, + func(ctx context.Context, team *org_model.Team) error { + team.CanCreateOrgRepo = true + count++ + + if !autofix { + return nil + } + + return models.UpdateTeam(team, false, false) + }, + ) + if err != nil { + logger.Critical("Unable to iterate across repounits to fix incorrect can_create_org_repo: Error %v", err) + return err + } + + if !autofix { + if count == 0 { + logger.Info("Found no team with incorrect can_create_org_repo") + } else { + logger.Warn("Found %d teams with incorrect can_create_org_repo", count) + } + return nil + } + logger.Info("Fixed %d teams with incorrect can_create_org_repo", count) + + return nil +} + +func init() { + Register(&Check{ + Title: "Check for incorrect can_create_org_repo for org owner teams", + Name: "fix-owner-team-create-org-repo", + IsDefault: false, + Run: fixOwnerTeamCreateOrgRepo, + Priority: 7, + }) +} From c576b50441a62201c2c17de5cfd003c941a3479b Mon Sep 17 00:00:00 2001 From: CaiCandong <50507092+CaiCandong@users.noreply.github.com> Date: Tue, 29 Aug 2023 17:07:15 +0800 Subject: [PATCH 41/60] Fix being unable to use a repo that prohibits accepting PRs as a PR source. (#26785) ## Description Sometimes, we need to use an upstream mirror repository to update the current development repository, but mirror repositories are prohibited from PR. It should not appear in `merge to,` but it can appear in `pull from.` Fix #24585 #26193 #26781 Related #24183 Many thanks to @apnote for assisting me in reproducing this bug! ## ScreenShot --- ### Before ### After --- routers/web/repo/compare.go | 2 +- templates/repo/diff/compare.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 4ceb52d039..aee3495612 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -460,7 +460,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo { rootRepo.ID != ci.HeadRepo.ID && rootRepo.ID != baseRepo.ID { canRead := access_model.CheckRepoUnitUser(ctx, rootRepo, ctx.Doer, unit.TypeCode) - if canRead && rootRepo.AllowsPulls() { + if canRead { ctx.Data["RootRepo"] = rootRepo if !fileOnly { branches, tags, err := getBranchesAndTagsForRepo(ctx, rootRepo) diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index 3bb7c2e81f..e4ae7d4dc8 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -77,7 +77,7 @@
{{$OwnForkCompareName}}:{{.}}
{{end}} {{end}} - {{if .RootRepo}} + {{if and .RootRepo .RootRepo.AllowsPulls}} {{range .RootRepoBranches}}
{{$RootRepoCompareName}}:{{.}}
{{end}} From 29dc1d3b8b91bc83bd2518f80d371e17125c480a Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 29 Aug 2023 11:09:44 +0200 Subject: [PATCH 42/60] Update info regarding internet connection for build (#26776) This build info was outdated since we no longer vendor go modules. Co-authored-by: wxiaoguang --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ff9bcc0659..767a7858e4 100644 --- a/README.md +++ b/README.md @@ -79,10 +79,10 @@ or if SQLite support is required: The `build` target is split into two sub-targets: -- `make backend` which requires [Go Stable](https://go.dev/dl/), required version is defined in [go.mod](/go.mod). -- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies. +- `make backend` which requires [Go Stable](https://go.dev/dl/), the required version is defined in [go.mod](/go.mod). +- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater. -When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity. +Internet connectivity is required to download the go and npm modules. When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js. Parallelism (`make -j `) is not supported. From db09b35590758b62ec8764718dff8a854749bcfb Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 29 Aug 2023 12:14:33 +0200 Subject: [PATCH 43/60] Remove fomantic `item` module (#26775) All selectors had `.ui.items` prefix and I did not find it in any of the templates or JS, so this is a pretty safe removal. Co-authored-by: Giteabot --- web_src/fomantic/build/semantic.css | 525 ---------------------------- web_src/fomantic/semantic.json | 1 - 2 files changed, 526 deletions(-) diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css index c2003ab2de..cddc3f5ee9 100644 --- a/web_src/fomantic/build/semantic.css +++ b/web_src/fomantic/build/semantic.css @@ -10928,531 +10928,6 @@ a.ui.black.header:hover { /******************************* Site Overrides *******************************/ -/*! - * # Fomantic-UI - Item - * http://github.com/fomantic/Fomantic-UI/ - * - * - * Released under the MIT license - * http://opensource.org/licenses/MIT - * - */ - -/******************************* - Standard -*******************************/ - -/*-------------- - Item ----------------*/ - -.ui.items > .item { - display: flex; - margin: 1em 0; - width: 100%; - min-height: 0; - background: transparent; - padding: 0; - border: none; - border-radius: 0; - box-shadow: none; - transition: box-shadow 0.1s ease; - z-index: ''; -} - -.ui.items > .item a { - cursor: pointer; -} - -/*-------------- - Items ----------------*/ - -.ui.items { - margin: 1.5em 0; -} - -.ui.items:first-child { - margin-top: 0 !important; -} - -.ui.items:last-child { - margin-bottom: 0 !important; -} - -/*-------------- - Item ----------------*/ - -.ui.items > .item:after { - display: block; - content: ' '; - height: 0; - clear: both; - overflow: hidden; - visibility: hidden; -} - -.ui.items > .item:first-child { - margin-top: 0; -} - -.ui.items > .item:last-child { - margin-bottom: 0; -} - -/*-------------- - Images ----------------*/ - -.ui.items > .item > .image { - position: relative; - flex: 0 0 auto; - display: block; - float: none; - margin: 0; - padding: 0; - max-height: ''; - align-self: start; -} - -.ui.items > .item > .image > img { - display: block; - width: 100%; - height: auto; - border-radius: 0.125rem; - border: none; -} - -.ui.items > .item > .image:only-child > img { - border-radius: 0; -} - -/*-------------- - Content ----------------*/ - -.ui.items > .item > .content { - display: block; - flex: 1 1 auto; - background: none; - color: rgba(0, 0, 0, 0.87); - margin: 0; - padding: 0; - box-shadow: none; - font-size: 1em; - border: none; - border-radius: 0; -} - -.ui.items > .item > .content:after { - display: block; - content: ' '; - height: 0; - clear: both; - overflow: hidden; - visibility: hidden; -} - -.ui.items > .item > .image + .content { - min-width: 0; - width: auto; - display: block; - margin-left: 0; - align-self: start; - padding-left: 1.5em; -} - -.ui.items > .item > .content > .header { - display: inline-block; - margin: -0.21425em 0 0; - font-family: var(--fonts-regular); - font-weight: 500; - color: rgba(0, 0, 0, 0.85); -} - -/* Default Header Size */ - -.ui.items > .item > .content > .header:not(.ui) { - font-size: 1.28571429em; -} - -/*-------------- - Floated ----------------*/ - -.ui.items > .item [class*="left floated"] { - float: left; -} - -.ui.items > .item [class*="right floated"] { - float: right; -} - -/*-------------- - Content Image ----------------*/ - -.ui.items > .item .content img { - align-self: center; - width: ''; -} - -.ui.items > .item img.avatar, -.ui.items > .item .avatar img { - width: ''; - height: ''; - border-radius: 500rem; -} - -/*-------------- - Description ----------------*/ - -.ui.items > .item > .content > .description { - margin-top: 0.6em; - max-width: auto; - font-size: 1em; - line-height: 1.4285em; - color: rgba(0, 0, 0, 0.87); -} - -/*-------------- - Paragraph ----------------*/ - -.ui.items > .item > .content p { - margin: 0 0 0.5em; -} - -.ui.items > .item > .content p:last-child { - margin-bottom: 0; -} - -/*-------------- - Meta ----------------*/ - -.ui.items > .item .meta { - margin: 0.5em 0 0.5em; - font-size: 1em; - line-height: 1em; - color: rgba(0, 0, 0, 0.6); -} - -.ui.items > .item .meta * { - margin-right: 0.3em; -} - -.ui.items > .item .meta :last-child { - margin-right: 0; -} - -.ui.items > .item .meta [class*="right floated"] { - margin-right: 0; - margin-left: 0.3em; -} - -/*-------------- - Links ----------------*/ - -/* Generic */ - -.ui.items > .item > .content a:not(.ui) { - color: ''; - transition: color 0.1s ease; -} - -.ui.items > .item > .content a:not(.ui):hover { - color: ''; -} - -/* Header */ - -.ui.items > .item > .content > a.header { - color: rgba(0, 0, 0, 0.85); -} - -.ui.items > .item > .content > a.header:hover { - color: #1e70bf; -} - -/* Meta */ - -.ui.items > .item .meta > a:not(.ui) { - color: rgba(0, 0, 0, 0.4); -} - -.ui.items > .item .meta > a:not(.ui):hover { - color: rgba(0, 0, 0, 0.87); -} - -/*-------------- - Labels ----------------*/ - -/*-----Star----- */ - -/* Icon */ - -.ui.items > .item > .content .favorite.icon { - cursor: pointer; - opacity: 0.75; - transition: color 0.1s ease; -} - -.ui.items > .item > .content .favorite.icon:hover { - opacity: 1; - color: #FFB70A; -} - -.ui.items > .item > .content .active.favorite.icon { - color: #FFE623; -} - -/*-----Like----- */ - -/* Icon */ - -.ui.items > .item > .content .like.icon { - cursor: pointer; - opacity: 0.75; - transition: color 0.1s ease; -} - -.ui.items > .item > .content .like.icon:hover { - opacity: 1; - color: #FF2733; -} - -.ui.items > .item > .content .active.like.icon { - color: #FF2733; -} - -/*---------------- - Extra Content ------------------*/ - -.ui.items > .item .extra { - display: block; - position: relative; - background: none; - margin: 0.5rem 0 0; - width: 100%; - padding: 0 0 0; - top: 0; - left: 0; - color: rgba(0, 0, 0, 0.4); - box-shadow: none; - transition: color 0.1s ease; - border-top: none; -} - -.ui.items > .item .extra > * { - margin: 0.25rem 0.5rem 0.25rem 0; -} - -.ui.items > .item .extra > [class*="right floated"] { - margin: 0.25rem 0 0.25rem 0.5rem; -} - -.ui.items > .item .extra:after { - display: block; - content: ' '; - height: 0; - clear: both; - overflow: hidden; - visibility: hidden; -} - -/******************************* - Responsive -*******************************/ - -/* Default Image Width */ - -.ui.items > .item > .image:not(.ui) { - width: 175px; -} - -/* Tablet Only */ - -@media only screen and (min-width: 768px) and (max-width: 991.98px) { - .ui.items > .item { - margin: 1em 0; - } - - .ui.items > .item > .image:not(.ui) { - width: 150px; - } - - .ui.items > .item > .image + .content { - display: block; - padding: 0 0 0 1em; - } -} - -/* Mobile Only */ - -@media only screen and (max-width: 767.98px) { - .ui.items:not(.unstackable) > .item { - flex-direction: column; - margin: 2em 0; - } - - .ui.items:not(.unstackable) > .item > .image { - display: block; - margin-left: auto; - margin-right: auto; - } - - .ui.items:not(.unstackable) > .item > .image, - .ui.items:not(.unstackable) > .item > .image > img { - max-width: 100% !important; - width: auto !important; - max-height: 250px !important; - } - - .ui.items:not(.unstackable) > .item > .image + .content { - display: block; - padding: 1.5em 0 0; - } -} - -/******************************* - Variations -*******************************/ - -/*------------------- - Aligned - --------------------*/ - -.ui.items > .item > .image + [class*="top aligned"].content { - align-self: flex-start; -} - -.ui.items > .item > .image + [class*="middle aligned"].content { - align-self: center; -} - -.ui.items > .item > .image + [class*="bottom aligned"].content { - align-self: flex-end; -} - -/*-------------- - Relaxed - ---------------*/ - -.ui.relaxed.items > .item { - margin: 1.5em 0; -} - -.ui[class*="very relaxed"].items > .item { - margin: 2em 0; -} - -/*------------------- - Divided - --------------------*/ - -.ui.divided.items > .item { - border-top: 1px solid rgba(34, 36, 38, 0.15); - margin: 0; - padding: 1em 0; -} - -.ui.divided.items > .item:first-child { - border-top: none; - margin-top: 0 !important; - padding-top: 0 !important; -} - -.ui.divided.items > .item:last-child { - margin-bottom: 0 !important; - padding-bottom: 0 !important; -} - -/* Relaxed Divided */ - -.ui.relaxed.divided.items > .item { - margin: 0; - padding: 1.5em 0; -} - -.ui[class*="very relaxed"].divided.items > .item { - margin: 0; - padding: 2em 0; -} - -/*------------------- - Link - --------------------*/ - -.ui.items a.item:hover, -.ui.link.items > .item:hover { - cursor: pointer; -} - -.ui.items a.item:hover .content .header, -.ui.link.items > .item:hover .content .header { - color: #1e70bf; -} - -/*-------------- - Size ----------------*/ - -.ui.items > .item { - font-size: 1em; -} - -.ui.mini.items > .item { - font-size: 0.78571429em; -} - -.ui.tiny.items > .item { - font-size: 0.85714286em; -} - -.ui.small.items > .item { - font-size: 0.92857143em; -} - -.ui.large.items > .item { - font-size: 1.14285714em; -} - -.ui.big.items > .item { - font-size: 1.28571429em; -} - -.ui.huge.items > .item { - font-size: 1.42857143em; -} - -.ui.massive.items > .item { - font-size: 1.71428571em; -} - -/*--------------- - Unstackable - ----------------*/ - -@media only screen and (max-width: 767.98px) { - .ui.unstackable.items > .item > .image, - .ui.unstackable.items > .item > .image > img { - width: 125px !important; - } -} - -/******************************* - Theme Overrides -*******************************/ - -/******************************* - User Variable Overrides -*******************************/ /*! * # Fomantic-UI - Label * http://github.com/fomantic/Fomantic-UI/ diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json index 79aec9493f..eeb67f6097 100644 --- a/web_src/fomantic/semantic.json +++ b/web_src/fomantic/semantic.json @@ -31,7 +31,6 @@ "grid", "header", "input", - "item", "label", "list", "menu", From ad3cbbc3b1ce80c22d439848c41a926280521344 Mon Sep 17 00:00:00 2001 From: Eng Zer Jun Date: Tue, 29 Aug 2023 19:03:43 +0800 Subject: [PATCH 44/60] Remove redundant nil check in `WalkGitLog` (#26773) From the Go specification: > "1. For a nil slice, the number of iterations is 0." https://go.dev/ref/spec#For_range Therefore, an additional nil check for before the loop is unnecessary. Signed-off-by: Eng Zer Jun --- modules/git/log_name_status.go | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go index 70f6ef9dbb..7519e32b90 100644 --- a/modules/git/log_name_status.go +++ b/modules/git/log_name_status.go @@ -374,27 +374,25 @@ heaploop: break heaploop } parentRemaining.Remove(current.CommitID) - if current.Paths != nil { - for i, found := range current.Paths { - if !found { - continue + for i, found := range current.Paths { + if !found { + continue + } + changed[i] = false + if results[i] == "" { + results[i] = current.CommitID + if err := repo.LastCommitCache.Put(headRef, path.Join(treepath, paths[i]), current.CommitID); err != nil { + return nil, err } - changed[i] = false - if results[i] == "" { - results[i] = current.CommitID - if err := repo.LastCommitCache.Put(headRef, path.Join(treepath, paths[i]), current.CommitID); err != nil { + delete(path2idx, paths[i]) + remaining-- + if results[0] == "" { + results[0] = current.CommitID + if err := repo.LastCommitCache.Put(headRef, treepath, current.CommitID); err != nil { return nil, err } - delete(path2idx, paths[i]) + delete(path2idx, "") remaining-- - if results[0] == "" { - results[0] = current.CommitID - if err := repo.LastCommitCache.Put(headRef, treepath, current.CommitID); err != nil { - return nil, err - } - delete(path2idx, "") - remaining-- - } } } } From 008f5d8cf1c40b96f43b19ecf48240fc4b86e3f0 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 29 Aug 2023 21:15:19 +0900 Subject: [PATCH 45/60] Add default label in branch select list (#26697) --- options/locale/locale_en-US.ini | 1 + templates/repo/branch_dropdown.tmpl | 1 + web_src/js/components/RepoBranchTagSelector.vue | 3 +++ 3 files changed, 5 insertions(+) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 31d47ad6af..f8e068fe19 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -970,6 +970,7 @@ trust_model_helper_collaborator_committer = Collaborator+Committer: Trust signat trust_model_helper_default = Default: Use the default trust model for this installation create_repo = Create Repository default_branch = Default Branch +default_branch_label = default default_branch_helper = The default branch is the base branch for pull requests and code commits. mirror_prune = Prune mirror_prune_desc = Remove obsolete remote-tracking references diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl index a011111d5b..79eff1c53a 100644 --- a/templates/repo/branch_dropdown.tmpl +++ b/templates/repo/branch_dropdown.tmpl @@ -30,6 +30,7 @@ 'textCreateBranchFrom': {{.root.locale.Tr "repo.branch.create_from"}}, 'textBranches': {{.root.locale.Tr "repo.branches"}}, 'textTags': {{.root.locale.Tr "repo.tags"}}, + 'textDefaultBranchLabel': {{.root.locale.Tr "repo.default_branch_label"}}, 'mode': '{{if or .root.IsViewTag .isTag}}tags{{else}}branches{{end}}', 'showBranchesInDropdown': {{$showBranchesInDropdown}}, diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue index 07fbd634fd..1889bfb222 100644 --- a/web_src/js/components/RepoBranchTagSelector.vue +++ b/web_src/js/components/RepoBranchTagSelector.vue @@ -30,6 +30,9 @@
{{ item.name }} +
+ {{ textDefaultBranchLabel }} +
From 915cabdb14450ea5dcfc74ec55bb22d8cb0f29b1 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 29 Aug 2023 21:46:52 +0900 Subject: [PATCH 46/60] Fix context filter has no effect in dashboard (#26695) Fix #26686 --- routers/web/user/home.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/web/user/home.go b/routers/web/user/home.go index d1a4877e6d..c44e5a50af 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -157,7 +157,7 @@ func Milestones(ctx *context.Context) { } repoOpts := repo_model.SearchRepoOptions{ - Actor: ctxUser, + Actor: ctx.Doer, OwnerID: ctxUser.ID, Private: true, AllPublic: false, // Include also all public repositories of users and public organisations @@ -449,7 +449,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { // - Team has read permission to repository. repoOpts := &repo_model.SearchRepoOptions{ Actor: ctx.Doer, - OwnerID: ctx.Doer.ID, + OwnerID: ctxUser.ID, Private: true, AllPublic: false, AllLimited: false, From 96ba747ff21ddffdbab0cc7b37e78381861bc9e4 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 29 Aug 2023 22:03:34 +0800 Subject: [PATCH 47/60] Fix notification circle (border-radius) (#26794) `border-radius` means `radius`, not `diameter`, so it should be `50%` and `boxHeight / 2` --- web_src/css/base.css | 2 +- web_src/css/modules/navbar.css | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web_src/css/base.css b/web_src/css/base.css index 1a88823cb1..c0176cc437 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -18,7 +18,7 @@ /* non-color variables */ --border-radius: 4px; --border-radius-medium: 6px; - --border-radius-circle: 100%; + --border-radius-circle: 50%; --opacity-disabled: 0.55; --height-loading: 16rem; --tab-size: 4; diff --git a/web_src/css/modules/navbar.css b/web_src/css/modules/navbar.css index 15114f90b2..de25ea6811 100644 --- a/web_src/css/modules/navbar.css +++ b/web_src/css/modules/navbar.css @@ -133,8 +133,8 @@ left: 6px; top: -9px; min-width: 17px; - min-height: 17px; - border-radius: var(--border-radius-circle); + height: 17px; + border-radius: 11px; /* (height + 2 * borderThickness) / 2 */ display: flex; align-items: center; justify-content: center; From 3507cad6f5d164509939754f15beef6de280e743 Mon Sep 17 00:00:00 2001 From: Chongyi Zheng Date: Tue, 29 Aug 2023 10:25:24 -0400 Subject: [PATCH 48/60] Use Go 1.21 for golangci-lint (#26786) Co-authored-by: Giteabot --- .golangci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 7c35bdd2a8..069dc13c99 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -29,7 +29,7 @@ linters: fast: false run: - go: "1.20" + go: "1.21" timeout: 10m skip-dirs: - node_modules @@ -75,7 +75,7 @@ linters-settings: - name: modifies-value-receiver gofumpt: extra-rules: true - lang-version: "1.20" + lang-version: "1.21" depguard: rules: main: From ad43486cd3ab65b5b023afb5ad5b62aa862d1345 Mon Sep 17 00:00:00 2001 From: Chongyi Zheng Date: Tue, 29 Aug 2023 11:47:26 -0400 Subject: [PATCH 49/60] Fix some slice append usages (#26778) Co-authored-by: delvh --- modules/setting/service.go | 2 +- routers/api/v1/repo/pull.go | 16 +++++++--------- services/repository/files/temp_repo.go | 6 +++--- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/modules/setting/service.go b/modules/setting/service.go index 595ea6528f..74a7e90f7c 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -223,7 +223,7 @@ func loadServiceFrom(rootCfg ConfigProvider) { Service.UserDeleteWithCommentsMaxTime = sec.Key("USER_DELETE_WITH_COMMENTS_MAX_TIME").MustDuration(0) sec.Key("VALID_SITE_URL_SCHEMES").MustString("http,https") Service.ValidSiteURLSchemes = sec.Key("VALID_SITE_URL_SCHEMES").Strings(",") - schemes := make([]string, len(Service.ValidSiteURLSchemes)) + schemes := make([]string, 0, len(Service.ValidSiteURLSchemes)) for _, scheme := range Service.ValidSiteURLSchemes { scheme = strings.ToLower(strings.TrimSpace(scheme)) if scheme != "" { diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index a507c1f44d..58f2fc69ce 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -326,11 +326,9 @@ func CreatePullRequest(ctx *context.APIContext) { return } - labelIDs = make([]int64, len(form.Labels)) - orgLabelIDs := make([]int64, len(form.Labels)) - - for i := range labels { - labelIDs[i] = labels[i].ID + labelIDs = make([]int64, 0, len(labels)) + for _, label := range labels { + labelIDs = append(labelIDs, label.ID) } if ctx.Repo.Owner.IsOrganization() { @@ -340,12 +338,12 @@ func CreatePullRequest(ctx *context.APIContext) { return } - for i := range orgLabels { - orgLabelIDs[i] = orgLabels[i].ID + orgLabelIDs := make([]int64, 0, len(orgLabels)) + for _, orgLabel := range orgLabels { + orgLabelIDs = append(orgLabelIDs, orgLabel.ID) } + labelIDs = append(labelIDs, orgLabelIDs...) } - - labelIDs = append(labelIDs, orgLabelIDs...) } if form.Milestone > 0 { diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index a086d15a4f..7f6b8137ae 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -114,12 +114,12 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro return nil, err } - filelist := make([]string, len(filenames)) + fileList := make([]string, 0, len(filenames)) for _, line := range bytes.Split(stdOut.Bytes(), []byte{'\000'}) { - filelist = append(filelist, string(line)) + fileList = append(fileList, string(line)) } - return filelist, nil + return fileList, nil } // RemoveFilesFromIndex removes the given files from the index From 438c7642c79c44576302f9503cf14d39b3414956 Mon Sep 17 00:00:00 2001 From: puni9869 <80308335+puni9869@users.noreply.github.com> Date: Tue, 29 Aug 2023 23:59:13 +0530 Subject: [PATCH 50/60] Updating the js libraries to latest version. (#26795) As title. image --- package-lock.json | 133 +++++++++++++++++++++++----------------------- package.json | 18 +++---- 2 files changed, 75 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index e56a49efae..8dcccfddfb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,21 +17,21 @@ "@primer/octicons": "19.6.0", "@webcomponents/custom-elements": "1.6.0", "add-asset-webpack-plugin": "2.0.1", - "ansi_up": "6.0.0", + "ansi_up": "6.0.2", "asciinema-player": "3.5.0", "clippie": "4.0.6", "css-loader": "6.8.1", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", - "esbuild-loader": "4.0.0", + "esbuild-loader": "4.0.2", "escape-goat": "4.0.0", "fast-glob": "3.3.1", - "jquery": "3.7.0", + "jquery": "3.7.1", "jquery.are-you-sure": "1.9.0", "katex": "0.16.8", "license-checker-webpack-plugin": "0.2.1", "lightningcss-loader": "2.1.0", - "mermaid": "10.3.1", + "mermaid": "10.4.0", "mini-css-extract-plugin": "2.7.6", "minimatch": "9.0.3", "monaco-editor": "0.41.0", @@ -58,16 +58,16 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", "@playwright/test": "1.37.1", "@stoplight/spectral-cli": "6.10.1", - "@vitejs/plugin-vue": "4.3.1", - "eslint": "8.47.0", + "@vitejs/plugin-vue": "4.3.4", + "eslint": "8.48.0", "eslint-plugin-array-func": "3.1.8", "eslint-plugin-custom-elements": "0.0.8", - "eslint-plugin-import": "2.28.0", + "eslint-plugin-import": "2.28.1", "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", "eslint-plugin-regexp": "1.15.0", - "eslint-plugin-sonarjs": "0.20.0", + "eslint-plugin-sonarjs": "0.21.0", "eslint-plugin-unicorn": "48.0.1", "eslint-plugin-vue": "9.17.0", "eslint-plugin-vue-scoped-css": "2.5.0", @@ -80,7 +80,7 @@ "stylelint-declaration-strict-value": "1.9.2", "stylelint-stylistic": "0.4.3", "svgo": "3.0.2", - "updates": "14.3.5", + "updates": "14.4.0", "vite-string-plugin": "1.1.2", "vitest": "0.34.2" }, @@ -992,9 +992,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1146,9 +1146,9 @@ } }, "node_modules/@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -2030,9 +2030,9 @@ "dev": true }, "node_modules/@vitejs/plugin-vue": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.1.tgz", - "integrity": "sha512-tUBEtWcF7wFtII7ayNiLNDTCE1X1afySEo+XNVMNkFXaThENyCowIEX095QqbJZGTgoOcSVDJGlnde2NG4jtbQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz", + "integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==", "dev": true, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -2112,9 +2112,9 @@ } }, "node_modules/@vitest/snapshot/node_modules/magic-string": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", - "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", + "version": "0.30.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz", + "integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -2607,9 +2607,9 @@ } }, "node_modules/ansi_up": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.0.tgz", - "integrity": "sha512-3lnYPwXYbCSXQw52OWemps5mgfYuNsX3R5fgkHXoUDpRe8Ex/ivir1AdQLUWkdpQLyQZS/v3ofY8JGQDORwuLQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.2.tgz", + "integrity": "sha512-3G3vKvl1ilEp7J1u6BmULpMA0xVoW/f4Ekqhl8RTrJrhEBkonKn5k3bUc5Xt+qDayA6iDX0jyUh3AbZjB/l0tw==", "engines": { "node": "*" } @@ -3088,9 +3088,9 @@ ] }, "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", + "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", @@ -4222,9 +4222,9 @@ } }, "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -4596,12 +4596,12 @@ } }, "node_modules/esbuild-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.0.0.tgz", - "integrity": "sha512-J7TJWyHV2YHmflZaXLZ0Vf4wYmixDyGTw26bt4Ok+XOqSyYA4VWAVt2zJGqIfCA7TwZRDKN8hvus4akN2yAbmA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.0.2.tgz", + "integrity": "sha512-kj88m0yrtTEJDeUEF+3TZsq7t9VPzQQj7UmXAzUbIaipoYSrd0UxKAcg4l9CBgP8uVoploiw+nKr8DIv6Y9gXw==", "dependencies": { "esbuild": "^0.19.0", - "get-tsconfig": "^4.6.2", + "get-tsconfig": "^4.7.0", "loader-utils": "^2.0.4", "webpack-sources": "^1.4.3" }, @@ -4644,15 +4644,15 @@ } }, "node_modules/eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -4765,9 +4765,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz", - "integrity": "sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==", + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", + "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", "dev": true, "dependencies": { "array-includes": "^3.1.6", @@ -4779,13 +4779,12 @@ "eslint-import-resolver-node": "^0.3.7", "eslint-module-utils": "^2.8.0", "has": "^1.0.3", - "is-core-module": "^2.12.1", + "is-core-module": "^2.13.0", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.6", "object.groupby": "^1.0.0", "object.values": "^1.1.6", - "resolve": "^1.22.3", "semver": "^6.3.1", "tsconfig-paths": "^3.14.2" }, @@ -4904,9 +4903,9 @@ } }, "node_modules/eslint-plugin-sonarjs": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.20.0.tgz", - "integrity": "sha512-BRhZ7BY/oTr6DDaxvx58ReTg7R+J8T+Y2ZVGgShgpml25IHBTIG7EudUtHuJD1zhtMgUEt59x3VNvUQRo2LV6w==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.21.0.tgz", + "integrity": "sha512-oezUDfFT5S6j3rQheZ4DLPrbetPmMS7zHIKWGHr0CM3g5JgyZroz1FpIKa4jV83NsGpmgIeagpokWDKIJzRQmw==", "dev": true, "engines": { "node": ">=14" @@ -6463,9 +6462,9 @@ } }, "node_modules/jquery": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.0.tgz", - "integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==" + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, "node_modules/jquery.are-you-sure": { "version": "1.9.0", @@ -7417,9 +7416,9 @@ } }, "node_modules/mermaid": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.3.1.tgz", - "integrity": "sha512-hkenh7WkuRWPcob3oJtrN3W+yzrrIYuWF1OIfk/d0xGE8UWlvDhfexaHmDwwe8DKQgqMLI8DWEPwGprxkumjuw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.4.0.tgz", + "integrity": "sha512-4QCQLp79lvz7UZxow5HUX7uWTPJOaQBVExduo91tliXC7v78i6kssZOPHxLL+Xs30KU72cpPn3g3imw/xm/gaw==", "dependencies": { "@braintree/sanitize-url": "^6.0.1", "@types/d3-scale": "^4.0.3", @@ -7969,15 +7968,15 @@ } }, "node_modules/mlly": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.0.tgz", - "integrity": "sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.1.tgz", + "integrity": "sha512-SCDs78Q2o09jiZiE2WziwVBEqXQ02XkGdUy45cbJf+BpYRIjArXRJ1Wbowxkb+NaM9DWvS3UC9GiO/6eqvQ/pg==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.10.0", "pathe": "^1.1.1", "pkg-types": "^1.0.3", - "ufo": "^1.1.2" + "ufo": "^1.3.0" } }, "node_modules/monaco-editor": { @@ -8797,12 +8796,12 @@ } }, "node_modules/pretty-format": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", - "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -10511,9 +10510,9 @@ "dev": true }, "node_modules/ufo": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.2.0.tgz", - "integrity": "sha512-RsPyTbqORDNDxqAdQPQBpgqhWle1VcTSou/FraClYlHf6TZnQcGslpLcAphNR+sQW4q5lLWLbOsRlh9j24baQg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.0.tgz", + "integrity": "sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==", "dev": true }, "node_modules/uint8-to-base64": { @@ -10587,9 +10586,9 @@ } }, "node_modules/updates": { - "version": "14.3.5", - "resolved": "https://registry.npmjs.org/updates/-/updates-14.3.5.tgz", - "integrity": "sha512-kx1sm2RXd9guF3lAmAaC8mpfAlG5iSPHiPkSJtEC5d/Gaa+NoxwxcuySb0c5pBFlzuGGU8ZxxQ0qRl9HLfwRRg==", + "version": "14.4.0", + "resolved": "https://registry.npmjs.org/updates/-/updates-14.4.0.tgz", + "integrity": "sha512-fAB49LEq46XlJfQmLDWHt3Yt7XpSAxj1GwO6MxgEMHlGbhyGLSNu2hPYuSzipNRhO7phJNp8UDi0kikn/RAwwQ==", "dev": true, "bin": { "updates": "bin/updates.js" diff --git a/package.json b/package.json index fd3c46c868..d3b425df0f 100644 --- a/package.json +++ b/package.json @@ -16,21 +16,21 @@ "@primer/octicons": "19.6.0", "@webcomponents/custom-elements": "1.6.0", "add-asset-webpack-plugin": "2.0.1", - "ansi_up": "6.0.0", + "ansi_up": "6.0.2", "asciinema-player": "3.5.0", "clippie": "4.0.6", "css-loader": "6.8.1", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", - "esbuild-loader": "4.0.0", + "esbuild-loader": "4.0.2", "escape-goat": "4.0.0", "fast-glob": "3.3.1", - "jquery": "3.7.0", + "jquery": "3.7.1", "jquery.are-you-sure": "1.9.0", "katex": "0.16.8", "license-checker-webpack-plugin": "0.2.1", "lightningcss-loader": "2.1.0", - "mermaid": "10.3.1", + "mermaid": "10.4.0", "mini-css-extract-plugin": "2.7.6", "minimatch": "9.0.3", "monaco-editor": "0.41.0", @@ -57,16 +57,16 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", "@playwright/test": "1.37.1", "@stoplight/spectral-cli": "6.10.1", - "@vitejs/plugin-vue": "4.3.1", - "eslint": "8.47.0", + "@vitejs/plugin-vue": "4.3.4", + "eslint": "8.48.0", "eslint-plugin-array-func": "3.1.8", "eslint-plugin-custom-elements": "0.0.8", - "eslint-plugin-import": "2.28.0", + "eslint-plugin-import": "2.28.1", "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", "eslint-plugin-regexp": "1.15.0", - "eslint-plugin-sonarjs": "0.20.0", + "eslint-plugin-sonarjs": "0.21.0", "eslint-plugin-unicorn": "48.0.1", "eslint-plugin-vue": "9.17.0", "eslint-plugin-vue-scoped-css": "2.5.0", @@ -79,7 +79,7 @@ "stylelint-declaration-strict-value": "1.9.2", "stylelint-stylistic": "0.4.3", "svgo": "3.0.2", - "updates": "14.3.5", + "updates": "14.4.0", "vite-string-plugin": "1.1.2", "vitest": "0.34.2" }, From 2d9249b6d9b57dea57b70357432bda945504c4b5 Mon Sep 17 00:00:00 2001 From: Chongyi Zheng Date: Tue, 29 Aug 2023 16:19:40 -0400 Subject: [PATCH 51/60] Replace deprecated `elliptic.Marshal` (#26800) In PR #26786, the Go version for golangci-lint is bumped to 1.21. This causes the following error: ``` models/migrations/v1_16/v210.go:132:23: SA1019: elliptic.Marshal has been deprecated since Go 1.21: for ECDH, use the crypto/ecdh package. This function returns an encoding equivalent to that of PublicKey.Bytes in crypto/ecdh. (staticcheck) PublicKey: elliptic.Marshal(elliptic.P256(), parsed.PubKey.X, parsed.PubKey.Y), ``` The change now uses [func (*PublicKey) ECDH](https://pkg.go.dev/crypto/ecdsa#PublicKey.ECDH), which is added in Go 1.20. --- models/migrations/v1_16/v210.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go index 4e55afccc1..533bb4bf80 100644 --- a/models/migrations/v1_16/v210.go +++ b/models/migrations/v1_16/v210.go @@ -4,7 +4,6 @@ package v1_16 //nolint import ( - "crypto/elliptic" "encoding/base32" "fmt" "strings" @@ -123,13 +122,17 @@ func RemigrateU2FCredentials(x *xorm.Engine) error { if err != nil { continue } + pubKey, err := parsed.PubKey.ECDH() + if err != nil { + continue + } remigrated := &webauthnCredential{ ID: reg.ID, Name: reg.Name, LowerName: strings.ToLower(reg.Name), UserID: reg.UserID, CredentialID: base32.HexEncoding.EncodeToString(parsed.KeyHandle), - PublicKey: elliptic.Marshal(elliptic.P256(), parsed.PubKey.X, parsed.PubKey.Y), + PublicKey: pubKey.Bytes(), AttestationType: "fido-u2f", AAGUID: []byte{}, SignCount: reg.Counter, From b91057b172dea07a9db1bf96a32d2ab25a0e030d Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Wed, 30 Aug 2023 04:54:49 +0800 Subject: [PATCH 52/60] feat(API): add route and implementation for creating/updating repository secret (#26766) spec: https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-a-repository-secret - Add a new route for creating or updating a secret value in a repository - Create a new file `routers/api/v1/repo/action.go` with the implementation of the `CreateOrUpdateSecret` function - Update the Swagger documentation for the `updateRepoSecret` operation in the `v1_json.tmpl` template file --------- Signed-off-by: Bo-Yi Wu Co-authored-by: Giteabot --- models/secret/secret.go | 28 +++++++++++++ routers/api/v1/api.go | 6 ++- routers/api/v1/org/action.go | 23 ++++------- routers/api/v1/repo/action.go | 75 ++++++++++++++++++++++++++++++++++ templates/swagger/v1_json.tmpl | 59 ++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 17 deletions(-) create mode 100644 routers/api/v1/repo/action.go diff --git a/models/secret/secret.go b/models/secret/secret.go index 410cb3770e..1cb816e9db 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -160,3 +160,31 @@ func DeleteSecret(ctx context.Context, orgID, repoID int64, name string) error { return nil } + +// CreateOrUpdateSecret creates or updates a secret and returns true if it was created +func CreateOrUpdateSecret(ctx context.Context, orgID, repoID int64, name, data string) (bool, error) { + sc := new(Secret) + name = strings.ToUpper(name) + has, err := db.GetEngine(ctx). + Where("owner_id=?", orgID). + And("repo_id=?", repoID). + And("name=?", name). + Get(sc) + if err != nil { + return false, err + } + + if !has { + _, err = InsertEncryptedSecret(ctx, orgID, repoID, name, data) + if err != nil { + return false, err + } + return true, nil + } + + if err := UpdateSecret(ctx, orgID, repoID, name, data); err != nil { + return false, err + } + + return false, nil +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 6424931a47..32e5a10bbe 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -933,6 +933,10 @@ func Routes() *web.Route { m.Post("/accept", repo.AcceptTransfer) m.Post("/reject", repo.RejectTransfer) }, reqToken()) + m.Group("/actions/secrets", func() { + m.Combo("/{secretname}"). + Put(reqToken(), reqOwner(), bind(api.CreateOrUpdateSecretOption{}), repo.CreateOrUpdateSecret) + }) m.Group("/hooks/git", func() { m.Combo("").Get(repo.ListGitHooks) m.Group("/{id}", func() { @@ -1301,7 +1305,7 @@ func Routes() *web.Route { m.Group("/actions/secrets", func() { m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets) m.Combo("/{secretname}"). - Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateOrgSecret). + Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateSecret). Delete(reqToken(), reqOrgOwnership(), org.DeleteOrgSecret) }) m.Group("/public_members", func() { diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index ee18cca26d..0bf741e825 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -74,7 +74,7 @@ func listActionsSecrets(ctx *context.APIContext) { } // create or update one secret of the organization -func CreateOrUpdateOrgSecret(ctx *context.APIContext) { +func CreateOrUpdateSecret(ctx *context.APIContext) { // swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret // --- // summary: Create or Update a secret value in an organization @@ -108,26 +108,17 @@ func CreateOrUpdateOrgSecret(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" secretName := ctx.Params(":secretname") if err := actions.NameRegexMatch(secretName); err != nil { - ctx.Error(http.StatusBadRequest, "CreateOrUpdateOrgSecret", err) + ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) return } opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) - err := secret_model.UpdateSecret( - ctx, ctx.Org.Organization.ID, 0, secretName, opt.Data, - ) - if secret_model.IsErrSecretNotFound(err) { - _, err := secret_model.InsertEncryptedSecret( - ctx, ctx.Org.Organization.ID, 0, secretName, actions.ReserveLineBreakForTextarea(opt.Data), - ) - if err != nil { - ctx.Error(http.StatusInternalServerError, "InsertEncryptedSecret", err) - return - } - ctx.Status(http.StatusCreated) + isCreated, err := secret_model.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, secretName, opt.Data) + if err != nil { + ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) return } - if err != nil { - ctx.Error(http.StatusInternalServerError, "UpdateSecret", err) + if isCreated { + ctx.Status(http.StatusCreated) return } diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go new file mode 100644 index 0000000000..015c731a75 --- /dev/null +++ b/routers/api/v1/repo/action.go @@ -0,0 +1,75 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "net/http" + + secret_model "code.gitea.io/gitea/models/secret" + "code.gitea.io/gitea/modules/context" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/web/shared/actions" +) + +// create or update one secret of the repository +func CreateOrUpdateSecret(ctx *context.APIContext) { + // swagger:operation PUT /repos/{owner}/{repo}/actions/secrets/{secretname} repository updateRepoSecret + // --- + // summary: Create or Update a secret value in a repository + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repository + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repository + // type: string + // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/CreateOrUpdateSecretOption" + // responses: + // "201": + // description: response when creating a secret + // "204": + // description: response when updating a secret + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + + owner := ctx.Repo.Owner + repo := ctx.Repo.Repository + + secretName := ctx.Params(":secretname") + if err := actions.NameRegexMatch(secretName); err != nil { + ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) + return + } + opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) + isCreated, err := secret_model.CreateOrUpdateSecret(ctx, owner.ID, repo.ID, secretName, opt.Data) + if err != nil { + ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) + return + } + if isCreated { + ctx.Status(http.StatusCreated) + return + } + + ctx.Status(http.StatusNoContent) +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index d37f4463f5..78491de2e1 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -3230,6 +3230,65 @@ } } }, + "/repos/{owner}/{repo}/actions/secrets/{secretname}": { + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Create or Update a secret value in a repository", + "operationId": "updateRepoSecret", + "parameters": [ + { + "type": "string", + "description": "owner of the repository", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repository", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the secret", + "name": "secretname", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/CreateOrUpdateSecretOption" + } + } + ], + "responses": { + "201": { + "description": "response when creating a secret" + }, + "204": { + "description": "response when updating a secret" + }, + "400": { + "$ref": "#/responses/error" + }, + "403": { + "$ref": "#/responses/forbidden" + } + } + } + }, "/repos/{owner}/{repo}/activities/feeds": { "get": { "produces": [ From 4f5a2117c37f1bf89d1686f407dff600a8783a97 Mon Sep 17 00:00:00 2001 From: js6pak Date: Wed, 30 Aug 2023 00:13:16 +0200 Subject: [PATCH 53/60] Include the GITHUB_TOKEN/GITEA_TOKEN secret for fork pull requests (#26759) Include `GITHUB_TOKEN`/`GITEA_TOKEN` secrets for actions triggered by pull requests This makes it consistent with the environment variables which you can already access ```shell echo env: $GITHUB_TOKEN echo expression: ${{ secrets.GITHUB_TOKEN }} ``` before ![image](https://github.com/go-gitea/gitea/assets/35262707/b6f750f6-3995-40f0-b8aa-df01e7997c37) after ![image](https://github.com/go-gitea/gitea/assets/35262707/ab74464b-7638-458a-afd5-f39e6101d2cf) --------- Co-authored-by: Jason Song Co-authored-by: Giteabot --- routers/api/actions/runner/utils.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go index e95df7a00f..b8c7ca842a 100644 --- a/routers/api/actions/runner/utils.go +++ b/routers/api/actions/runner/utils.go @@ -55,8 +55,12 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string { secrets := map[string]string{} + + secrets["GITHUB_TOKEN"] = task.Token + secrets["GITEA_TOKEN"] = task.Token + if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget { - // ignore secrets for fork pull request + // ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_TOKEN which are automatically generated. // for the tasks triggered by pull_request_target event, they could access the secrets because they will run in the context of the base branch // see the documentation: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target return secrets @@ -82,13 +86,6 @@ func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[s } } - if _, ok := secrets["GITHUB_TOKEN"]; !ok { - secrets["GITHUB_TOKEN"] = task.Token - } - if _, ok := secrets["GITEA_TOKEN"]; !ok { - secrets["GITEA_TOKEN"] = task.Token - } - return secrets } From 1a9998ce916c69a50e9141a86ed8105dabea80ef Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 30 Aug 2023 07:13:30 +0800 Subject: [PATCH 54/60] Improve flex list item padding (#26779) Replace #26761 It's better to keep children elements simple, and let parent containers layout the necessary padding/margin. The old `not(:last-child)` and `.flex-item + .flex-item` are not easy to maintain (for example, what if the developer would like to use a "tiny height" item?) The old approach also makes some UI look strange because the first item doesn't have proper padding-top. In this PR, we just simply use `.flex-item { padding: ... }`: * Developers could manually set the item height they want easily * It's easier to make it work with various containers -- with padding (`ui segment`) and without padding (`div`) And added more samples/examples. ![image](https://github.com/go-gitea/gitea/assets/2114189/719ea712-0241-4426-b67f-5723993c4ed7) Co-authored-by: Giteabot --- templates/devtest/flex-list.tmpl | 23 ++++++++++++++++++++--- web_src/css/shared/flex-list.css | 15 ++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/templates/devtest/flex-list.tmpl b/templates/devtest/flex-list.tmpl index f9087a5714..cbc2632139 100644 --- a/templates/devtest/flex-list.tmpl +++ b/templates/devtest/flex-list.tmpl @@ -1,8 +1,8 @@ {{template "base/head" .}} -
-
-

Flex List

+
+
+

Flex List (standalone)

@@ -84,6 +84,23 @@
+ +
+ +

Flex List (with "ui segment")

+
+
+
item 1
+
item 2
+
+
+
+

Flex List (with "ui segment")

+
+
item 1
+
item 2
+
+
{{template "base/footer" .}} diff --git a/web_src/css/shared/flex-list.css b/web_src/css/shared/flex-list.css index c73f78ebfe..1489983cfd 100644 --- a/web_src/css/shared/flex-list.css +++ b/web_src/css/shared/flex-list.css @@ -6,10 +6,7 @@ display: flex; gap: 8px; align-items: flex-start; -} - -.flex-item:not(:last-child) { - padding-bottom: 8px; + padding: 1em 0; } .flex-item-baseline { @@ -92,5 +89,13 @@ .flex-list > .flex-item + .flex-item { border-top: 1px solid var(--color-secondary); - padding-top: 8px; +} + +/* Fomantic UI segment has default "padding: 1em", so here it removes the padding-top and padding-bottom accordingly */ +.ui.segment > .flex-list:first-child > .flex-item:first-child { + padding-top: 0; +} + +.ui.segment > .flex-list:last-child > .flex-item:last-child { + padding-bottom: 0; } From 7bc80cb35012d5c3c6a5105ec8cd01b70de86ef7 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 30 Aug 2023 03:40:13 +0200 Subject: [PATCH 55/60] Add various missing files-changed dependencies (#26799) We were missing a number of config files like `.golangci.yml` in the dependencies for the pull request pipelines, which resulted in the linting not running for https://github.com/go-gitea/gitea/pull/26786 because only `.golangci.yml` had changed. --- .github/workflows/files-changed.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/files-changed.yml b/.github/workflows/files-changed.yml index f9156d668d..b9277d3109 100644 --- a/.github/workflows/files-changed.yml +++ b/.github/workflows/files-changed.yml @@ -43,6 +43,8 @@ jobs: - "go.mod" - "go.sum" - "Makefile" + - ".golangci.yml" + - ".editorconfig" frontend: - "**/*.js" @@ -51,16 +53,21 @@ jobs: - "package.json" - "package-lock.json" - "Makefile" + - ".eslintrc.yaml" + - ".stylelintrc.yaml" + - ".npmrc" docs: - "**/*.md" - "docs/**" + - ".markdownlint.yaml" actions: - ".github/workflows/*" templates: - "templates/**/*.tmpl" + - "pyproject.toml" - "poetry.lock" docker: @@ -72,3 +79,6 @@ jobs: swagger: - "templates/swagger/v1_json.tmpl" - "Makefile" + - "package.json" + - "package-lock.json" + - ".spectral.yml" From 508de3a58d79009992df0e5f0cf1acedbac500bb Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 30 Aug 2023 03:56:44 +0200 Subject: [PATCH 56/60] Fix Uint8Array comparisons and update vitest (#26805) Compare those `Uint8Array` via conversion to Array which are properly comparable, so that we don't have to worry about whether `TextEncoder` and `UInt8Array` from the environment are compatible or not. --------- Co-authored-by: delvh --- package-lock.json | 62 ++++++++++++++++++++-------------------- package.json | 2 +- web_src/js/utils.test.js | 19 +++++++----- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8dcccfddfb..dda23bcbd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,7 +82,7 @@ "svgo": "3.0.2", "updates": "14.4.0", "vite-string-plugin": "1.1.2", - "vitest": "0.34.2" + "vitest": "0.34.3" }, "engines": { "node": ">= 16.0.0" @@ -2043,13 +2043,13 @@ } }, "node_modules/@vitest/expect": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.2.tgz", - "integrity": "sha512-EZm2dMNlLyIfDMha17QHSQcg2KjeAZaXd65fpPzXY5bvnfx10Lcaz3N55uEe8PhF+w4pw+hmrlHLLlRn9vkBJg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.3.tgz", + "integrity": "sha512-F8MTXZUYRBVsYL1uoIft1HHWhwDbSzwAU9Zgh8S6WFC3YgVb4AnFV2GXO3P5Em8FjEYaZtTnQYoNwwBrlOMXgg==", "dev": true, "dependencies": { - "@vitest/spy": "0.34.2", - "@vitest/utils": "0.34.2", + "@vitest/spy": "0.34.3", + "@vitest/utils": "0.34.3", "chai": "^4.3.7" }, "funding": { @@ -2057,12 +2057,12 @@ } }, "node_modules/@vitest/runner": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.2.tgz", - "integrity": "sha512-8ydGPACVX5tK3Dl0SUwxfdg02h+togDNeQX3iXVFYgzF5odxvaou7HnquALFZkyVuYskoaHUOqOyOLpOEj5XTA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.3.tgz", + "integrity": "sha512-lYNq7N3vR57VMKMPLVvmJoiN4bqwzZ1euTW+XXYH5kzr3W/+xQG3b41xJn9ChJ3AhYOSoweu974S1V3qDcFESA==", "dev": true, "dependencies": { - "@vitest/utils": "0.34.2", + "@vitest/utils": "0.34.3", "p-limit": "^4.0.0", "pathe": "^1.1.1" }, @@ -2098,9 +2098,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.2.tgz", - "integrity": "sha512-qhQ+xy3u4mwwLxltS4Pd4SR+XHv4EajiTPNY3jkIBLUApE6/ce72neJPSUQZ7bL3EBuKI+NhvzhGj3n5baRQUQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.3.tgz", + "integrity": "sha512-QyPaE15DQwbnIBp/yNJ8lbvXTZxS00kRly0kfFgAD5EYmCbYcA+1EEyRalc93M0gosL/xHeg3lKAClIXYpmUiQ==", "dev": true, "dependencies": { "magic-string": "^0.30.1", @@ -2124,9 +2124,9 @@ } }, "node_modules/@vitest/spy": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.2.tgz", - "integrity": "sha512-yd4L9OhfH6l0Av7iK3sPb3MykhtcRN5c5K5vm1nTbuN7gYn+yvUVVsyvzpHrjqS7EWqn9WsPJb7+0c3iuY60tA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.3.tgz", + "integrity": "sha512-N1V0RFQ6AI7CPgzBq9kzjRdPIgThC340DGjdKdPSE8r86aUSmeliTUgkTqLSgtEwWWsGfBQ+UetZWhK0BgJmkQ==", "dev": true, "dependencies": { "tinyspy": "^2.1.1" @@ -2136,9 +2136,9 @@ } }, "node_modules/@vitest/utils": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.2.tgz", - "integrity": "sha512-Lzw+kAsTPubhoQDp1uVAOP6DhNia1GMDsI9jgB0yMn+/nDaPieYQ88lKqz/gGjSHL4zwOItvpehec9OY+rS73w==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.3.tgz", + "integrity": "sha512-kiSnzLG6m/tiT0XEl4U2H8JDBjFtwVlaE8I3QfGiMFR0QvnRDfYfdP3YvTBWM/6iJDAyaPY6yVQiCTUc7ZzTHA==", "dev": true, "dependencies": { "diff-sequences": "^29.4.3", @@ -10735,9 +10735,9 @@ } }, "node_modules/vite-node": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.2.tgz", - "integrity": "sha512-JtW249Zm3FB+F7pQfH56uWSdlltCo1IOkZW5oHBzeQo0iX4jtC7o1t9aILMGd9kVekXBP2lfJBEQt9rBh07ebA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.3.tgz", + "integrity": "sha512-+0TzJf1g0tYXj6tR2vEyiA42OPq68QkRZCu/ERSo2PtsDJfBpDyEfuKbRvLmZqi/CgC7SCBtyC+WjTGNMRIaig==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -11172,19 +11172,19 @@ } }, "node_modules/vitest": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.2.tgz", - "integrity": "sha512-WgaIvBbjsSYMq/oiMlXUI7KflELmzM43BEvkdC/8b5CAod4ryAiY2z8uR6Crbi5Pjnu5oOmhKa9sy7uk6paBxQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.3.tgz", + "integrity": "sha512-7+VA5Iw4S3USYk+qwPxHl8plCMhA5rtfwMjgoQXMT7rO5ldWcdsdo3U1QD289JgglGK4WeOzgoLTsGFu6VISyQ==", "dev": true, "dependencies": { "@types/chai": "^4.3.5", "@types/chai-subset": "^1.3.3", "@types/node": "*", - "@vitest/expect": "0.34.2", - "@vitest/runner": "0.34.2", - "@vitest/snapshot": "0.34.2", - "@vitest/spy": "0.34.2", - "@vitest/utils": "0.34.2", + "@vitest/expect": "0.34.3", + "@vitest/runner": "0.34.3", + "@vitest/snapshot": "0.34.3", + "@vitest/spy": "0.34.3", + "@vitest/utils": "0.34.3", "acorn": "^8.9.0", "acorn-walk": "^8.2.0", "cac": "^6.7.14", @@ -11199,7 +11199,7 @@ "tinybench": "^2.5.0", "tinypool": "^0.7.0", "vite": "^3.0.0 || ^4.0.0", - "vite-node": "0.34.2", + "vite-node": "0.34.3", "why-is-node-running": "^2.2.2" }, "bin": { diff --git a/package.json b/package.json index d3b425df0f..e4f1743feb 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "svgo": "3.0.2", "updates": "14.4.0", "vite-string-plugin": "1.1.2", - "vitest": "0.34.2" + "vitest": "0.34.3" }, "browserslist": [ "defaults", diff --git a/web_src/js/utils.test.js b/web_src/js/utils.test.js index 812ef3bedf..6648b669c7 100644 --- a/web_src/js/utils.test.js +++ b/web_src/js/utils.test.js @@ -133,17 +133,22 @@ test('toAbsoluteUrl', () => { expect(() => toAbsoluteUrl('path')).toThrowError('unsupported'); }); -const uint8array = (s) => new TextEncoder().encode(s); test('encodeURLEncodedBase64, decodeURLEncodedBase64', () => { + // TextEncoder is Node.js API while Uint8Array is jsdom API and their outputs are not + // structurally comparable, so we convert to array to compare. The conversion can be + // removed once https://github.com/jsdom/jsdom/issues/2524 is resolved. + const encoder = new TextEncoder(); + const uint8array = encoder.encode.bind(encoder); + expect(encodeURLEncodedBase64(uint8array('AA?'))).toEqual('QUE_'); // standard base64: "QUE/" expect(encodeURLEncodedBase64(uint8array('AA~'))).toEqual('QUF-'); // standard base64: "QUF+" - expect(decodeURLEncodedBase64('QUE/')).toEqual(uint8array('AA?')); - expect(decodeURLEncodedBase64('QUF+')).toEqual(uint8array('AA~')); - expect(decodeURLEncodedBase64('QUE_')).toEqual(uint8array('AA?')); - expect(decodeURLEncodedBase64('QUF-')).toEqual(uint8array('AA~')); + expect(Array.from(decodeURLEncodedBase64('QUE/'))).toEqual(Array.from(uint8array('AA?'))); + expect(Array.from(decodeURLEncodedBase64('QUF+'))).toEqual(Array.from(uint8array('AA~'))); + expect(Array.from(decodeURLEncodedBase64('QUE_'))).toEqual(Array.from(uint8array('AA?'))); + expect(Array.from(decodeURLEncodedBase64('QUF-'))).toEqual(Array.from(uint8array('AA~'))); expect(encodeURLEncodedBase64(uint8array('a'))).toEqual('YQ'); // standard base64: "YQ==" - expect(decodeURLEncodedBase64('YQ')).toEqual(uint8array('a')); - expect(decodeURLEncodedBase64('YQ==')).toEqual(uint8array('a')); + expect(Array.from(decodeURLEncodedBase64('YQ'))).toEqual(Array.from(uint8array('a'))); + expect(Array.from(decodeURLEncodedBase64('YQ=='))).toEqual(Array.from(uint8array('a'))); }); From 815d267c8031daa19b82b291a6393a54715567c0 Mon Sep 17 00:00:00 2001 From: CaiCandong <50507092+CaiCandong@users.noreply.github.com> Date: Wed, 30 Aug 2023 10:27:53 +0800 Subject: [PATCH 57/60] Fix verifyCommits error when push a new branch (#26664) > ### Description > If a new branch is pushed, and the repository has a rule that would require signed commits for the new branch, the commit is rejected with a 500 error regardless of whether it's signed. > > When pushing a new branch, the "old" commit is the empty ID (0000000000000000000000000000000000000000). verifyCommits has no provision for this and passes an invalid commit range to git rev-list. Prior to 1.19 this wasn't an issue because only pre-existing individual branches could be protected. > > I was able to reproduce with [try.gitea.io/CraigTest/test](https://try.gitea.io/CraigTest/test), which is set up with a blanket rule to require commits on all branches. Fix #25565 Very thanks to @Craig-Holmquist-NTI for reporting the bug and suggesting an valid solution! --------- Co-authored-by: wxiaoguang Co-authored-by: Lunny Xiao --- models/fixtures/email_address.yml | 10 +- models/fixtures/gpg_key.yml | 24 +++- models/fixtures/user.yml | 4 +- routers/private/hook_verification.go | 40 +++--- routers/private/hook_verification_test.go | 43 ++++++ routers/private/main_test.go | 17 +++ .../tests/repos/repo1_hook_verification/HEAD | 1 + .../repos/repo1_hook_verification/config | 6 + .../repos/repo1_hook_verification/info/refs | 1 + .../repos/repo1_hook_verification/logs/HEAD | 1 + .../logs/refs/heads/main | 1 + .../08/cbc8f0e903b0916025ae7577564b7ed39ecb2c | Bin 0 -> 86 bytes .../0b/5987362fe3fabdd4406babdc819642ee2f5a2a | Bin 0 -> 22 bytes .../13/b0f23f673b161f4b5cb66f051cb93c99729e1e | Bin 0 -> 86 bytes .../23/33a51fdb238b7023a62ae3dcc58994061a7c86 | Bin 0 -> 86 bytes .../2b/df04adb23d2b40b6085efb230856e5e2a775b7 | Bin 0 -> 55 bytes .../65/a457425a679cbe9adf0d2741785d3ceabb44a7 | Bin 0 -> 50 bytes .../72/920278f2f999e3005801e5d5b8ab8139d3641c | 2 + .../8b/903ede7c494725624bf842ec890f6342dababd | Bin 0 -> 86 bytes .../93/eac826f6188f34646cea81bf426aa5ba7d3bfe | Bin 0 -> 684 bytes .../97/79d17a04f1e2640583d35703c62460b2d86e0a | 2 + .../9c/e3f779ae33f31fce17fac3c512047b75d7498b | Bin 0 -> 153 bytes .../a9/f76e70a663e40091749a97eeac5f57a6fec141 | Bin 0 -> 24 bytes .../ba/0caedd359ebe310ef431335576e20f2b84e9b9 | Bin 0 -> 63 bytes .../bb/87653e0819460e79b5f075f2563f583cbbf18c | Bin 0 -> 682 bytes .../cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa | 3 + .../d7/66f2917716d45be24bfa968b8409544941be32 | 3 + .../e3/7e5d19823e42fad252f6341b1f77a7bc6ee451 | Bin 0 -> 59 bytes .../e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 | Bin 0 -> 15 bytes .../e8/4e452c3a20c02dee17ec2f63c0cb9ef6c759eb | Bin 0 -> 677 bytes .../objects/info/packs | 1 + .../08/cbc8f0e903b0916025ae7577564b7ed39ecb2c | Bin 0 -> 86 bytes .../0b/5987362fe3fabdd4406babdc819642ee2f5a2a | Bin 0 -> 22 bytes .../13/b0f23f673b161f4b5cb66f051cb93c99729e1e | Bin 0 -> 86 bytes .../23/33a51fdb238b7023a62ae3dcc58994061a7c86 | Bin 0 -> 86 bytes .../8b/903ede7c494725624bf842ec890f6342dababd | Bin 0 -> 86 bytes .../93/eac826f6188f34646cea81bf426aa5ba7d3bfe | Bin 0 -> 684 bytes .../9c/e3f779ae33f31fce17fac3c512047b75d7498b | Bin 0 -> 153 bytes .../a9/f76e70a663e40091749a97eeac5f57a6fec141 | Bin 0 -> 24 bytes .../bb/87653e0819460e79b5f075f2563f583cbbf18c | Bin 0 -> 682 bytes .../cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa | 3 + .../repo1_hook_verification/refs/heads/main | 1 + .../repo1_hook_verification_dummy_gpg_key.txt | 127 ++++++++++++++++++ 43 files changed, 270 insertions(+), 20 deletions(-) create mode 100644 routers/private/hook_verification_test.go create mode 100644 routers/private/main_test.go create mode 100644 routers/private/tests/repos/repo1_hook_verification/HEAD create mode 100644 routers/private/tests/repos/repo1_hook_verification/config create mode 100644 routers/private/tests/repos/repo1_hook_verification/info/refs create mode 100644 routers/private/tests/repos/repo1_hook_verification/logs/HEAD create mode 100644 routers/private/tests/repos/repo1_hook_verification/logs/refs/heads/main create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/0b/5987362fe3fabdd4406babdc819642ee2f5a2a create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/13/b0f23f673b161f4b5cb66f051cb93c99729e1e create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/23/33a51fdb238b7023a62ae3dcc58994061a7c86 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/2b/df04adb23d2b40b6085efb230856e5e2a775b7 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/72/920278f2f999e3005801e5d5b8ab8139d3641c create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/8b/903ede7c494725624bf842ec890f6342dababd create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/93/eac826f6188f34646cea81bf426aa5ba7d3bfe create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/97/79d17a04f1e2640583d35703c62460b2d86e0a create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/9c/e3f779ae33f31fce17fac3c512047b75d7498b create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/a9/f76e70a663e40091749a97eeac5f57a6fec141 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/ba/0caedd359ebe310ef431335576e20f2b84e9b9 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/bb/87653e0819460e79b5f075f2563f583cbbf18c create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/d7/66f2917716d45be24bfa968b8409544941be32 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/e3/7e5d19823e42fad252f6341b1f77a7bc6ee451 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/e8/4e452c3a20c02dee17ec2f63c0cb9ef6c759eb create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/info/packs create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/0b/5987362fe3fabdd4406babdc819642ee2f5a2a create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/13/b0f23f673b161f4b5cb66f051cb93c99729e1e create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/23/33a51fdb238b7023a62ae3dcc58994061a7c86 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/8b/903ede7c494725624bf842ec890f6342dababd create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/93/eac826f6188f34646cea81bf426aa5ba7d3bfe create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/9c/e3f779ae33f31fce17fac3c512047b75d7498b create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/a9/f76e70a663e40091749a97eeac5f57a6fec141 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/bb/87653e0819460e79b5f075f2563f583cbbf18c create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa create mode 100644 routers/private/tests/repos/repo1_hook_verification/refs/heads/main create mode 100644 routers/private/tests/repos/repo1_hook_verification_dummy_gpg_key.txt diff --git a/models/fixtures/email_address.yml b/models/fixtures/email_address.yml index e7df5fdc5f..1c32379b60 100644 --- a/models/fixtures/email_address.yml +++ b/models/fixtures/email_address.yml @@ -276,4 +276,12 @@ email: user2-2@example.com lower_email: user2-2@example.com is_activated: false - is_primary: false \ No newline at end of file + is_primary: false + +- + id: 36 + uid: 36 + email: abcde@gitea.com + lower_email: abcde@gitea.com + is_activated: true + is_primary: false diff --git a/models/fixtures/gpg_key.yml b/models/fixtures/gpg_key.yml index ca780a73aa..2d54313fdf 100644 --- a/models/fixtures/gpg_key.yml +++ b/models/fixtures/gpg_key.yml @@ -1 +1,23 @@ -[] # empty +- + id: 5 + owner_id: 36 + key_id: B15431642629B826 + primary_key_id: + content: xsDNBGTrY3UBDAC2HLBqmMplAV15qSnC7g1c4dV406f5EHNhFr95Nup2My6b2eafTlvedv77s8PT/I7F3fy4apOZs5A7w2SsPlLMcQ3ev4uGOsxRtkq5RLy1Yb6SNueX0Da2UVKR5KTC5Q6BWaqxwS0IjKOLZ/xz0Pbe/ClV3bZSKBEY2omkVo3Z0HZ771vB2clPRvGJ/IdeKOsZ3ZytSFXfyiJBdARmeSPmydXLil8+Ibq5iLAeow5PK8hK1TCOnKHzLWNqcNq70tyjoHvcGi70iGjoVEEUgPCLLuU8WmzTJwlvA3BuDzjtaO7TLo/jdE6iqkHtMSS8x+43sAH6hcFRCWAVh/0Uq7n36uGDfNxGnX3YrmX3LR9x5IsBES1rGGWbpxio4o5GIf/Xd+JgDd9rzJCqRuZ3/sW/TxK38htWaVNZV0kMkHUCTc1ctzWpCm635hbFCHBhPYIp+/z206khkAKDbz/CNuU91Wazsh7KO07wrwDtxfDDbInJ8TfHE2TGjzjQzgChfmcAEQEAAQ== + verified: true + can_sign: true + can_encrypt_comms: true + can_encrypt_storage: true + can_certify: true + +- + id: 6 + owner_id: 36 + key_id: EE3AF48454AFD619 + primary_key_id: B15431642629B826 + content: zsDNBGTrY3UBDADsHrzuOicQaPdUQm0+0UNrs92cESm/j/4yBBUk+sfLZAo6J99c4eh4nAQzzZ7al080rYKB0G+7xoRz1eHcQH6zrVcqB8KYtf/sdY47WaMiMyxM+kTSvzp7tsv7QuSQZ0neUEXRyYMz5ttBfIjWUd+3NDItuHyB+MtNWlS3zXgaUbe5VifqKaNmzN0Ye4yXTKcpypE3AOqPVz+iIFv3c6TmsqLHJaR4VoicCleAqLyF/28WsJO7M9dDW+EM3MZVnsVpycTURyHAJGfSk10waQZAaRwmarCN/q0KEJ+aEAK/SRliUneBZoMO5hY5iBeG432tofwaQqAahPv9uXIb1n2JEMKwnMlMA9UGD1AcDbywfj1m/ZGBBw95i4Ekkfn43RvV3THr7uJU/dRqqP+iic4MwpUrOxqELW/kmeHXlBcNbZZhEEvwRoW7U2/9eeuog4nRleRJ0pi/xOP9wmxkKjaIPIK3phdBtEpVk4w/UTAWNdyIIrFggukeAnZFyGJwlm8AEQEAAQ== + verified: true + can_sign: true + can_encrypt_comms: true + can_encrypt_storage: true + can_certify: true diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index c7c5c024be..f24d098a7e 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -1301,7 +1301,7 @@ lower_name: limited_org36 name: limited_org36 full_name: Limited Org 36 - email: limited_org36@example.com + email: abcde@gitea.com keep_email_private: false email_notifications_preference: enabled passwd: ZogKvWdyEx:password @@ -1320,7 +1320,7 @@ allow_create_organization: true prohibit_login: false avatar: avatar22 - avatar_email: limited_org36@example.com + avatar_email: abcde@gitea.com use_custom_avatar: false num_followers: 0 num_following: 0 diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go index caf3874ec3..8604789529 100644 --- a/routers/private/hook_verification.go +++ b/routers/private/hook_verification.go @@ -28,23 +28,31 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env [] _ = stdoutWriter.Close() }() + var command *git.Command + if oldCommitID == git.EmptySHA { + // When creating a new branch, the oldCommitID is empty, by using "newCommitID --not --all": + // List commits that are reachable by following the newCommitID, exclude "all" existing heads/tags commits + // So, it only lists the new commits received, doesn't list the commits already present in the receiving repository + command = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(newCommitID).AddArguments("--not", "--all") + } else { + command = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID) + } // This is safe as force pushes are already forbidden - err = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID). - Run(&git.RunOpts{ - Env: env, - Dir: repo.Path, - Stdout: stdoutWriter, - PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { - _ = stdoutWriter.Close() - err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env) - if err != nil { - log.Error("%v", err) - cancel() - } - _ = stdoutReader.Close() - return err - }, - }) + err = command.Run(&git.RunOpts{ + Env: env, + Dir: repo.Path, + Stdout: stdoutWriter, + PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { + _ = stdoutWriter.Close() + err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env) + if err != nil { + log.Error("%v", err) + cancel() + } + _ = stdoutReader.Close() + return err + }, + }) if err != nil && !isErrUnverifiedCommit(err) { log.Error("Unable to check commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err) } diff --git a/routers/private/hook_verification_test.go b/routers/private/hook_verification_test.go new file mode 100644 index 0000000000..cd0c05ff50 --- /dev/null +++ b/routers/private/hook_verification_test.go @@ -0,0 +1,43 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + "testing" + + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/git" + + "github.com/stretchr/testify/assert" +) + +var testReposDir = "tests/repos/" + +func TestVerifyCommits(t *testing.T) { + unittest.PrepareTestEnv(t) + + gitRepo, err := git.OpenRepository(context.Background(), testReposDir+"repo1_hook_verification") + defer gitRepo.Close() + assert.NoError(t, err) + + testCases := []struct { + base, head string + verified bool + }{ + {"72920278f2f999e3005801e5d5b8ab8139d3641c", "d766f2917716d45be24bfa968b8409544941be32", true}, + {git.EmptySHA, "93eac826f6188f34646cea81bf426aa5ba7d3bfe", true}, // New branch with verified commit + {"9779d17a04f1e2640583d35703c62460b2d86e0a", "72920278f2f999e3005801e5d5b8ab8139d3641c", false}, + {git.EmptySHA, "9ce3f779ae33f31fce17fac3c512047b75d7498b", false}, // New branch with unverified commit + } + + for _, tc := range testCases { + err = verifyCommits(tc.base, tc.head, gitRepo, nil) + if tc.verified { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + } +} diff --git a/routers/private/main_test.go b/routers/private/main_test.go new file mode 100644 index 0000000000..700af6ec8d --- /dev/null +++ b/routers/private/main_test.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "path/filepath" + "testing" + + "code.gitea.io/gitea/models/unittest" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m, &unittest.TestOptions{ + GiteaRootPath: filepath.Join("..", ".."), + }) +} diff --git a/routers/private/tests/repos/repo1_hook_verification/HEAD b/routers/private/tests/repos/repo1_hook_verification/HEAD new file mode 100644 index 0000000000..b870d82622 --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/routers/private/tests/repos/repo1_hook_verification/config b/routers/private/tests/repos/repo1_hook_verification/config new file mode 100644 index 0000000000..64280b806c --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = true + symlinks = false + ignorecase = true diff --git a/routers/private/tests/repos/repo1_hook_verification/info/refs b/routers/private/tests/repos/repo1_hook_verification/info/refs new file mode 100644 index 0000000000..ee593c4db2 --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/info/refs @@ -0,0 +1 @@ +d766f2917716d45be24bfa968b8409544941be32 refs/heads/main diff --git a/routers/private/tests/repos/repo1_hook_verification/logs/HEAD b/routers/private/tests/repos/repo1_hook_verification/logs/HEAD new file mode 100644 index 0000000000..5c549b9b4e --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/logs/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 d766f2917716d45be24bfa968b8409544941be32 Gitea 1693148474 +0800 push diff --git a/routers/private/tests/repos/repo1_hook_verification/logs/refs/heads/main b/routers/private/tests/repos/repo1_hook_verification/logs/refs/heads/main new file mode 100644 index 0000000000..5c549b9b4e --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/logs/refs/heads/main @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 d766f2917716d45be24bfa968b8409544941be32 Gitea 1693148474 +0800 push diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c b/routers/private/tests/repos/repo1_hook_verification/objects/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c new file mode 100644 index 0000000000000000000000000000000000000000..d55278f9d45b3d2613b96d44eb9e79e507bc0f27 GIT binary patch literal 86 zcmV-c0IC0Y0V^p=O;s>7HexU|FfcPQQApG)sVHH1Huur&O&6~@dv|NDE04Ny=t)oT sM5ux!LJE?>3RZs4D_EBNgkfUItm*I8#D_2YchC`PLJCd=0AmO~qq@l?H2?qr literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/0b/5987362fe3fabdd4406babdc819642ee2f5a2a b/routers/private/tests/repos/repo1_hook_verification/objects/0b/5987362fe3fabdd4406babdc819642ee2f5a2a new file mode 100644 index 0000000000000000000000000000000000000000..d8b6020bc02054c403ad6288079eb97b8c0907a7 GIT binary patch literal 22 dcmb1&*~>k49dzZ(*R$F2e$wK literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/13/b0f23f673b161f4b5cb66f051cb93c99729e1e b/routers/private/tests/repos/repo1_hook_verification/objects/13/b0f23f673b161f4b5cb66f051cb93c99729e1e new file mode 100644 index 0000000000000000000000000000000000000000..77936d82415e3e526af9ef249a3b6531c14df4fa GIT binary patch literal 86 zcmV-c0IC0Y0V^p=O;s>7HexU|FfcPQQApG)sVHIKj%+v6fBb9j6^HEAcN(WTz0;4< sf+|P?D|j~d(d7HexU|FfcPQQApG)sVHIKj%+v6fBb9j6^HEAcN(WTz0;4< sf+|QNq#zlr;Mv?qvo~G5=Iq_Axvo6w#-S%Y!4sh-q~KHl0Pg!cNh*0LG5`Po literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/2b/df04adb23d2b40b6085efb230856e5e2a775b7 b/routers/private/tests/repos/repo1_hook_verification/objects/2b/df04adb23d2b40b6085efb230856e5e2a775b7 new file mode 100644 index 0000000000000000000000000000000000000000..355b88e95e80e2602144ed88debaa1ea38e27f3d GIT binary patch literal 55 zcmb7HexU|FfcPQQApG)sVHH1Huur&O&6~@dv|NDE04Ny=t)oT sM5ux!LJE?>3RZs4D_EBNgkfUItm*I8#D_2YchC`PLJCe30B#*W6uSE()c^nh literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/93/eac826f6188f34646cea81bf426aa5ba7d3bfe b/routers/private/tests/repos/repo1_hook_verification/objects/93/eac826f6188f34646cea81bf426aa5ba7d3bfe new file mode 100644 index 0000000000000000000000000000000000000000..80abd3ade146e5e807d77fa798872ad11813c6e8 GIT binary patch literal 684 zcmV;d0#p5X0hN-;j-yBvMSIOx#2#fAQ!q%?t!6S{8z(ry7 zzWW=nqQbMny9NVjMrIi5P2a`F?0>uz_u^R<;Ny2R({fJ0vpn$SSSK|I&*nF$00k=F z+B8+wbX6t0$~04NPVD1GD>h`*LCKyMe@XQCKI!pJ?Sdg0RaGw<|6fjlNl)|J{F1cx z)SG+v$NaR(2}}67T|di1ZR*FRc)EBf!%!FOMR3bPS33%n`zdT*r_sG_ zXJpyEKRSdH=?%Gu{Zkn8Xg11>#$p656J{C4T}23tuHBhc?3&Ig5RT7Bq^U-{Z;p6s z`}M+9L|6TFuAQFem_s$iYbh>Kn~o_tOTyk;H3nT4t8jK(BT;}Q9d6wz!$}QGzVYe- zPrI%#wVV#8aJh7i)Y#EO)t=$kOV%xiT!gdIJc6?K?AnYdux?ws+)h?7H2F9Fr3TUbLOVrs59EDU literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/97/79d17a04f1e2640583d35703c62460b2d86e0a b/routers/private/tests/repos/repo1_hook_verification/objects/97/79d17a04f1e2640583d35703c62460b2d86e0a new file mode 100644 index 0000000000..7f3293a8bc --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/objects/97/79d17a04f1e2640583d35703c62460b2d86e0a @@ -0,0 +1,2 @@ +x1 +!ES{AwGGa 9EQg W#AZ/p((Bhۼ&:pLY`U-z\ZM:xJ/G}:3 \ No newline at end of file diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/9c/e3f779ae33f31fce17fac3c512047b75d7498b b/routers/private/tests/repos/repo1_hook_verification/objects/9c/e3f779ae33f31fce17fac3c512047b75d7498b new file mode 100644 index 0000000000000000000000000000000000000000..61a9ee8391cacd644d02db180d3e4acef6a6426a GIT binary patch literal 153 zcmV;K0A~Mq0hNwH3c@fD06pgwxeKz%wn-L5{L1cTt39-pkP80Z;txCx3=E8J%QAN# zLpb$j=Ao!)nZO88fI@;evgI-7F%6QfBAH55_u^)2_bNOnf(A+q254F8l+2_cVGV;& zg^bLO_x{|PXVGf@`osM(cjJq0%gu*G7=}U#{TT{?JM1ym?T^Im>)YBqeY`YY^%>n4 H#@RwpUeQZ( literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/a9/f76e70a663e40091749a97eeac5f57a6fec141 b/routers/private/tests/repos/repo1_hook_verification/objects/a9/f76e70a663e40091749a97eeac5f57a6fec141 new file mode 100644 index 0000000000000000000000000000000000000000..fb79dc96b958666cf9f58227b26cf5a55ddfca72 GIT binary patch literal 24 gcmb7HexU|FfcPQQApG)sVHH1Huur&O&6~@dv|NDE04Ny=t)oT VM5ux!LJE=zDM-Po004)>KZVCe7#IKm literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/bb/87653e0819460e79b5f075f2563f583cbbf18c b/routers/private/tests/repos/repo1_hook_verification/objects/bb/87653e0819460e79b5f075f2563f583cbbf18c new file mode 100644 index 0000000000000000000000000000000000000000..a765d667581bf143461d197e64e95fdf5219fd51 GIT binary patch literal 682 zcmV;b0#*HZ0hN-;&ZAZkg|p@~LpW8gCR>D%cjyKGXM zlD?yo&MBR|e>_?YL{az+k7WsH7Eu+$R#0FO4e5$Qe2yTjD4x!d#O9JDFj7gE#48)i zCkBP^5gCaV1R53`k4lC^RVG0Zi5w;I94B!!Dp`ieX5926$WUIC;QO!jSKZ<=`#tX; ze*zjxEX|1`&w~RcQWWu_A7i-uA1C#zp4u9G`+F4Kuv~C9&LFT1SH4_*{TC+y0b0*^ zE3&MpvRoWnUC+*IYTECEWb%|eO63%KTdF3nSxt`e<1(gWE~|Ct{mTjPQJiPJ%0ox- z)Y>Sa7e_mNi|p3?A_d<5GUQh)Ma3;@L+0LC>zP_zKZ5<>BPDD%0$h39+17$1A&s^) z7?eXV=i#h$V*3pE)jPA&w%T1lpXx7B<%2AmqA;f1AlCT);S~gEjIE|sWtF1K`4zU!<@#hZ}d0a>{q!txQMx0qSLV*(x zaB!Zlo9dm4Xq$Y?BG*2gtTcpa-!;~Q)NY?Bv%bs(aAy6Dro>0SI1Gs{mdRIRT*d8x zh4HvBZX!e-#h4{-mXfWl3zV7dl+0(cmPly(3IQg#-|lm6ug_wu?(WPWs?kVi=#Rsm zydM}_q>L4;m p1w͗-7pĄpZsDZL̾Le@ \ No newline at end of file diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/d7/66f2917716d45be24bfa968b8409544941be32 b/routers/private/tests/repos/repo1_hook_verification/objects/d7/66f2917716d45be24bfa968b8409544941be32 new file mode 100644 index 0000000000..154458468c --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/objects/d7/66f2917716d45be24bfa968b8409544941be32 @@ -0,0 +1,3 @@ +xˮFE3+zn%44!%QxۀsA` 8{2IMjdf2"$e +-( !J"a@BaHo3V<$/)$JJDBH{ # RROnfFO +q[2̇~zjjL}prmFqh `@ث՘f?3[7) ^uֿ,l7zr|&Ou49:Qj1x6Q%tsV| (V,aL,G~r@`$[! Xˊep[8 o(kZγyeйYkd63;3 Ri ދdYDk91V]/C#&poFb}uW&]+m xaqdIX3 3KI#i_rgĩ7=`@[&A̤Lo3~M8MGt>xvQ(aWo"srzeŭ}QD֨fK)mr>>̚$F8x ^J k{mczI*^Mb m6M~hp {0 ]€?nUwgɠJ б<72 \ No newline at end of file diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/e3/7e5d19823e42fad252f6341b1f77a7bc6ee451 b/routers/private/tests/repos/repo1_hook_verification/objects/e3/7e5d19823e42fad252f6341b1f77a7bc6ee451 new file mode 100644 index 0000000000000000000000000000000000000000..b3f925e8178ae2e9412790904043936967b15dcd GIT binary patch literal 59 zcmV-B0L1@z0V^p=O;xb8WH2-^Ff%bxNYpE-C}DUu_tET47q2;ccWbUIkGgT_Nl)-Z RsDdOy3X*Xu003m3Fc_uO7S{j( literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/routers/private/tests/repos/repo1_hook_verification/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 0000000000000000000000000000000000000000..711223894375fe1186ac5bfffdc48fb1fa1e65cc GIT binary patch literal 15 WcmbNKosd;#-0#B*d5_BMiL=QIGE!M@-P=zj!Bq&x5K+66$s&YhU#!n zni1@v1ZlVvbC^q#L|B^UMVk3e@QH{Mkzu8b6*wlP@HCcf4{(xZ1pNLp{wk`G;NQ}= z{u3~Q#4?-+B_3>Ok*29H{XRC5|4}KrVyFu6>))rKyUQ5*b2wiPfl^)JT_WzmJVnR*Q%GFQ>qyVs&!ot+$MG z6Zq4XFVoAx?&oDq`gQM3kG3f0uJ4S=f7@sOD&W`k$lb(!!?HzFQDF40Ot^_~32R?) zH?1)pAGSlA)`?Z0cl~xwlu3^5P)u)acfSY z(Rk7;KJUeY@9)$n(ZuMbm1vJNZdIM`GhjNo3nwXgYm0D|AO)CLYn!|;IZe@JXpJ2> zHksU>=W4sQ=Rk3d?v=OZ)W~tMw#U2L1&g4kmz}n$GM;@>pzm!<*H%@2nxu8^^{qds zXcb@Bz+Zu4tSiB=6PX^ZZE>-Gxa~L0| zoeS%6LwF`gS$dADL4H8>V})lO)(Q?gr$zF$Us#_4{xebf2tHfpWt zuE@xH+=4D=1kZb!g_sM`$x~-9Z^}F_tp_*fEyd16X!S-_`?oA9rcfc$kzVLKx!s^ L^=7HexU|FfcPQQApG)sVHH1Huur&O&6~@dv|NDE04Ny=t)oT sM5ux!LJE?>3RZs4D_EBNgkfUItm*I8#D_2YchC`PLJCd=0AmO~qq@l?H2?qr literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/0b/5987362fe3fabdd4406babdc819642ee2f5a2a b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/0b/5987362fe3fabdd4406babdc819642ee2f5a2a new file mode 100644 index 0000000000000000000000000000000000000000..d8b6020bc02054c403ad6288079eb97b8c0907a7 GIT binary patch literal 22 dcmb1&*~>k49dzZ(*R$F2e$wK literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/13/b0f23f673b161f4b5cb66f051cb93c99729e1e b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/13/b0f23f673b161f4b5cb66f051cb93c99729e1e new file mode 100644 index 0000000000000000000000000000000000000000..77936d82415e3e526af9ef249a3b6531c14df4fa GIT binary patch literal 86 zcmV-c0IC0Y0V^p=O;s>7HexU|FfcPQQApG)sVHIKj%+v6fBb9j6^HEAcN(WTz0;4< sf+|P?D|j~d(d7HexU|FfcPQQApG)sVHIKj%+v6fBb9j6^HEAcN(WTz0;4< sf+|QNq#zlr;Mv?qvo~G5=Iq_Axvo6w#-S%Y!4sh-q~KHl0Pg!cNh*0LG5`Po literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/8b/903ede7c494725624bf842ec890f6342dababd b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/8b/903ede7c494725624bf842ec890f6342dababd new file mode 100644 index 0000000000000000000000000000000000000000..fd3c3a488b22a43d8391d6db5a5787a33c77024e GIT binary patch literal 86 zcmV-c0IC0Y0V^p=O;s>7HexU|FfcPQQApG)sVHH1Huur&O&6~@dv|NDE04Ny=t)oT sM5ux!LJE?>3RZs4D_EBNgkfUItm*I8#D_2YchC`PLJCe30B#*W6uSE()c^nh literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/93/eac826f6188f34646cea81bf426aa5ba7d3bfe b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/93/eac826f6188f34646cea81bf426aa5ba7d3bfe new file mode 100644 index 0000000000000000000000000000000000000000..80abd3ade146e5e807d77fa798872ad11813c6e8 GIT binary patch literal 684 zcmV;d0#p5X0hN-;j-yBvMSIOx#2#fAQ!q%?t!6S{8z(ry7 zzWW=nqQbMny9NVjMrIi5P2a`F?0>uz_u^R<;Ny2R({fJ0vpn$SSSK|I&*nF$00k=F z+B8+wbX6t0$~04NPVD1GD>h`*LCKyMe@XQCKI!pJ?Sdg0RaGw<|6fjlNl)|J{F1cx z)SG+v$NaR(2}}67T|di1ZR*FRc)EBf!%!FOMR3bPS33%n`zdT*r_sG_ zXJpyEKRSdH=?%Gu{Zkn8Xg11>#$p656J{C4T}23tuHBhc?3&Ig5RT7Bq^U-{Z;p6s z`}M+9L|6TFuAQFem_s$iYbh>Kn~o_tOTyk;H3nT4t8jK(BT;}Q9d6wz!$}QGzVYe- zPrI%#wVV#8aJh7i)Y#EO)t=$kOV%xiT!gdIJc6?K?AnYdux?ws+)h?7H2F9Fr3TUbLOVrs59EDU literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/9c/e3f779ae33f31fce17fac3c512047b75d7498b b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/9c/e3f779ae33f31fce17fac3c512047b75d7498b new file mode 100644 index 0000000000000000000000000000000000000000..61a9ee8391cacd644d02db180d3e4acef6a6426a GIT binary patch literal 153 zcmV;K0A~Mq0hNwH3c@fD06pgwxeKz%wn-L5{L1cTt39-pkP80Z;txCx3=E8J%QAN# zLpb$j=Ao!)nZO88fI@;evgI-7F%6QfBAH55_u^)2_bNOnf(A+q254F8l+2_cVGV;& zg^bLO_x{|PXVGf@`osM(cjJq0%gu*G7=}U#{TT{?JM1ym?T^Im>)YBqeY`YY^%>n4 H#@RwpUeQZ( literal 0 HcmV?d00001 diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/a9/f76e70a663e40091749a97eeac5f57a6fec141 b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/a9/f76e70a663e40091749a97eeac5f57a6fec141 new file mode 100644 index 0000000000000000000000000000000000000000..fb79dc96b958666cf9f58227b26cf5a55ddfca72 GIT binary patch literal 24 gcmb~LpW8gCR>D%cjyKGXM zlD?yo&MBR|e>_?YL{az+k7WsH7Eu+$R#0FO4e5$Qe2yTjD4x!d#O9JDFj7gE#48)i zCkBP^5gCaV1R53`k4lC^RVG0Zi5w;I94B!!Dp`ieX5926$WUIC;QO!jSKZ<=`#tX; ze*zjxEX|1`&w~RcQWWu_A7i-uA1C#zp4u9G`+F4Kuv~C9&LFT1SH4_*{TC+y0b0*^ zE3&MpvRoWnUC+*IYTECEWb%|eO63%KTdF3nSxt`e<1(gWE~|Ct{mTjPQJiPJ%0ox- z)Y>Sa7e_mNi|p3?A_d<5GUQh)Ma3;@L+0LC>zP_zKZ5<>BPDD%0$h39+17$1A&s^) z7?eXV=i#h$V*3pE)jPA&w%T1lpXx7B<%2AmqA;f1AlCT);S~gEjIE|sWtF1K`4zU!<@#hZ}d0a>{q!txQMx0qSLV*(x zaB!Zlo9dm4Xq$Y?BG*2gtTcpa-!;~Q)NY?Bv%bs(aAy6Dro>0SI1Gs{mdRIRT*d8x zh4HvBZX!e-#h4{-mXfWl3zV7dl+0(cmPly(3IQg#-|lm6ug_wu?(WPWs?kVi=#Rsm zydM}_q>L4;m p1w͗-7pĄpZsDZL̾Le@ \ No newline at end of file diff --git a/routers/private/tests/repos/repo1_hook_verification/refs/heads/main b/routers/private/tests/repos/repo1_hook_verification/refs/heads/main new file mode 100644 index 0000000000..2186e820a7 --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/refs/heads/main @@ -0,0 +1 @@ +d766f2917716d45be24bfa968b8409544941be32 diff --git a/routers/private/tests/repos/repo1_hook_verification_dummy_gpg_key.txt b/routers/private/tests/repos/repo1_hook_verification_dummy_gpg_key.txt new file mode 100644 index 0000000000..3061c75dcb --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification_dummy_gpg_key.txt @@ -0,0 +1,127 @@ +# GPG key for abcde@gitea.com + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQGNBGTrY3UBDAC2HLBqmMplAV15qSnC7g1c4dV406f5EHNhFr95Nup2My6b2eaf +Tlvedv77s8PT/I7F3fy4apOZs5A7w2SsPlLMcQ3ev4uGOsxRtkq5RLy1Yb6SNueX +0Da2UVKR5KTC5Q6BWaqxwS0IjKOLZ/xz0Pbe/ClV3bZSKBEY2omkVo3Z0HZ771vB +2clPRvGJ/IdeKOsZ3ZytSFXfyiJBdARmeSPmydXLil8+Ibq5iLAeow5PK8hK1TCO +nKHzLWNqcNq70tyjoHvcGi70iGjoVEEUgPCLLuU8WmzTJwlvA3BuDzjtaO7TLo/j +dE6iqkHtMSS8x+43sAH6hcFRCWAVh/0Uq7n36uGDfNxGnX3YrmX3LR9x5IsBES1r +GGWbpxio4o5GIf/Xd+JgDd9rzJCqRuZ3/sW/TxK38htWaVNZV0kMkHUCTc1ctzWp +Cm635hbFCHBhPYIp+/z206khkAKDbz/CNuU91Wazsh7KO07wrwDtxfDDbInJ8TfH +E2TGjzjQzgChfmcAEQEAAbQXYWJjZGUgPGFiY2RlQGdpdGVhLmNvbT6JAc4EEwEI +ADgWIQRo/BkcvP70fnQCv16xVDFkJim4JgUCZOtjdQIbAwULCQgHAgYVCgkICwIE +FgIDAQIeAQIXgAAKCRCxVDFkJim4Js6+C/9yIjHqcyM88hQAYQUoiPYfgJ0f2NsD +Ai/XypyDaFbRy9Wqm3oKvMr9L9G5xgOXshjRaRWOpODAwLmtVrJfOV5BhxLEcBcO +2hDdM3ycp8Gt7+Fx/o0cUjPiiC18hh3K5LRfeE7oYynSJDgjoDNuzIMuyoWuJPNc ++IcE4roND55qyyyC9ObrTLz1GgGm1bXtkHhZ1NdOfQ4q8M48K39Jn7pmnmSX3R74 +CSU6flh/o9AtzGLjU70JUOLFcWnR5D0iEI8mOsdfEHr+p+CvDVG9l4unPhMunT+Q +OUwV2DEmqo9P+yIert1ucVTDoSf+FrRaKUHg8r1Tt6T4/4GyIeSxG72NImK0h8jz ++bADPZhxuG4UR1Mj8bilqhWgODFPi/5DrDsNMWq1pEvjn6f4pCUx0IDTnPTniOXt +afXtAD4Rz0rwJWYqgeJFHgjXzaxBiOE1bhS26NPEvyAa0T9Tj3E73ICMESAmVad2 +JqO/mVxkLDGWdpXM7qB8bO2YGMOplrTvWaa5AY0EZOtjdQEMAOwevO46JxBo91RC +bT7RQ2uz3ZwRKb+P/jIEFST6x8tkCjon31zh6HicBDPNntqXTzStgoHQb7vGhHPV +4dxAfrOtVyoHwpi1/+x1jjtZoyIzLEz6RNK/Onu2y/tC5JBnSd5QRdHJgzPm20F8 +iNZR37c0Mi24fIH4y01aVLfNeBpRt7lWJ+opo2bM3Rh7jJdMpynKkTcA6o9XP6Ig +W/dzpOayosclpHhWiJwKV4CovIX/bxawk7sz10Nb4QzcxlWexWnJxNRHIcAkZ9KT +XTBpBkBpHCZqsI3+rQoQn5oQAr9JGWJSd4Fmgw7mFjmIF4bjfa2h/BpCoBqE+/25 +chvWfYkQwrCcyUwD1QYPUBwNvLB+PWb9kYEHD3mLgSSR+fjdG9XdMevu4lT91Gqo +/6KJzgzClSs7GoQtb+SZ4deUFw1tlmEQS/BGhbtTb/1566iDidGV5EnSmL/E4/3C +bGQqNog8gremF0G0SlWTjD9RMBY13IgisWCC6R4CdkXIYnCWbwARAQABiQG2BBgB +CAAgFiEEaPwZHLz+9H50Ar9esVQxZCYpuCYFAmTrY3UCGwwACgkQsVQxZCYpuCb1 +AAv/dI5YtGxBXaHAMj+lOLmZi5w4t0M7Zafa8tNnWrBwj4KixiXEt52i5YKxuaVD +3+/cMqidSDp0M5Cxx0wcmnmg+mdFFcowtXIXuk1TGTcHcOCPoXgF6gfoGimNNE1A +w1+EnC4/TbjMCKEM7b2QZ7/CgkBxZJWbScN4Jtawory9LEQqo0/epYJwf+79GHIJ +rpODAPiPJEMKmlej23KyoFuusOi17C0vHCf3GZNj4F2So3LOrcs51qTlOum2MdL5 +oTdqffatzs6p4u5bHBxyRugQlQggTRSK+TXLdxnFXr9ukXjIC2mFir7CCnZHw4e+ +2JwZfaAom0ZX+pLwrReSop4BPPU2YDzt3XCUk0S9kpiOsN7iFWUMCFreIE50DOxt +9406kSGopYKVaifbDl4MdLXM4v+oucLe7/yOViT/dm4FcIytIR+jzC8MaLQTB23e +uzm2wOjI1YOwv7Il6PWZyDdU+tyzXcaJ7wSFBeQFZZtqph2TItCeV04HoaKHHc25 +4akc +=OYIo +-----END PGP PUBLIC KEY BLOCK----- + +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQWGBGTrY3UBDAC2HLBqmMplAV15qSnC7g1c4dV406f5EHNhFr95Nup2My6b2eaf +Tlvedv77s8PT/I7F3fy4apOZs5A7w2SsPlLMcQ3ev4uGOsxRtkq5RLy1Yb6SNueX +0Da2UVKR5KTC5Q6BWaqxwS0IjKOLZ/xz0Pbe/ClV3bZSKBEY2omkVo3Z0HZ771vB +2clPRvGJ/IdeKOsZ3ZytSFXfyiJBdARmeSPmydXLil8+Ibq5iLAeow5PK8hK1TCO +nKHzLWNqcNq70tyjoHvcGi70iGjoVEEUgPCLLuU8WmzTJwlvA3BuDzjtaO7TLo/j +dE6iqkHtMSS8x+43sAH6hcFRCWAVh/0Uq7n36uGDfNxGnX3YrmX3LR9x5IsBES1r +GGWbpxio4o5GIf/Xd+JgDd9rzJCqRuZ3/sW/TxK38htWaVNZV0kMkHUCTc1ctzWp +Cm635hbFCHBhPYIp+/z206khkAKDbz/CNuU91Wazsh7KO07wrwDtxfDDbInJ8TfH +E2TGjzjQzgChfmcAEQEAAf4HAwKN54iG/XBl5/UViAmmiESRj3u+uJC9EztalVbj +156bjamUHBYIoCH4SBB0l0bR/o9ZN3vE4ZvyF3OyJ0AKF9epjWIuz7S+QIm1NLzk +IqwRyfGPsktwtZOF1CsathN4RyJL5/3nB9g4BLYfRARe9lwU0C0HQjBwAVj8m6RN ++wMTHZqW7tUN75npgPRLUI30H3GPVm3yLfS88Ol8nd31r7V0JsXZ2/mM9CWF4sUy +o1DW3P/rBn49s/x2qL/acEL+5PK7suFBP8Pjp5cwGjnSehoWeOclXgstkg3OEryY +2JP74muDVmaEVOAk7wiRjUD7HYuEOm/MbphFyen7QtO8WtN3IRKgNm19v5Skd4AF +NW9ZAdQOk2yHw7zyRk7HOPmEbEstbyE1RYWIfgZGjJlEJ2DI5ABwVJJ3W6DRPiZ3 +owd/JxBUVu/wigIjbg6z6ZQd/bn1XwKyhyTtgyTyILzE1gqtO7xs1XmK3wcww794 +cVLjqSnAdaeXMt4P+sDA17Wqky0f/jQ9kq7/tv7ipq9jvp9RaQ1ccRsz+mGgBVl+ +oLg4klKN47ZQGt0SQpLzHLL8SHzY0dz5US+Z2J+hdZia6jEmfilY9r4WPe7djMYz +Na908DmcbjfAg4XHPqVRXjgraUiT2YTo2LOV2dHn7550hJ/JshpOVqrJUrjhCgDN +usEMK3KXJkFvf6zflMv3t8HMD2SGBfpCJSwDaW+mrmtpR6a5laoZxg/009qZqgpj +FuenLuZmgYrHXozMXllwi6MLvSE/ioXrK4fqvpAwzOk6ArqZdWfxoJDYNQKXVL7z +Arniq9Ctaag8hr5T+JoZ9wNPNVF/LuEwPTWDur4qpU07KqWt9OFKPsEDNzxVZfNM +vtSCYvQ1uUH3CbPLQvPpd5TnyhjwKYtTzyW4OcuZHrWIZp9fZi5QdhWxobqGQiBk ++nRNFe0FPVEN0VcNdYJIDKcDLsOYCkGy08tucZnbKtr8JaK7XBSOo9Frg1i/j4Aa +GnXWlkMTVAkuxLZPATTOgdBoYmHMYKQvw31aFBrf3QU9c3EEg9UPYFMErVIeBHBB +BS+E7QZToHScCG1zezlr4rdqarkz0Yvzc3aduoSAOJHDf/Il+tOkepMne1y5fi72 +5UT1yWGbXXkTCV/pM6s0pLaEvNHmGvPQ6VGbJ//5w+42PFD1d7yEai53OgSZNs7B ++Ie/6Vq5GYzTM0bT3/o7/O1Zi56y791YKaas9wgxOhmMIZ0hsTecQJLJZGotUlOv +V7fZUhPRc4ksUeCyM3G0E89ilFtY6NuPcWQ8yMeS4sRRLmie+iaT+kNvAqL5mXvg +WNLhFIXPC1gpGLB8lpT5YEY647aPjQEig7QXYWJjZGUgPGFiY2RlQGdpdGVhLmNv +bT6JAc4EEwEIADgWIQRo/BkcvP70fnQCv16xVDFkJim4JgUCZOtjdQIbAwULCQgH +AgYVCgkICwIEFgIDAQIeAQIXgAAKCRCxVDFkJim4Js6+C/9yIjHqcyM88hQAYQUo +iPYfgJ0f2NsDAi/XypyDaFbRy9Wqm3oKvMr9L9G5xgOXshjRaRWOpODAwLmtVrJf +OV5BhxLEcBcO2hDdM3ycp8Gt7+Fx/o0cUjPiiC18hh3K5LRfeE7oYynSJDgjoDNu +zIMuyoWuJPNc+IcE4roND55qyyyC9ObrTLz1GgGm1bXtkHhZ1NdOfQ4q8M48K39J +n7pmnmSX3R74CSU6flh/o9AtzGLjU70JUOLFcWnR5D0iEI8mOsdfEHr+p+CvDVG9 +l4unPhMunT+QOUwV2DEmqo9P+yIert1ucVTDoSf+FrRaKUHg8r1Tt6T4/4GyIeSx +G72NImK0h8jz+bADPZhxuG4UR1Mj8bilqhWgODFPi/5DrDsNMWq1pEvjn6f4pCUx +0IDTnPTniOXtafXtAD4Rz0rwJWYqgeJFHgjXzaxBiOE1bhS26NPEvyAa0T9Tj3E7 +3ICMESAmVad2JqO/mVxkLDGWdpXM7qB8bO2YGMOplrTvWaadBYYEZOtjdQEMAOwe +vO46JxBo91RCbT7RQ2uz3ZwRKb+P/jIEFST6x8tkCjon31zh6HicBDPNntqXTzSt +goHQb7vGhHPV4dxAfrOtVyoHwpi1/+x1jjtZoyIzLEz6RNK/Onu2y/tC5JBnSd5Q +RdHJgzPm20F8iNZR37c0Mi24fIH4y01aVLfNeBpRt7lWJ+opo2bM3Rh7jJdMpynK +kTcA6o9XP6IgW/dzpOayosclpHhWiJwKV4CovIX/bxawk7sz10Nb4QzcxlWexWnJ +xNRHIcAkZ9KTXTBpBkBpHCZqsI3+rQoQn5oQAr9JGWJSd4Fmgw7mFjmIF4bjfa2h +/BpCoBqE+/25chvWfYkQwrCcyUwD1QYPUBwNvLB+PWb9kYEHD3mLgSSR+fjdG9Xd +Mevu4lT91Gqo/6KJzgzClSs7GoQtb+SZ4deUFw1tlmEQS/BGhbtTb/1566iDidGV +5EnSmL/E4/3CbGQqNog8gremF0G0SlWTjD9RMBY13IgisWCC6R4CdkXIYnCWbwAR +AQAB/gcDAgtreHsdznsa9bAha2g+J5zygs7rp95KvqRm4SGrgWPnngMewrHXrJAx +REUQFbOYJKvb6+SB47N8BTIh/nEY/B6dpvC36QSHB0XAgkktiOhdS2rTlrq+bKse +rZzoM/jbcxS3/cwi4VWH4lQhz7TLZtQxFZDuwyiik8/m5KscMxQrbYJg++4KpFQQ +En7RRUO0hEaYdnqQ9t3M8SWLwZn2yK3hzBE0gkQ8CJA3Zokv3DO7FSsAX823O25B +X7NgIpmbHCeYK6YV0gjQUKP1o3Sf7DhJzO1iltg0+obNTDl9RoeFgxTVORCdUlGA +kPdgoBbAGtadpZlCMThn7FlIn+ogqwQpAcoSTZjX31SOQBBpgMW9yf3GTNk2Nvrn +08zIA0hnUWFfc4VY6fbjbX5bF0jpoJ3XG6Hwa1VVRwQGFLxFV23TbZ+baLLuxEBx +A86XDC5zWFMwF/7aYL8oeXgoI+499u9G4Gw9G87va7rQXlTQJcHQRqu9YaGcxwOi +UslhNtVWz52iIURappUfFaGBRGUvtx2DOTgn4m099nnPaKDUiLmc4bFIHwzyA7Pl +RdAmLosrxSyIxHdlUOS/KshucXXKGVoYkJqGLXNQCY6x2zbyBPX9/a/0P59UP/WU +qwAHuGbXlToGhSKZzC8KmVs12tyQsAZ/47D+G29kEcRlaey1+N3Uor1jN7D66uyj +M1jYFhBudNIuuTR8sfrYjmbYIj8y0bgvF4RN6sU1padoTETadWNyIcFiRMZQ0oQd +KJBa3CxdqQZ2EU4a5jkA4UTQE13IySh7eNbYP5VwBgr3Z59gcbouKfFxKBhmPHF2 +BAmC0VXI2BgqKNqM6QgVj5UKrp41AX4D+iIhyKa0D3rapuIywXg1AtsrAlrOU/Ig +tQCj/a0NjIVJpLqVKBUdd4Eea69fDCJGIoaDNyp7qwo+nA1O2oDbc32EryJYUkHm +XMoLmx5y+/rxRsRevBv0ojwu3zsx2K93M1wHYd0z+SJsU8QGFinoFgYcmNp/tgMW +WtHBN4AijDuDSZAyG+MrWIj3NS4mbajx+utEIn3DC/ofFPlTmgX3OvpOPG1hnhBH +xSZUME+znOnqJMpUqnna4jbHEPwvRIXUY6InFKgl1Bu4grww/oo3qi7NwWL0Mcdy +qabWhdlEz5N/QBBPWVQllelgI+xTmZoCRUhh1mn+PM900vXXeM/DIALnxEXs9I/m +l4wPdLZlCdaKZS8vv33adyS6i9gWfI3NPWxZ2TyqC7nf5D5OK1zKSu3iWx17nXn2 +ak5hZnaXfzTxuZL3E8KZD/qsDm80c2PXFitogJTih37N6A8UQOJPtWbkfvPiwUvI +gw0oouggn0iJQVNoiQG2BBgBCAAgFiEEaPwZHLz+9H50Ar9esVQxZCYpuCYFAmTr +Y3UCGwwACgkQsVQxZCYpuCb1AAv/dI5YtGxBXaHAMj+lOLmZi5w4t0M7Zafa8tNn +WrBwj4KixiXEt52i5YKxuaVD3+/cMqidSDp0M5Cxx0wcmnmg+mdFFcowtXIXuk1T +GTcHcOCPoXgF6gfoGimNNE1Aw1+EnC4/TbjMCKEM7b2QZ7/CgkBxZJWbScN4Jtaw +ory9LEQqo0/epYJwf+79GHIJrpODAPiPJEMKmlej23KyoFuusOi17C0vHCf3GZNj +4F2So3LOrcs51qTlOum2MdL5oTdqffatzs6p4u5bHBxyRugQlQggTRSK+TXLdxnF +Xr9ukXjIC2mFir7CCnZHw4e+2JwZfaAom0ZX+pLwrReSop4BPPU2YDzt3XCUk0S9 +kpiOsN7iFWUMCFreIE50DOxt9406kSGopYKVaifbDl4MdLXM4v+oucLe7/yOViT/ +dm4FcIytIR+jzC8MaLQTB23euzm2wOjI1YOwv7Il6PWZyDdU+tyzXcaJ7wSFBeQF +ZZtqph2TItCeV04HoaKHHc254akc +=PPG4 +-----END PGP PRIVATE KEY BLOCK----- From 5315153059529f03b06c8f25487ffcc21ae3163f Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Wed, 30 Aug 2023 08:55:25 +0200 Subject: [PATCH 58/60] Use `Set[Type]` instead of `map[Type]bool/struct{}`. (#26804) --- build/backport-locales.go | 7 ++++--- build/generate-go-licenses.go | 14 +++++--------- models/unit/unit.go | 6 +++--- modules/assetfs/layered.go | 19 +++++++------------ modules/templates/util_dict.go | 10 +++++----- routers/api/actions/runner/utils.go | 8 +++----- routers/web/user/home.go | 7 ++----- services/auth/source/ldap/source_sync.go | 7 ++++--- services/migrations/gitlab.go | 6 +++--- 9 files changed, 36 insertions(+), 48 deletions(-) diff --git a/build/backport-locales.go b/build/backport-locales.go index 0346215348..d112dd72bd 100644 --- a/build/backport-locales.go +++ b/build/backport-locales.go @@ -12,6 +12,7 @@ import ( "path/filepath" "strings" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/setting" ) @@ -58,7 +59,7 @@ func main() { // use old en-US as the base, and copy the new translations to the old locales enUsOld := inisOld["options/locale/locale_en-US.ini"] - brokenWarned := map[string]bool{} + brokenWarned := make(container.Set[string]) for path, iniOld := range inisOld { if iniOld == enUsOld { continue @@ -77,7 +78,7 @@ func main() { broken := oldStr != "" && strings.Count(oldStr, "%") != strings.Count(newStr, "%") broken = broken || strings.Contains(oldStr, "\n") || strings.Contains(oldStr, "\n") if broken { - brokenWarned[secOld.Name()+"."+keyEnUs.Name()] = true + brokenWarned.Add(secOld.Name() + "." + keyEnUs.Name()) fmt.Println("----") fmt.Printf("WARNING: skip broken locale: %s , [%s] %s\n", path, secEnUS.Name(), keyEnUs.Name()) fmt.Printf("\told: %s\n", strings.ReplaceAll(oldStr, "\n", "\\n")) @@ -103,7 +104,7 @@ func main() { broken = broken || strings.HasPrefix(str, "`\"") broken = broken || strings.Count(str, `"`)%2 == 1 broken = broken || strings.Count(str, "`")%2 == 1 - if broken && !brokenWarned[sec.Name()+"."+key.Name()] { + if broken && !brokenWarned.Contains(sec.Name()+"."+key.Name()) { fmt.Printf("WARNING: found broken locale: %s , [%s] %s\n", path, sec.Name(), key.Name()) fmt.Printf("\tstr: %s\n", strings.ReplaceAll(str, "\n", "\\n")) fmt.Println("----") diff --git a/build/generate-go-licenses.go b/build/generate-go-licenses.go index c3b40c226f..84ba39025c 100644 --- a/build/generate-go-licenses.go +++ b/build/generate-go-licenses.go @@ -15,6 +15,8 @@ import ( "regexp" "sort" "strings" + + "code.gitea.io/gitea/modules/container" ) // regexp is based on go-license, excluding README and NOTICE @@ -55,20 +57,14 @@ func main() { // yml // // It could be removed once we have a better regex. - excludedExt := map[string]bool{ - ".gitignore": true, - ".go": true, - ".mod": true, - ".sum": true, - ".toml": true, - ".yml": true, - } + excludedExt := container.SetOf(".gitignore", ".go", ".mod", ".sum", ".toml", ".yml") + var paths []string err := filepath.WalkDir(base, func(path string, entry fs.DirEntry, err error) error { if err != nil { return err } - if entry.IsDir() || !licenseRe.MatchString(entry.Name()) || excludedExt[filepath.Ext(entry.Name())] { + if entry.IsDir() || !licenseRe.MatchString(entry.Name()) || excludedExt.Contains(filepath.Ext(entry.Name())) { return nil } paths = append(paths, path) diff --git a/models/unit/unit.go b/models/unit/unit.go index 5f5e20de1e..ca3e7f999d 100644 --- a/models/unit/unit.go +++ b/models/unit/unit.go @@ -9,6 +9,7 @@ import ( "strings" "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) @@ -318,14 +319,13 @@ var ( // FindUnitTypes give the unit key names and return valid unique units and invalid keys func FindUnitTypes(nameKeys ...string) (res []Type, invalidKeys []string) { - m := map[Type]struct{}{} + m := make(container.Set[Type]) for _, key := range nameKeys { t := TypeFromKey(key) if t == TypeInvalid { invalidKeys = append(invalidKeys, key) - } else if _, ok := m[t]; !ok { + } else if m.Add(t) { res = append(res, t) - m[t] = struct{}{} } } return res, invalidKeys diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go index d69732f81b..9678d23ad6 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -14,6 +14,7 @@ import ( "sort" "time" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/util" @@ -130,7 +131,7 @@ func readDir(layer *Layer, name string) ([]fs.FileInfo, error) { // * false: only directories will be returned. // The returned files are sorted by name. func (l *LayeredFS) ListFiles(name string, fileMode ...bool) ([]string, error) { - fileMap := map[string]bool{} + fileSet := make(container.Set[string]) for _, layer := range l.layers { infos, err := readDir(layer, name) if err != nil { @@ -138,14 +139,11 @@ func (l *LayeredFS) ListFiles(name string, fileMode ...bool) ([]string, error) { } for _, info := range infos { if shouldInclude(info, fileMode...) { - fileMap[info.Name()] = true + fileSet.Add(info.Name()) } } } - files := make([]string, 0, len(fileMap)) - for file := range fileMap { - files = append(files, file) - } + files := fileSet.Values() sort.Strings(files) return files, nil } @@ -161,7 +159,7 @@ func (l *LayeredFS) ListAllFiles(name string, fileMode ...bool) ([]string, error } func listAllFiles(layers []*Layer, name string, fileMode ...bool) ([]string, error) { - fileMap := map[string]bool{} + fileSet := make(container.Set[string]) var list func(dir string) error list = func(dir string) error { for _, layer := range layers { @@ -172,7 +170,7 @@ func listAllFiles(layers []*Layer, name string, fileMode ...bool) ([]string, err for _, info := range infos { path := util.PathJoinRelX(dir, info.Name()) if shouldInclude(info, fileMode...) { - fileMap[path] = true + fileSet.Add(path) } if info.IsDir() { if err = list(path); err != nil { @@ -186,10 +184,7 @@ func listAllFiles(layers []*Layer, name string, fileMode ...bool) ([]string, err if err := list(name); err != nil { return nil, err } - var files []string - for file := range fileMap { - files = append(files, file) - } + files := fileSet.Values() sort.Strings(files) return files, nil } diff --git a/modules/templates/util_dict.go b/modules/templates/util_dict.go index 79c307b68d..8d6376b522 100644 --- a/modules/templates/util_dict.go +++ b/modules/templates/util_dict.go @@ -9,6 +9,7 @@ import ( "html/template" "reflect" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" ) @@ -51,7 +52,7 @@ func dict(args ...any) (map[string]any, error) { return m, nil } -func dumpVarMarshalable(v any, dumped map[uintptr]bool) (ret any, ok bool) { +func dumpVarMarshalable(v any, dumped container.Set[uintptr]) (ret any, ok bool) { if v == nil { return nil, true } @@ -61,11 +62,10 @@ func dumpVarMarshalable(v any, dumped map[uintptr]bool) (ret any, ok bool) { } if e.CanAddr() { addr := e.UnsafeAddr() - if dumped[addr] { + if !dumped.Add(addr) { return "[dumped]", false } - dumped[addr] = true - defer delete(dumped, addr) + defer dumped.Remove(addr) } switch e.Kind() { case reflect.Bool, reflect.String, @@ -107,7 +107,7 @@ func dumpVar(v any) template.HTML { if setting.IsProd { return "
dumpVar: only available in dev mode
" } - m, ok := dumpVarMarshalable(v, map[uintptr]bool{}) + m, ok := dumpVarMarshalable(v, make(container.Set[uintptr])) var dumpStr string jsonBytes, err := json.MarshalIndent(m, "", " ") if err != nil { diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go index b8c7ca842a..24432ab6b2 100644 --- a/routers/api/actions/runner/utils.go +++ b/routers/api/actions/runner/utils.go @@ -10,6 +10,7 @@ import ( actions_model "code.gitea.io/gitea/models/actions" secret_model "code.gitea.io/gitea/models/secret" actions_module "code.gitea.io/gitea/modules/actions" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" @@ -197,10 +198,7 @@ func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[str if len(task.Job.Needs) == 0 { return nil, nil } - needs := map[string]struct{}{} - for _, v := range task.Job.Needs { - needs[v] = struct{}{} - } + needs := container.SetOf(task.Job.Needs...) jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID}) if err != nil { @@ -209,7 +207,7 @@ func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[str ret := make(map[string]*runnerv1.TaskNeed, len(needs)) for _, job := range jobs { - if _, ok := needs[job.JobID]; !ok { + if !needs.Contains(job.JobID) { continue } if job.TaskID == 0 || !job.Status.IsDone() { diff --git a/routers/web/user/home.go b/routers/web/user/home.go index c44e5a50af..a7f6a52f1b 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -567,12 +567,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { // Remove repositories that should not be shown, // which are repositories that have no issues and are not selected by the user. - selectedReposMap := make(map[int64]struct{}, len(selectedRepoIDs)) - for _, repoID := range selectedRepoIDs { - selectedReposMap[repoID] = struct{}{} - } + selectedRepos := container.SetOf(selectedRepoIDs...) for k, v := range issueCountByRepo { - if _, ok := selectedReposMap[k]; !ok && v == 0 { + if v == 0 && !selectedRepos.Contains(k) { delete(issueCountByRepo, k) } } diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go index 43ee32c84b..df5eb60393 100644 --- a/services/auth/source/ldap/source_sync.go +++ b/services/auth/source/ldap/source_sync.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/organization" user_model "code.gitea.io/gitea/models/user" auth_module "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" source_service "code.gitea.io/gitea/services/auth/source" @@ -41,7 +42,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { usernameUsers := make(map[string]*user_model.User, len(users)) mailUsers := make(map[string]*user_model.User, len(users)) - keepActiveUsers := make(map[int64]struct{}) + keepActiveUsers := make(container.Set[int64]) for _, u := range users { usernameUsers[u.LowerName] = u @@ -97,7 +98,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { } if usr != nil { - keepActiveUsers[usr.ID] = struct{}{} + keepActiveUsers.Add(usr.ID) } else if len(su.Username) == 0 { // we cannot create the user if su.Username is empty continue @@ -208,7 +209,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { // Deactivate users not present in LDAP if updateExisting { for _, usr := range users { - if _, ok := keepActiveUsers[usr.ID]; ok { + if keepActiveUsers.Contains(usr.ID) { continue } diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go index 76180a5159..f626036254 100644 --- a/services/migrations/gitlab.go +++ b/services/migrations/gitlab.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" base "code.gitea.io/gitea/modules/migration" "code.gitea.io/gitea/modules/structs" @@ -673,16 +674,15 @@ func (g *GitlabDownloader) GetReviews(reviewable base.Reviewable) ([]*base.Revie func (g *GitlabDownloader) awardsToReactions(awards []*gitlab.AwardEmoji) []*base.Reaction { result := make([]*base.Reaction, 0, len(awards)) - uniqCheck := make(map[string]struct{}) + uniqCheck := make(container.Set[string]) for _, award := range awards { uid := fmt.Sprintf("%s%d", award.Name, award.User.ID) - if _, ok := uniqCheck[uid]; !ok { + if uniqCheck.Add(uid) { result = append(result, &base.Reaction{ UserID: int64(award.User.ID), UserName: award.User.Username, Content: award.Name, }) - uniqCheck[uid] = struct{}{} } } return result From 2590707122aac20c53b554dc5695396ef35fbea4 Mon Sep 17 00:00:00 2001 From: delvh Date: Wed, 30 Aug 2023 12:37:17 +0200 Subject: [PATCH 59/60] Remove fomantic `text` module (#26777) Corollary to #26775: All selectors I found that are actually used and not necessarily present in the current code have been copied to `web_src/css/base.css`. Everything else should be a clean removal. --- web_src/css/base.css | 8 +- web_src/fomantic/build/semantic.css | 135 ---------------------------- web_src/fomantic/semantic.json | 3 +- 3 files changed, 5 insertions(+), 141 deletions(-) diff --git a/web_src/css/base.css b/web_src/css/base.css index c0176cc437..43fb2c9c75 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -1175,6 +1175,10 @@ img.ui.avatar, color: var(--color-gold) !important; } +.text.small { + font-size: 0.75em; +} + /* FIXME: this is a serious pollution, do not use this for "float: left" anymore */ .ui.left:not(.action) { float: left; @@ -1257,10 +1261,6 @@ img.ui.avatar, text-align: right !important; } -.ui .text.small { - font-size: 0.75em; -} - .ui .text.normal { font-weight: var(--font-weight-normal); } diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css index cddc3f5ee9..ad3f13bb58 100644 --- a/web_src/fomantic/build/semantic.css +++ b/web_src/fomantic/build/semantic.css @@ -19371,139 +19371,4 @@ input::selection { /******************************* Site Overrides -*******************************/ -/*! - * # Fomantic-UI - Text - * http://github.com/fomantic/Fomantic-UI/ - * - * - * Released under the MIT license - * https://github.com/fomantic/Fomantic-UI/blob/master/LICENSE.md - * - */ - -/******************************* - Text -*******************************/ - -span.ui.text { - line-height: 1; -} - -span.ui.primary.text { - color: #2185D0; -} - -span.ui.secondary.text { - color: #1B1C1D; -} - -span.ui.red.text { - color: #DB2828; -} - -span.ui.orange.text { - color: #F2711C; -} - -span.ui.yellow.text { - color: #FBBD08; -} - -span.ui.olive.text { - color: #B5CC18; -} - -span.ui.green.text { - color: #21BA45; -} - -span.ui.teal.text { - color: #00B5AD; -} - -span.ui.blue.text { - color: #2185D0; -} - -span.ui.violet.text { - color: #6435C9; -} - -span.ui.purple.text { - color: #A333C8; -} - -span.ui.pink.text { - color: #E03997; -} - -span.ui.brown.text { - color: #A5673F; -} - -span.ui.grey.text { - color: #767676; -} - -span.ui.black.text { - color: #1B1C1D; -} - -span.ui.error.text { - color: #DB2828; -} - -span.ui.info.text { - color: #31CCEC; -} - -span.ui.success.text { - color: #21BA45; -} - -span.ui.warning.text { - color: #F2C037; -} - -span.ui.disabled.text { - opacity: var(--opacity-disabled); -} - -/* Sizes */ - -span.ui.medium.text { - font-size: 1em; -} - -span.ui.mini.text { - font-size: 0.4em; -} - -span.ui.tiny.text { - font-size: 0.5em; -} - -span.ui.small.text { - font-size: 0.75em; -} - -span.ui.large.text { - font-size: 1.5em; -} - -span.ui.big.text { - font-size: 2em; -} - -span.ui.huge.text { - font-size: 4em; -} - -span.ui.massive.text { - font-size: 8em; -} - -/******************************* - Theme Overrides *******************************/ \ No newline at end of file diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json index eeb67f6097..43d0b412b3 100644 --- a/web_src/fomantic/semantic.json +++ b/web_src/fomantic/semantic.json @@ -40,7 +40,6 @@ "segment", "site", "tab", - "table", - "text" + "table" ] } From 1bb9b1c4d906010c47936bf0ceba82efd1c0c014 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 30 Aug 2023 21:46:24 +0800 Subject: [PATCH 60/60] Remove polluted ".ui.left" style (#26809) --- templates/repo/settings/collaboration.tmpl | 16 ++++------------ web_src/css/base.css | 5 ----- web_src/css/repo.css | 18 ------------------ web_src/js/components/DashboardRepoList.vue | 2 +- 4 files changed, 5 insertions(+), 36 deletions(-) diff --git a/templates/repo/settings/collaboration.tmpl b/templates/repo/settings/collaboration.tmpl index 13b2df0653..900d044171 100644 --- a/templates/repo/settings/collaboration.tmpl +++ b/templates/repo/settings/collaboration.tmpl @@ -41,12 +41,8 @@
{{.CsrfTokenHtml}} -
- + @@ -93,12 +89,8 @@ {{if $allowedToChangeTeams}}
{{.CsrfTokenHtml}} -
- + diff --git a/web_src/css/base.css b/web_src/css/base.css index 43fb2c9c75..43fcfe4632 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -1179,11 +1179,6 @@ img.ui.avatar, font-size: 0.75em; } -/* FIXME: this is a serious pollution, do not use this for "float: left" anymore */ -.ui.left:not(.action) { - float: left; -} - /* FIXME: this is a serious pollution, do not use this for "float: right" anymore */ .ui.right:not(.action) { float: right; diff --git a/web_src/css/repo.css b/web_src/css/repo.css index eb3f987741..bf6f0a7b5e 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -1907,24 +1907,6 @@ flex-wrap: wrap; } -.repository.settings.collaboration #repo-collab-form #search-user-box .results { - left: 7px; -} - -.repository.settings.collaboration #repo-collab-form .ui.button { - margin-left: 5px; - margin-top: -3px; -} - -.repository.settings.collaboration #repo-collab-team-form #search-team-box .results { - left: 7px; -} - -.repository.settings.collaboration #repo-collab-team-form .ui.button { - margin-left: 5px; - margin-top: -3px; -} - /* if the element is for a checkbox, then it should have a padding-left to align to the checkbox's text */ .repository.settings.branches .branch-protection .ui.checkbox .help, .repository.settings.branches .branch-protection .checkbox-sub-item { diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue index f1ea937f05..898362776b 100644 --- a/web_src/js/components/DashboardRepoList.vue +++ b/web_src/js/components/DashboardRepoList.vue @@ -15,7 +15,7 @@