From c7dcb58b1d96970959a5c8ac8d3955e4b7d027df Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 29 Feb 2024 22:16:02 +0800 Subject: [PATCH 1/4] Update FAQ about git hook problems (#29495) Close https://github.com/go-gitea/gitea/issues/29338#issuecomment-1970363817 --- docs/content/help/faq.en-us.md | 6 ++++-- docs/content/help/faq.zh-cn.md | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/content/help/faq.en-us.md b/docs/content/help/faq.en-us.md index 5ea2c10f5ee5..b3b09801256d 100644 --- a/docs/content/help/faq.en-us.md +++ b/docs/content/help/faq.en-us.md @@ -221,9 +221,11 @@ Our translations are currently crowd-sourced on our [Crowdin project](https://cr Whether you want to change a translation or add a new one, it will need to be there as all translations are overwritten in our CI via the Crowdin integration. -## Push Hook / Webhook aren't running +## Push Hook / Webhook / Actions aren't running -If you can push but can't see push activities on the home dashboard, or the push doesn't trigger webhook, there are a few possibilities: +If you can push but can't see push activities on the home dashboard, or the push doesn't trigger webhook and Actions workflows, it's likely that the git hooks are not working. + +There are a few possibilities: 1. The git hooks are out of sync: run "Resynchronize pre-receive, update and post-receive hooks of all repositories" on the site admin panel 2. The git repositories (and hooks) are stored on some filesystems (ex: mounted by NAS) which don't support script execution, make sure the filesystem supports `chmod a+x any-script` diff --git a/docs/content/help/faq.zh-cn.md b/docs/content/help/faq.zh-cn.md index b8dd3cd18035..25230df70b22 100644 --- a/docs/content/help/faq.zh-cn.md +++ b/docs/content/help/faq.zh-cn.md @@ -225,9 +225,11 @@ Gitea还提供了自己的SSH服务器,用于在SSHD不可用时使用。 无论您想要更改翻译还是添加新的翻译,都需要在Crowdin集成中进行,因为所有翻译都会被CI覆盖。 -## 推送钩子/ Webhook未运行 +## 推送钩子/ Webhook / Actions 未运行 -如果您可以推送但无法在主页仪表板上看到推送活动,或者推送不触发Webhook,有几种可能性: +如果您可以推送但无法在主页仪表板上看到推送活动,或者推送不触发 Webhook 和 Actions,可能是 git 钩子不工作而导致的。 + +这可能是由于以下原因: 1. Git钩子不同步:在站点管理面板上运行“重新同步所有仓库的pre-receive、update和post-receive钩子” 2. Git仓库(和钩子)存储在一些不支持脚本执行的文件系统上(例如由NAS挂载),请确保文件系统支持`chmod a+x any-script` From f6656181e4a07d6c415927220efa2077d509f7c6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 29 Feb 2024 19:52:49 +0100 Subject: [PATCH 2/4] migrate some more "OptionalBool" to "Option[bool]" (#29479) just some refactoring bits towards replacing **util.OptionalBool** with **optional.Option[bool]** --------- Co-authored-by: KN4CK3R --- models/repo/repo_list.go | 52 +++++++++---------- models/repo/repo_list_test.go | 83 +++++++++++++++---------------- models/user/email_address.go | 19 +++---- models/user/email_address_test.go | 6 +-- models/user/search.go | 34 ++++++------- models/user/user_test.go | 18 +++---- modules/indexer/issues/indexer.go | 4 +- modules/optional/option_test.go | 10 ++++ modules/util/util.go | 10 ++-- modules/util/util_test.go | 20 ++++---- routers/api/v1/repo/issue.go | 5 +- routers/api/v1/repo/repo.go | 26 +++++----- routers/web/admin/emails.go | 6 +-- routers/web/admin/users.go | 2 +- routers/web/explore/user.go | 4 +- routers/web/home.go | 4 +- routers/web/repo/issue.go | 5 +- routers/web/repo/repo.go | 24 ++++----- routers/web/shared/user/header.go | 3 +- routers/web/user/home.go | 9 ++-- routers/web/user/notification.go | 3 +- routers/web/user/profile.go | 7 +-- routers/web/user/search.go | 3 +- 23 files changed, 183 insertions(+), 174 deletions(-) diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 533ca5251fec..6b452291eabb 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -125,11 +126,11 @@ type SearchRepoOptions struct { // None -> include public and private // True -> include just private // False -> include just public - IsPrivate util.OptionalBool + IsPrivate optional.Option[bool] // None -> include collaborative AND non-collaborative // True -> include just collaborative // False -> include just non-collaborative - Collaborate util.OptionalBool + Collaborate optional.Option[bool] // What type of unit the user can be collaborative in, // it is ignored if Collaborate is False. // TypeInvalid means any unit type. @@ -137,19 +138,19 @@ type SearchRepoOptions struct { // None -> include forks AND non-forks // True -> include just forks // False -> include just non-forks - Fork util.OptionalBool + Fork optional.Option[bool] // None -> include templates AND non-templates // True -> include just templates // False -> include just non-templates - Template util.OptionalBool + Template optional.Option[bool] // None -> include mirrors AND non-mirrors // True -> include just mirrors // False -> include just non-mirrors - Mirror util.OptionalBool + Mirror optional.Option[bool] // None -> include archived AND non-archived // True -> include just archived // False -> include just non-archived - Archived util.OptionalBool + Archived optional.Option[bool] // only search topic name TopicOnly bool // only search repositories with specified primary language @@ -159,7 +160,7 @@ type SearchRepoOptions struct { // None -> include has milestones AND has no milestone // True -> include just has milestones // False -> include just has no milestone - HasMilestones util.OptionalBool + HasMilestones optional.Option[bool] // LowerNames represents valid lower names to restrict to LowerNames []string // When specified true, apply some filters over the conditions: @@ -359,12 +360,12 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { ))) } - if opts.IsPrivate != util.OptionalBoolNone { - cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.IsTrue()}) + if opts.IsPrivate.Has() { + cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.Value()}) } - if opts.Template != util.OptionalBoolNone { - cond = cond.And(builder.Eq{"is_template": opts.Template == util.OptionalBoolTrue}) + if opts.Template.Has() { + cond = cond.And(builder.Eq{"is_template": opts.Template.Value()}) } // Restrict to starred repositories @@ -380,11 +381,11 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { // Restrict repositories to those the OwnerID owns or contributes to as per opts.Collaborate if opts.OwnerID > 0 { accessCond := builder.NewCond() - if opts.Collaborate != util.OptionalBoolTrue { + if !opts.Collaborate.Value() { accessCond = builder.Eq{"owner_id": opts.OwnerID} } - if opts.Collaborate != util.OptionalBoolFalse { + if opts.Collaborate.ValueOrDefault(true) { // A Collaboration is: collaborateCond := builder.NewCond() @@ -472,31 +473,32 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true}))) } - if opts.Fork != util.OptionalBoolNone || opts.OnlyShowRelevant { - if opts.OnlyShowRelevant && opts.Fork == util.OptionalBoolNone { + if opts.Fork.Has() || opts.OnlyShowRelevant { + if opts.OnlyShowRelevant && !opts.Fork.Has() { cond = cond.And(builder.Eq{"is_fork": false}) } else { - cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue}) + cond = cond.And(builder.Eq{"is_fork": opts.Fork.Value()}) } } - if opts.Mirror != util.OptionalBoolNone { - cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue}) + if opts.Mirror.Has() { + cond = cond.And(builder.Eq{"is_mirror": opts.Mirror.Value()}) } if opts.Actor != nil && opts.Actor.IsRestricted { cond = cond.And(AccessibleRepositoryCondition(opts.Actor, unit.TypeInvalid)) } - if opts.Archived != util.OptionalBoolNone { - cond = cond.And(builder.Eq{"is_archived": opts.Archived == util.OptionalBoolTrue}) + if opts.Archived.Has() { + cond = cond.And(builder.Eq{"is_archived": opts.Archived.Value()}) } - switch opts.HasMilestones { - case util.OptionalBoolTrue: - cond = cond.And(builder.Gt{"num_milestones": 0}) - case util.OptionalBoolFalse: - cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"})) + if opts.HasMilestones.Has() { + if opts.HasMilestones.Value() { + cond = cond.And(builder.Gt{"num_milestones": 0}) + } else { + cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"})) + } } if opts.OnlyShowRelevant { diff --git a/models/repo/repo_list_test.go b/models/repo/repo_list_test.go index 83e37a27fdb1..88cfcde62083 100644 --- a/models/repo/repo_list_test.go +++ b/models/repo/repo_list_test.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "github.com/stretchr/testify/assert" ) @@ -27,62 +27,62 @@ func getTestCases() []struct { }{ { name: "PublicRepositoriesByName", - opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: optional.Some(false)}, count: 7, }, { name: "PublicAndPrivateRepositoriesByName", - opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: optional.Some(false)}, count: 14, }, { name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage", - opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: optional.Some(false)}, count: 14, }, { name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage", - opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: optional.Some(false)}, count: 14, }, { name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage", - opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)}, count: 14, }, { name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage", - opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)}, count: 14, }, { name: "PublicRepositoriesOfUser", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: optional.Some(false)}, count: 2, }, { name: "PublicRepositoriesOfUser2", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: optional.Some(false)}, count: 0, }, { name: "PublicRepositoriesOfOrg3", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: optional.Some(false)}, count: 2, }, { name: "PublicAndPrivateRepositoriesOfUser", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: optional.Some(false)}, count: 4, }, { name: "PublicAndPrivateRepositoriesOfUser2", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: optional.Some(false)}, count: 0, }, { name: "PublicAndPrivateRepositoriesOfOrg3", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: optional.Some(false)}, count: 4, }, { @@ -117,32 +117,32 @@ func getTestCases() []struct { }, { name: "PublicRepositoriesOfOrganization", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: optional.Some(false)}, count: 1, }, { name: "PublicAndPrivateRepositoriesOfOrganization", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: optional.Some(false)}, count: 2, }, { name: "AllPublic/PublicRepositoriesByName", - opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: optional.Some(false)}, count: 7, }, { name: "AllPublic/PublicAndPrivateRepositoriesByName", - opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: optional.Some(false)}, count: 14, }, { name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: optional.Some(false)}, count: 33, }, { name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: optional.Some(false)}, count: 38, }, { @@ -157,12 +157,12 @@ func getTestCases() []struct { }, { name: "AllPublic/PublicRepositoriesOfOrganization", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: optional.Some(false), Template: optional.Some(false)}, count: 33, }, { name: "AllTemplates", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: util.OptionalBoolTrue}, + opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: optional.Some(true)}, count: 2, }, { @@ -190,7 +190,7 @@ func TestSearchRepository(t *testing.T) { PageSize: 10, }, Keyword: "repo_12", - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), }) assert.NoError(t, err) @@ -205,7 +205,7 @@ func TestSearchRepository(t *testing.T) { PageSize: 10, }, Keyword: "test_repo", - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), }) assert.NoError(t, err) @@ -220,7 +220,7 @@ func TestSearchRepository(t *testing.T) { }, Keyword: "repo_13", Private: true, - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), }) assert.NoError(t, err) @@ -236,7 +236,7 @@ func TestSearchRepository(t *testing.T) { }, Keyword: "test_repo", Private: true, - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), }) assert.NoError(t, err) @@ -257,7 +257,7 @@ func TestSearchRepository(t *testing.T) { PageSize: 10, }, Keyword: "description_14", - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), IncludeDescription: true, }) @@ -274,7 +274,7 @@ func TestSearchRepository(t *testing.T) { PageSize: 10, }, Keyword: "description_14", - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), IncludeDescription: false, }) @@ -327,30 +327,25 @@ func TestSearchRepository(t *testing.T) { assert.False(t, repo.IsPrivate) } - if testCase.opts.Fork == util.OptionalBoolTrue && testCase.opts.Mirror == util.OptionalBoolTrue { - assert.True(t, repo.IsFork || repo.IsMirror) + if testCase.opts.Fork.Value() && testCase.opts.Mirror.Value() { + assert.True(t, repo.IsFork && repo.IsMirror) } else { - switch testCase.opts.Fork { - case util.OptionalBoolFalse: - assert.False(t, repo.IsFork) - case util.OptionalBoolTrue: - assert.True(t, repo.IsFork) + if testCase.opts.Fork.Has() { + assert.Equal(t, testCase.opts.Fork.Value(), repo.IsFork) } - switch testCase.opts.Mirror { - case util.OptionalBoolFalse: - assert.False(t, repo.IsMirror) - case util.OptionalBoolTrue: - assert.True(t, repo.IsMirror) + if testCase.opts.Mirror.Has() { + assert.Equal(t, testCase.opts.Mirror.Value(), repo.IsMirror) } } if testCase.opts.OwnerID > 0 && !testCase.opts.AllPublic { - switch testCase.opts.Collaborate { - case util.OptionalBoolFalse: - assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID) - case util.OptionalBoolTrue: - assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID) + if testCase.opts.Collaborate.Has() { + if testCase.opts.Collaborate.Value() { + assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID) + } else { + assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID) + } } } } diff --git a/models/user/email_address.go b/models/user/email_address.go index 9ddd1838d580..5d67304691ad 100644 --- a/models/user/email_address.go +++ b/models/user/email_address.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" @@ -416,8 +417,8 @@ type SearchEmailOptions struct { db.ListOptions Keyword string SortType SearchEmailOrderBy - IsPrimary util.OptionalBool - IsActivated util.OptionalBool + IsPrimary optional.Option[bool] + IsActivated optional.Option[bool] } // SearchEmailResult is an e-mail address found in the user or email_address table @@ -444,18 +445,12 @@ func SearchEmails(ctx context.Context, opts *SearchEmailOptions) ([]*SearchEmail )) } - switch { - case opts.IsPrimary.IsTrue(): - cond = cond.And(builder.Eq{"email_address.is_primary": true}) - case opts.IsPrimary.IsFalse(): - cond = cond.And(builder.Eq{"email_address.is_primary": false}) + if opts.IsPrimary.Has() { + cond = cond.And(builder.Eq{"email_address.is_primary": opts.IsPrimary.Value()}) } - switch { - case opts.IsActivated.IsTrue(): - cond = cond.And(builder.Eq{"email_address.is_activated": true}) - case opts.IsActivated.IsFalse(): - cond = cond.And(builder.Eq{"email_address.is_activated": false}) + if opts.IsActivated.Has() { + cond = cond.And(builder.Eq{"email_address.is_activated": opts.IsActivated.Value()}) } count, err := db.GetEngine(ctx).Join("INNER", "`user`", "`user`.ID = email_address.uid"). diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go index dc3073f98bd6..c2e010d95b3f 100644 --- a/models/user/email_address_test.go +++ b/models/user/email_address_test.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "github.com/stretchr/testify/assert" ) @@ -128,14 +128,14 @@ func TestListEmails(t *testing.T) { assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 27 })) // Must find only primary addresses (i.e. from the `user` table) - opts = &user_model.SearchEmailOptions{IsPrimary: util.OptionalBoolTrue} + opts = &user_model.SearchEmailOptions{IsPrimary: optional.Some(true)} emails, _, err = user_model.SearchEmails(db.DefaultContext, opts) assert.NoError(t, err) assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.IsPrimary })) assert.False(t, contains(func(s *user_model.SearchEmailResult) bool { return !s.IsPrimary })) // Must find only inactive addresses (i.e. not validated) - opts = &user_model.SearchEmailOptions{IsActivated: util.OptionalBoolFalse} + opts = &user_model.SearchEmailOptions{IsActivated: optional.Some(false)} emails, _, err = user_model.SearchEmails(db.DefaultContext, opts) assert.NoError(t, err) assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return !s.IsActivated })) diff --git a/models/user/search.go b/models/user/search.go index 9484bf442598..45b051187ea0 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -10,8 +10,8 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "xorm.io/builder" "xorm.io/xorm" @@ -33,11 +33,11 @@ type SearchUserOptions struct { SupportedSortOrders container.Set[string] // if not nil, only allow to use the sort orders in this set - IsActive util.OptionalBool - IsAdmin util.OptionalBool - IsRestricted util.OptionalBool - IsTwoFactorEnabled util.OptionalBool - IsProhibitLogin util.OptionalBool + IsActive optional.Option[bool] + IsAdmin optional.Option[bool] + IsRestricted optional.Option[bool] + IsTwoFactorEnabled optional.Option[bool] + IsProhibitLogin optional.Option[bool] IncludeReserved bool ExtraParamStrings map[string]string @@ -89,24 +89,24 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess cond = cond.And(builder.Eq{"login_name": opts.LoginName}) } - if !opts.IsActive.IsNone() { - cond = cond.And(builder.Eq{"is_active": opts.IsActive.IsTrue()}) + if opts.IsActive.Has() { + cond = cond.And(builder.Eq{"is_active": opts.IsActive.Value()}) } - if !opts.IsAdmin.IsNone() { - cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.IsTrue()}) + if opts.IsAdmin.Has() { + cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()}) } - if !opts.IsRestricted.IsNone() { - cond = cond.And(builder.Eq{"is_restricted": opts.IsRestricted.IsTrue()}) + if opts.IsRestricted.Has() { + cond = cond.And(builder.Eq{"is_restricted": opts.IsRestricted.Value()}) } - if !opts.IsProhibitLogin.IsNone() { - cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.IsTrue()}) + if opts.IsProhibitLogin.Has() { + cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.Value()}) } e := db.GetEngine(ctx) - if opts.IsTwoFactorEnabled.IsNone() { + if !opts.IsTwoFactorEnabled.Has() { return e.Where(cond) } @@ -114,7 +114,7 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess // While using LEFT JOIN, sometimes the performance might not be good, but it won't be a problem now, such SQL is seldom executed. // There are some possible methods to refactor this SQL in future when we really need to optimize the performance (but not now): // (1) add a column in user table (2) add a setting value in user_setting table (3) use search engines (bleve/elasticsearch) - if opts.IsTwoFactorEnabled.IsTrue() { + if opts.IsTwoFactorEnabled.Value() { cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL")) } else { cond = cond.And(builder.Expr("two_factor.uid IS NULL")) @@ -131,7 +131,7 @@ func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _ defer sessCount.Close() count, err := sessCount.Count(new(User)) if err != nil { - return nil, 0, fmt.Errorf("Count: %w", err) + return nil, 0, fmt.Errorf("count: %w", err) } if len(opts.OrderBy) == 0 { diff --git a/models/user/user_test.go b/models/user/user_test.go index 68cee9cdbd16..f522f743d59c 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -16,10 +16,10 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password/hash" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) @@ -103,29 +103,29 @@ func TestSearchUsers(t *testing.T) { testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}}, []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40}) - testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolFalse}, + testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)}, []int64{9}) - testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, + testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40}) - testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, + testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) // order by name asc default - testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, + testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) - testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: util.OptionalBoolTrue}, + testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)}, []int64{1}) - testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: util.OptionalBoolTrue}, + testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)}, []int64{29}) - testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: util.OptionalBoolTrue}, + testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)}, []int64{37}) - testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: util.OptionalBoolTrue}, + testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)}, []int64{24}) } diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 57037d2947c6..e3bc21b49d85 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -20,10 +20,10 @@ import ( "code.gitea.io/gitea/modules/indexer/issues/internal" "code.gitea.io/gitea/modules/indexer/issues/meilisearch" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" ) // IndexerMetadata is used to send data to the queue, so it contains only the ids. @@ -220,7 +220,7 @@ func PopulateIssueIndexer(ctx context.Context) error { ListOptions: db_model.ListOptions{Page: page, PageSize: repo_model.RepositoryListDefaultPageSize}, OrderBy: db_model.SearchOrderByID, Private: true, - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), }) if err != nil { log.Error("SearchRepositoryByName: %v", err) diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go index 410fd7357765..4f55608004f8 100644 --- a/modules/optional/option_test.go +++ b/modules/optional/option_test.go @@ -27,6 +27,16 @@ func TestOption(t *testing.T) { assert.Equal(t, int(1), some.Value()) assert.Equal(t, int(1), some.ValueOrDefault(2)) + noneBool := optional.None[bool]() + assert.False(t, noneBool.Has()) + assert.False(t, noneBool.Value()) + assert.True(t, noneBool.ValueOrDefault(true)) + + someBool := optional.Some(true) + assert.True(t, someBool.Has()) + assert.True(t, someBool.Value()) + assert.True(t, someBool.ValueOrDefault(false)) + var ptr *int assert.False(t, optional.FromPtr(ptr).Has()) diff --git a/modules/util/util.go b/modules/util/util.go index 28b549f40553..615f654e4752 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -68,13 +68,13 @@ func OptionalBoolOf(b bool) OptionalBool { return OptionalBoolFalse } -// OptionalBoolParse get the corresponding OptionalBool of a string using strconv.ParseBool -func OptionalBoolParse(s string) OptionalBool { - b, e := strconv.ParseBool(s) +// OptionalBoolParse get the corresponding optional.Option[bool] of a string using strconv.ParseBool +func OptionalBoolParse(s string) optional.Option[bool] { + v, e := strconv.ParseBool(s) if e != nil { - return OptionalBoolNone + return optional.None[bool]() } - return OptionalBoolOf(b) + return optional.Some(v) } // IsEmptyString checks if the provided string is empty diff --git a/modules/util/util_test.go b/modules/util/util_test.go index c5830ce01cb2..819e12ee91ff 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -8,6 +8,8 @@ import ( "strings" "testing" + "code.gitea.io/gitea/modules/optional" + "github.com/stretchr/testify/assert" ) @@ -173,17 +175,17 @@ func Test_RandomBytes(t *testing.T) { assert.NotEqual(t, bytes3, bytes4) } -func Test_OptionalBool(t *testing.T) { - assert.Equal(t, OptionalBoolNone, OptionalBoolParse("")) - assert.Equal(t, OptionalBoolNone, OptionalBoolParse("x")) +func TestOptionalBoolParse(t *testing.T) { + assert.Equal(t, optional.None[bool](), OptionalBoolParse("")) + assert.Equal(t, optional.None[bool](), OptionalBoolParse("x")) - assert.Equal(t, OptionalBoolFalse, OptionalBoolParse("0")) - assert.Equal(t, OptionalBoolFalse, OptionalBoolParse("f")) - assert.Equal(t, OptionalBoolFalse, OptionalBoolParse("False")) + assert.Equal(t, optional.Some(false), OptionalBoolParse("0")) + assert.Equal(t, optional.Some(false), OptionalBoolParse("f")) + assert.Equal(t, optional.Some(false), OptionalBoolParse("False")) - assert.Equal(t, OptionalBoolTrue, OptionalBoolParse("1")) - assert.Equal(t, OptionalBoolTrue, OptionalBoolParse("t")) - assert.Equal(t, OptionalBoolTrue, OptionalBoolParse("True")) + assert.Equal(t, optional.Some(true), OptionalBoolParse("1")) + assert.Equal(t, optional.Some(true), OptionalBoolParse("t")) + assert.Equal(t, optional.Some(true), OptionalBoolParse("True")) } // Test case for any function which accepts and returns a single string. diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index efc1a08a0501..227e0e725cfb 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -142,7 +143,7 @@ func SearchIssues(ctx *context.APIContext) { Private: false, AllPublic: true, TopicOnly: false, - Collaborate: util.OptionalBoolNone, + Collaborate: optional.None[bool](), // This needs to be a column that is not nil in fixtures or // MySQL will return different results when sorting by null in some cases OrderBy: db.SearchOrderByAlphabetically, @@ -165,7 +166,7 @@ func SearchIssues(ctx *context.APIContext) { opts.OwnerID = owner.ID opts.AllLimited = false opts.AllPublic = false - opts.Collaborate = util.OptionalBoolFalse + opts.Collaborate = optional.Some(false) } if ctx.FormString("team") != "" { if ctx.FormString("owner") == "" { diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index da443bbf18cc..6fde73a4e8f8 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -24,10 +24,10 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" @@ -135,33 +135,33 @@ func Search(ctx *context.APIContext) { PriorityOwnerID: ctx.FormInt64("priority_owner_id"), TeamID: ctx.FormInt64("team_id"), TopicOnly: ctx.FormBool("topic"), - Collaborate: util.OptionalBoolNone, + Collaborate: optional.None[bool](), Private: ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private")), - Template: util.OptionalBoolNone, + Template: optional.None[bool](), StarredByID: ctx.FormInt64("starredBy"), IncludeDescription: ctx.FormBool("includeDesc"), } if ctx.FormString("template") != "" { - opts.Template = util.OptionalBoolOf(ctx.FormBool("template")) + opts.Template = optional.Some(ctx.FormBool("template")) } if ctx.FormBool("exclusive") { - opts.Collaborate = util.OptionalBoolFalse + opts.Collaborate = optional.Some(false) } mode := ctx.FormString("mode") switch mode { case "source": - opts.Fork = util.OptionalBoolFalse - opts.Mirror = util.OptionalBoolFalse + opts.Fork = optional.Some(false) + opts.Mirror = optional.Some(false) case "fork": - opts.Fork = util.OptionalBoolTrue + opts.Fork = optional.Some(true) case "mirror": - opts.Mirror = util.OptionalBoolTrue + opts.Mirror = optional.Some(true) case "collaborative": - opts.Mirror = util.OptionalBoolFalse - opts.Collaborate = util.OptionalBoolTrue + opts.Mirror = optional.Some(false) + opts.Collaborate = optional.Some(true) case "": default: ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid search mode: \"%s\"", mode)) @@ -169,11 +169,11 @@ func Search(ctx *context.APIContext) { } if ctx.FormString("archived") != "" { - opts.Archived = util.OptionalBoolOf(ctx.FormBool("archived")) + opts.Archived = optional.Some(ctx.FormBool("archived")) } if ctx.FormString("is_private") != "" { - opts.IsPrivate = util.OptionalBoolOf(ctx.FormBool("is_private")) + opts.IsPrivate = optional.Some(ctx.FormBool("is_private")) } sortMode := ctx.FormString("sort") diff --git a/routers/web/admin/emails.go b/routers/web/admin/emails.go index 4296d70aeee1..2cf4035c6a80 100644 --- a/routers/web/admin/emails.go +++ b/routers/web/admin/emails.go @@ -12,8 +12,8 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" ) @@ -68,10 +68,10 @@ func Emails(ctx *context.Context) { opts.Keyword = ctx.FormTrim("q") opts.SortType = orderBy if len(ctx.FormString("is_activated")) != 0 { - opts.IsActivated = util.OptionalBoolOf(ctx.FormBool("activated")) + opts.IsActivated = optional.Some(ctx.FormBool("activated")) } if len(ctx.FormString("is_primary")) != 0 { - opts.IsPrimary = util.OptionalBoolOf(ctx.FormBool("primary")) + opts.IsPrimary = optional.Some(ctx.FormBool("primary")) } if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) { diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index cbca26eba8c8..bbdbc820d782 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -276,7 +276,7 @@ func ViewUser(ctx *context.Context) { OwnerID: u.ID, OrderBy: db.SearchOrderByAlphabetically, Private: true, - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), }) if err != nil { ctx.ServerError("SearchRepository", err) diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index 41f440f9d963..b79a79fb2c86 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -12,10 +12,10 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sitemap" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" ) @@ -155,7 +155,7 @@ func Users(ctx *context.Context) { Actor: ctx.Doer, Type: user_model.UserTypeIndividual, ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, SupportedSortOrders: supportedSortOrders, diff --git a/routers/web/home.go b/routers/web/home.go index 555f94c9835d..d4be0931e850 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -13,10 +13,10 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sitemap" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/routers/web/auth" "code.gitea.io/gitea/routers/web/user" @@ -71,7 +71,7 @@ func HomeSitemap(ctx *context.Context) { _, cnt, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ Type: user_model.UserTypeIndividual, ListOptions: db.ListOptions{PageSize: 1}, - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), Visible: []structs.VisibleType{structs.VisibleTypePublic}, }) if err != nil { diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 65e74a2d90e3..d13c658d0522 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -38,6 +38,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -2519,7 +2520,7 @@ func SearchIssues(ctx *context.Context) { Private: false, AllPublic: true, TopicOnly: false, - Collaborate: util.OptionalBoolNone, + Collaborate: optional.None[bool](), // This needs to be a column that is not nil in fixtures or // MySQL will return different results when sorting by null in some cases OrderBy: db.SearchOrderByAlphabetically, @@ -2542,7 +2543,7 @@ func SearchIssues(ctx *context.Context) { opts.OwnerID = owner.ID opts.AllLimited = false opts.AllPublic = false - opts.Collaborate = util.OptionalBoolFalse + opts.Collaborate = optional.Some(false) } if ctx.FormString("team") != "" { if ctx.FormString("owner") == "" { diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 94e9d1267c47..49779efa3758 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -553,33 +553,33 @@ func SearchRepo(ctx *context.Context) { PriorityOwnerID: ctx.FormInt64("priority_owner_id"), TeamID: ctx.FormInt64("team_id"), TopicOnly: ctx.FormBool("topic"), - Collaborate: util.OptionalBoolNone, + Collaborate: optional.None[bool](), Private: ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private")), - Template: util.OptionalBoolNone, + Template: optional.None[bool](), StarredByID: ctx.FormInt64("starredBy"), IncludeDescription: ctx.FormBool("includeDesc"), } if ctx.FormString("template") != "" { - opts.Template = util.OptionalBoolOf(ctx.FormBool("template")) + opts.Template = optional.Some(ctx.FormBool("template")) } if ctx.FormBool("exclusive") { - opts.Collaborate = util.OptionalBoolFalse + opts.Collaborate = optional.Some(false) } mode := ctx.FormString("mode") switch mode { case "source": - opts.Fork = util.OptionalBoolFalse - opts.Mirror = util.OptionalBoolFalse + opts.Fork = optional.Some(false) + opts.Mirror = optional.Some(false) case "fork": - opts.Fork = util.OptionalBoolTrue + opts.Fork = optional.Some(true) case "mirror": - opts.Mirror = util.OptionalBoolTrue + opts.Mirror = optional.Some(true) case "collaborative": - opts.Mirror = util.OptionalBoolFalse - opts.Collaborate = util.OptionalBoolTrue + opts.Mirror = optional.Some(false) + opts.Collaborate = optional.Some(true) case "": default: ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid search mode: \"%s\"", mode)) @@ -587,11 +587,11 @@ func SearchRepo(ctx *context.Context) { } if ctx.FormString("archived") != "" { - opts.Archived = util.OptionalBoolOf(ctx.FormBool("archived")) + opts.Archived = optional.Some(ctx.FormBool("archived")) } if ctx.FormString("is_private") != "" { - opts.IsPrivate = util.OptionalBoolOf(ctx.FormBool("is_private")) + opts.IsPrivate = optional.Some(ctx.FormBool("is_private")) } sortMode := ctx.FormString("sort") diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 2253b8840d93..51b04e0613a5 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" @@ -114,7 +115,7 @@ func LoadHeaderCount(ctx *context.Context) error { Actor: ctx.Doer, OwnerID: ctx.ContextUser.ID, Private: ctx.IsSigned, - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), IncludeDescription: setting.UI.SearchRepoDescription, }) if err != nil { diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 78548e6df7a1..6f36806ff755 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -28,6 +28,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/web/feed" @@ -161,8 +162,8 @@ func Milestones(ctx *context.Context) { Private: true, AllPublic: false, // Include also all public repositories of users and public organisations AllLimited: false, // Include also all public repositories of limited organisations - Archived: util.OptionalBoolFalse, - HasMilestones: util.OptionalBoolTrue, // Just needs display repos has milestones + Archived: optional.Some(false), + HasMilestones: optional.Some(true), // Just needs display repos has milestones } if ctxUser.IsOrganization() && ctx.Org.Team != nil { @@ -465,9 +466,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { Private: true, AllPublic: false, AllLimited: false, - Collaborate: util.OptionalBoolNone, + Collaborate: optional.None[bool](), UnitType: unitType, - Archived: util.OptionalBoolFalse, + Archived: optional.Some(false), } if team != nil { repoOpts.TeamID = team.ID diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 05034f8efaba..801e1cf95eaf 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -17,6 +17,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -399,7 +400,7 @@ func NotificationWatching(ctx *context.Context) { OrderBy: orderBy, Private: ctx.IsSigned, WatchedByID: ctx.Doer.ID, - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), TopicOnly: ctx.FormBool("topic"), IncludeDescription: setting.UI.SearchRepoDescription, }) diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index e7890b7c1209..b9b069b0d4c9 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/web/feed" @@ -203,7 +204,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb OrderBy: orderBy, Private: ctx.IsSigned, StarredByID: ctx.ContextUser.ID, - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), TopicOnly: topicOnly, Language: language, IncludeDescription: setting.UI.SearchRepoDescription, @@ -225,7 +226,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb OrderBy: orderBy, Private: ctx.IsSigned, WatchedByID: ctx.ContextUser.ID, - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), TopicOnly: topicOnly, Language: language, IncludeDescription: setting.UI.SearchRepoDescription, @@ -270,7 +271,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb OwnerID: ctx.ContextUser.ID, OrderBy: orderBy, Private: ctx.IsSigned, - Collaborate: util.OptionalBoolFalse, + Collaborate: optional.Some(false), TopicOnly: topicOnly, Language: language, IncludeDescription: setting.UI.SearchRepoDescription, diff --git a/routers/web/user/search.go b/routers/web/user/search.go index 5ef61c88d45e..fb7729bbe141 100644 --- a/routers/web/user/search.go +++ b/routers/web/user/search.go @@ -8,7 +8,6 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) @@ -25,7 +24,7 @@ func Search(ctx *context.Context) { Keyword: ctx.FormTrim("q"), UID: ctx.FormInt64("uid"), Type: user_model.UserTypeIndividual, - IsActive: util.OptionalBoolFromGeneric(ctx.FormOptionalBool("active")), + IsActive: ctx.FormOptionalBool("active"), ListOptions: listOptions, }) if err != nil { From a6eb298098983b7aae028fff4d80d15d5510f10b Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Fri, 1 Mar 2024 00:27:12 +0000 Subject: [PATCH 3/4] [skip ci] Updated translations via Crowdin --- options/locale/locale_cs-CZ.ini | 36 +++++++++++++++++++++++++++++++++ options/locale/locale_zh-CN.ini | 4 +++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index d30103a8eb8f..2a421b11721e 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -123,6 +123,7 @@ pin=Připnout unpin=Odepnout artifacts=Artefakty +confirm_delete_artifact=Jste si jisti, že chcete odstranit artefakt „%s“? archived=Archivováno @@ -423,6 +424,7 @@ authorization_failed_desc=Autorizace selhala, protože jsme detekovali neplatný sspi_auth_failed=SSPI autentizace selhala password_pwned=Heslo, které jste zvolili, je na seznamu odcizených hesel, která byla dříve odhalena při narušení veřejných dat. Zkuste to prosím znovu s jiným heslem. password_pwned_err=Nelze dokončit požadavek na HaveIBeenPwned +last_admin=Nelze odstranit posledního správce. Musí existovat alespoň jeden správce. [mail] view_it_on=Zobrazit na %s @@ -588,6 +590,7 @@ org_still_own_packages=Organizace stále vlastní jeden nebo více balíčků. N target_branch_not_exist=Cílová větev neexistuje. +admin_cannot_delete_self=Nemůžete se smazat, dokud jste správce. Nejdříve prosím odeberte svá administrátorská oprávnění. [user] change_avatar=Změnit váš avatar… @@ -967,6 +970,8 @@ issue_labels_helper=Vyberte sadu štítků úkolů. license=Licence license_helper=Vyberte licenční soubor. license_helper_desc=Licence řídí, co ostatní mohou a nemohou dělat s vaším kódem. Nejste si jisti, která je pro váš projekt správná? Podívejte se na Zvolte licenci +object_format=Formát objektu +object_format_helper=Objektový formát repozitáře. Nelze později změnit. SHA1 je nejvíce kompatibilní. readme=README readme_helper=Vyberte šablonu souboru README. readme_helper_desc=Toto je místo, kde můžete napsat úplný popis vašeho projektu. @@ -1033,6 +1038,7 @@ desc.public=Veřejný desc.template=Šablona desc.internal=Interní desc.archived=Archivováno +desc.sha256=SHA256 template.items=Položky šablony template.git_content=Obsah gitu (výchozí větev) @@ -1183,6 +1189,8 @@ audio_not_supported_in_browser=Váš prohlížeč nepodporuje značku pro HTML5 stored_lfs=Uloženo pomocí Git LFS symbolic_link=Symbolický odkaz executable_file=Spustitelný soubor +vendored=Vendorováno +generated=Generováno commit_graph=Graf commitů commit_graph.select=Vybrat větve commit_graph.hide_pr_refs=Skrýt požadavky na natažení @@ -1518,7 +1526,11 @@ issues.label_title=Název štítku issues.label_description=Popis štítku issues.label_color=Barva štítku issues.label_exclusive=Exkluzivní +issues.label_archive=Archivovat štítek issues.label_archived_filter=Zobrazit archivované popisky +issues.label_archive_tooltip=Archivované štítky jsou ve výchozím nastavení vyloučeny z návrhů při hledání podle popisku. +issues.label_exclusive_desc=Pojmenujte štítek rozsah/položka, aby se stal vzájemně exkluzivním s jinými štítky rozsah/. +issues.label_exclusive_warning=Jakékoliv protichůdné rozsahy štítků budou odstraněny při úpravě štítků u úkolů nebo u požadavku na natažení. issues.label_count=%d štítků issues.label_open_issues=%d otevřených úkolů issues.label_edit=Upravit @@ -1619,6 +1631,7 @@ issues.dependency.issue_closing_blockedby=Uzavření tohoto úkolu je blokováno issues.dependency.issue_close_blocks=Tento úkol blokuje uzavření následujících úkolů issues.dependency.pr_close_blocks=Tento požadavek na natažení blokuje uzavření následujících úkolů issues.dependency.issue_close_blocked=Musíte zavřít všechny úkoly, které blokují tento úkol, aby jej bylo možné zavřít. +issues.dependency.issue_batch_close_blocked=Nelze uzavřít úkoly, které jste vybrali, protože úkol #%d má stále otevřené závislosti issues.dependency.pr_close_blocked=Musíte zavřít všechny úkoly, které blokují tento požadavek na natažení, aby jej bylo možné sloučit. issues.dependency.blocks_short=Blokuje issues.dependency.blocked_by_short=Závisí na @@ -1700,6 +1713,7 @@ pulls.select_commit_hold_shift_for_range=Vyberte commit. Podržte klávesu shift pulls.review_only_possible_for_full_diff=Posouzení je možné pouze při zobrazení plného rozlišení pulls.filter_changes_by_commit=Filtrovat podle commitu pulls.nothing_to_compare=Tyto větve jsou stejné. Není potřeba vytvářet požadavek na natažení. +pulls.nothing_to_compare_have_tag=Vybraná větev/značka je stejná. pulls.nothing_to_compare_and_allow_empty_pr=Tyto větve jsou stejné. Tento požadavek na natažení bude prázdný. pulls.has_pull_request=`Požadavek na natažení mezi těmito větvemi již existuje: %[2]s#%[3]d` pulls.create=Vytvořit požadavek na natažení @@ -1822,6 +1836,7 @@ milestones.update_ago=Aktualizováno %s milestones.no_due_date=Bez lhůty dokončení milestones.open=Otevřít milestones.close=Zavřít +milestones.new_subheader=Milníky vám pomohou organizovat úkoly a sledovat jejich pokrok. milestones.completeness=%d%% Dokončeno milestones.create=Vytvořit milník milestones.title=Název @@ -1955,6 +1970,7 @@ activity.git_stats_and_deletions=a activity.git_stats_deletion_1=%d odebrání activity.git_stats_deletion_n=%d odebrání +contributors.contribution_type.filter_label=Typ příspěvku: contributors.contribution_type.commits=Commity search=Vyhledat @@ -2341,6 +2357,7 @@ settings.matrix.room_id=ID místnosti settings.matrix.message_type=Typ zprávy settings.archive.button=Archivovat repozitář settings.archive.header=Archivovat tento repozitář +settings.archive.text=Archivace repozitáře způsobí, že bude zcela určen pouze pro čtení. Bude skryt z ovládacího panelu. Nikdo (ani vy!) nebude moci vytvářet nové revize ani otevírat nové úkoly nebo žádosti o natažení. settings.archive.success=Repozitář byl úspěšně archivován. settings.archive.error=Nastala chyba při archivování repozitáře. Prohlédněte si záznam pro více detailů. settings.archive.error_ismirror=Nemůžete archivovat zrcadlený repozitář. @@ -2545,6 +2562,11 @@ error.csv.unexpected=Tento soubor nelze vykreslit, protože obsahuje neočekáva error.csv.invalid_field_count=Soubor nelze vykreslit, protože má nesprávný počet polí na řádku %d. [graphs] +component_loading=Načítání %s... +component_loading_failed=Nelze načíst %s +component_loading_info=Může to chvíli trvat… +component_failed_to_load=Došlo k neočekávané chybě. +contributors.what=příspěvky [org] org_name_holder=Název organizace @@ -2715,6 +2737,7 @@ dashboard.delete_repo_archives.started=Spuštěna úloha smazání všech archiv dashboard.delete_missing_repos=Smazat všechny repozitáře, které nemají Git soubory dashboard.delete_missing_repos.started=Spuštěna úloha mazání všech repozitářů, které nemají Git soubory. dashboard.delete_generated_repository_avatars=Odstranit vygenerované avatary repozitářů +dashboard.sync_repo_tags=Synchronizovat značky z git dat do databáze dashboard.update_mirrors=Aktualizovat zrcadla dashboard.repo_health_check=Kontrola stavu všech repozitářů dashboard.check_repo_stats=Zkontrolovat všechny statistiky repositáře @@ -2762,11 +2785,14 @@ dashboard.delete_old_actions=Odstranit všechny staré akce z databáze dashboard.delete_old_actions.started=Začalo odstraňování všech starých akcí z databáze. dashboard.update_checker=Kontrola aktualizací dashboard.delete_old_system_notices=Odstranit všechna stará systémová upozornění z databáze +dashboard.gc_lfs=Úklid LFS meta objektů dashboard.stop_zombie_tasks=Zastavit zombie úlohy dashboard.stop_endless_tasks=Zastavit nekonečné úlohy dashboard.cancel_abandoned_jobs=Zrušit opuštěné úlohy dashboard.start_schedule_tasks=Spustit naplánované úlohy dashboard.sync_branch.started=Synchronizace větví byla spuštěna +dashboard.sync_tag.started=Synchronizace značek spuštěna +dashboard.rebuild_issue_indexer=Znovu sestavit index úkolů users.user_manage_panel=Správa uživatelských účtů users.new_account=Vytvořit uživatelský účet @@ -3184,6 +3210,12 @@ notices.desc=Popis notices.op=Akce notices.delete_success=Systémové upozornění bylo smazáno. +self_check.no_problem_found=Zatím nebyl nalezen žádný problém. +self_check.database_collation_mismatch=Očekávejte, že databáze použije collation: %s +self_check.database_collation_case_insensitive=Databáze používá collation %s, což je collation nerozlišující velká a malá písmena. Ačkoli s ní Gitea může pracovat, mohou se vyskytnout vzácné případy, kdy nebude fungovat podle očekávání. +self_check.database_inconsistent_collation_columns=Databáze používá collation %s, ale tyto sloupce používají chybné collation. To může způsobit neočekávané problémy. +self_check.database_fix_mysql=Pro uživatele MySQL/MariaDB můžete použít příkaz "gitea doctor convert", který opraví problémy s collation, nebo můžete také problém vyřešit příkazem "ALTER ... COLLATE ..." SQL ručně. +self_check.database_fix_mssql=Uživatelé MSSQL mohou problém vyřešit pouze pomocí příkazu "ALTER ... COLLATE ..." SQL ručně. [action] create_repo=vytvořil/a repozitář %s @@ -3371,6 +3403,7 @@ rpm.distros.suse=na distribuce založené na SUSE rpm.install=Pro instalaci balíčku spusťte následující příkaz: rpm.repository=Informace o repozitáři rpm.repository.architectures=Architektury +rpm.repository.multiple_groups=Tento balíček je k dispozici ve více skupinách. rubygems.install=Pro instalaci balíčku pomocí gem spusťte následující příkaz: rubygems.install2=nebo ho přidejte do Gemfie: rubygems.dependencies.runtime=Běhové závislosti @@ -3498,6 +3531,8 @@ runs.actors_no_select=Všichni aktéři runs.status_no_select=Všechny stavy runs.no_results=Nebyly nalezeny žádné výsledky. runs.no_workflows=Zatím neexistují žádné pracovní postupy. +runs.no_workflows.quick_start=Nevíte jak začít s Gitea Actions? Podívejte se na průvodce rychlým startem. +runs.no_workflows.documentation=Další informace o Gitea Actions naleznete v dokumentaci. runs.no_runs=Pracovní postup zatím nebyl spuštěn. runs.empty_commit_message=(prázdná zpráva commitu) @@ -3515,6 +3550,7 @@ variables.none=Zatím nejsou žádné proměnné. variables.deletion=Odstranit proměnnou variables.deletion.description=Odstranění proměnné je trvalé a nelze jej vrátit zpět. Pokračovat? variables.description=Proměnné budou předány určitým akcím a nelze je přečíst jinak. +variables.id_not_exist=Proměnná s ID %d neexistuje. variables.edit=Upravit proměnnou variables.deletion.failed=Nepodařilo se odstranit proměnnou. variables.deletion.success=Proměnná byla odstraněna. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 7c8153cbb133..89f237a11757 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -2119,7 +2119,7 @@ settings.trust_model.collaborator.long=协作者:信任协作者的签名 settings.trust_model.collaborator.desc=此仓库中协作者的有效签名将被标记为「可信」(无论它们是否是提交者),签名只符合提交者时将标记为「不可信」,都不匹配时标记为「不匹配」。 settings.trust_model.committer=提交者 settings.trust_model.committer.long=提交者: 信任与提交者相符的签名 (此特性类似 GitHub,这会强制采用 Gitea 作为提交者和签名者) -settings.trust_model.committer.desc=有效签名只有和提交者相匹配才会被标记为“受信任”,否则它们将被标记为“不匹配”。这强制 Gitea 成为签名提交的提交者,而实际提交者被加上 Co-authored-by: 和 Co-committed-by: 的标记。 默认的 Gitea 密钥必须撇撇数据库种的一名用户。 +settings.trust_model.committer.desc=有效签名只有和提交者相匹配才会被标记为“受信任”,否则它们将被标记为“不匹配”。这强制 Gitea 成为签名提交的提交者,而实际提交者被加上 Co-authored-by: 和 Co-committed-by: 的标记。 默认的 Gitea 密钥必须匹配数据库中的一名用户。 settings.trust_model.collaboratorcommitter=协作者+提交者 settings.trust_model.collaboratorcommitter.long=协作者+提交者:信任协作者同时是提交者的签名 settings.trust_model.collaboratorcommitter.desc=此仓库中协作者的有效签名在他同时是提交者时将被标记为「可信」,签名只匹配了提交者时将标记为「不可信」,都不匹配时标记为「不匹配」。这会强制 Gitea 成为签名者和提交者,实际的提交者将被标记于提交消息结尾处的「Co-Authored-By:」和「Co-Committed-By:」。默认的 Gitea 签名密钥必须匹配数据库中的一个用户密钥。 @@ -3250,7 +3250,9 @@ notices.delete_success=系统通知已被删除。 self_check.no_problem_found=尚未发现问题。 self_check.database_collation_mismatch=期望数据库使用的校验方式:%s self_check.database_collation_case_insensitive=数据库正在使用一个校验 %s, 这是一个不敏感的校验. 虽然Gitea可以与它合作,但可能有一些罕见的情况不如预期的那样起作用。 +self_check.database_inconsistent_collation_columns=数据库正在使用%s的排序规则,但是这些列使用了不匹配的排序规则。这可能会造成一些意外问题。 self_check.database_fix_mysql=对于MySQL/MariaDB用户,您可以使用“gitea doctor convert”命令来解决校验问题。 或者您也可以通过 "ALTER ... COLLATE ..." 这样的SQL 来手动解决这个问题。 +self_check.database_fix_mssql=对于MSSQL用户,您现在只能通过"ALTER ... COLLATE ..."SQLs手动解决这个问题。 [action] create_repo=创建了仓库 %s From 5e32cd6beb1a4f11bd19e6d44ba2a50828b686ef Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Thu, 29 Feb 2024 20:43:42 -0500 Subject: [PATCH 4/4] =?UTF-8?q?Don=E2=80=99t=20comment=20when=20locking=20?= =?UTF-8?q?(#29508)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reduces the number of emails/notifications on outdated issues. Co-authored-by: John Olheiser --- .github/workflows/cron-lock.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/cron-lock.yml b/.github/workflows/cron-lock.yml index d4172687d54d..665313135b67 100644 --- a/.github/workflows/cron-lock.yml +++ b/.github/workflows/cron-lock.yml @@ -20,8 +20,4 @@ jobs: - uses: dessant/lock-threads@v5 with: issue-inactive-days: 10 - issue-comment: | - Automatically locked because of our [CONTRIBUTING guidelines](https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md#issue-locking) pr-inactive-days: 7 - pr-comment: | - Automatically locked because of our [CONTRIBUTING guidelines](https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md#issue-locking)