From cbc7091c243fdde8d6639d395558a6171a15342c Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Tue, 9 May 2023 18:26:56 +0800 Subject: [PATCH 01/29] add new field --- models/git/protected_branch.go | 15 ++++++++------- routers/web/repo/setting_protected_branch.go | 2 ++ services/forms/repo_form.go | 1 + templates/repo/settings/protected_branch.tmpl | 2 ++ 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go index eef7e3935af87..b5600491df12b 100644 --- a/models/git/protected_branch.go +++ b/models/git/protected_branch.go @@ -37,13 +37,14 @@ type ProtectedBranch struct { isPlainName bool `xorm:"-"` CanPush bool `xorm:"NOT NULL DEFAULT false"` EnableWhitelist bool - WhitelistUserIDs []int64 `xorm:"JSON TEXT"` - WhitelistTeamIDs []int64 `xorm:"JSON TEXT"` - EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"` - WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"` - MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"` - MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"` - EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"` + WhitelistUserIDs []int64 `xorm:"JSON TEXT"` + WhitelistTeamIDs []int64 `xorm:"JSON TEXT"` + EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"` + WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"` + MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"` + MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"` + EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"` + StatusCheckPattern string StatusCheckContexts []string `xorm:"JSON TEXT"` EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"` ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"` diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 932bd373d7ae1..459c21991b10e 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -130,6 +130,8 @@ func SettingsProtectedBranch(c *context.Context) { } c.Data["branch_status_check_contexts"] = contexts + c.Data["branch_status_check_pattern"] = "" + if c.Repo.Owner.IsOrganization() { teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead) if err != nil { diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 41d7dc7d2b0a9..7a8024de6628a 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -199,6 +199,7 @@ type ProtectBranchForm struct { MergeWhitelistUsers string MergeWhitelistTeams string EnableStatusCheck bool + StatusCheckPattern string StatusCheckContexts []string RequiredApprovals int64 EnableApprovalsWhitelist bool diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 494cadfcc1bee..5f6d2becb3fcf 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -157,6 +157,8 @@
+ + From b51490d2cdc127e81222a01d733548926cb40ff8 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 10 May 2023 10:07:46 +0800 Subject: [PATCH 02/29] add migration --- models/git/protected_branch.go | 16 ++++++++-------- models/migrations/migrations.go | 2 ++ models/migrations/v1_20/v257.go | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 models/migrations/v1_20/v257.go diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go index b5600491df12b..67239019b771b 100644 --- a/models/git/protected_branch.go +++ b/models/git/protected_branch.go @@ -37,14 +37,14 @@ type ProtectedBranch struct { isPlainName bool `xorm:"-"` CanPush bool `xorm:"NOT NULL DEFAULT false"` EnableWhitelist bool - WhitelistUserIDs []int64 `xorm:"JSON TEXT"` - WhitelistTeamIDs []int64 `xorm:"JSON TEXT"` - EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"` - WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"` - MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"` - MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"` - EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"` - StatusCheckPattern string + WhitelistUserIDs []int64 `xorm:"JSON TEXT"` + WhitelistTeamIDs []int64 `xorm:"JSON TEXT"` + EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"` + WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"` + MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"` + MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"` + EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"` + StatusCheckPattern string `xorm:"TEXT"` StatusCheckContexts []string `xorm:"JSON TEXT"` EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"` ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"` diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 0e84ae9f0e29a..ef976fa3f93aa 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -491,6 +491,8 @@ var migrations = []Migration{ NewMigration("Add ArchivedUnix Column", v1_20.AddArchivedUnixToRepository), // v256 -> v257 NewMigration("Add is_internal column to package", v1_20.AddIsInternalColumnToPackage), + // v257 -> v258 + NewMigration("Add status_check_pattern to protected_branch", v1_20.AddStatusCheckPatternToProtectedBranch), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_20/v257.go b/models/migrations/v1_20/v257.go new file mode 100644 index 0000000000000..4664f8c6c9fee --- /dev/null +++ b/models/migrations/v1_20/v257.go @@ -0,0 +1,14 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_20 //nolint + +import "xorm.io/xorm" + +func AddStatusCheckPatternToProtectedBranch(x *xorm.Engine) error { + type ProtectedBranch struct { + StatusCheckPattern string `xorm:"TEXT"` + } + + return x.Sync(new(ProtectedBranch)) +} From 99866d4c41b89e5d28a51511135e493f801351fb Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 10 May 2023 17:08:08 +0800 Subject: [PATCH 03/29] improve MergeRequiredContextsCommitStatus --- routers/web/repo/pull.go | 7 +++++- routers/web/repo/setting_protected_branch.go | 4 +++- services/pull/commit_status.go | 23 +++++++++++++++---- templates/repo/settings/protected_branch.tmpl | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 070fc109dcec9..203188c205ed3 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -45,6 +45,8 @@ import ( "code.gitea.io/gitea/services/gitdiff" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" + + "github.com/gobwas/glob" ) const ( @@ -579,9 +581,12 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C return true } } + if gp, err := glob.Compile(pb.StatusCheckPattern); err == nil { + return gp.Match(context) + } return false } - ctx.Data["RequiredStatusCheckState"] = pull_service.MergeRequiredContextsCommitStatus(commitStatuses, pb.StatusCheckContexts) + ctx.Data["RequiredStatusCheckState"] = pull_service.MergeRequiredContextsCommitStatus(commitStatuses, pb.StatusCheckContexts, pb.StatusCheckPattern) } ctx.Data["HeadBranchMovedOn"] = headBranchSha != sha diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 459c21991b10e..4726abcf4e7bc 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -115,6 +115,7 @@ func SettingsProtectedBranch(c *context.Context) { c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(rule.WhitelistUserIDs), ",") c.Data["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistUserIDs), ",") c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",") + c.Data["status_check_pattern"] = rule.StatusCheckPattern contexts, _ := git_model.FindRepoRecentCommitStatusContexts(c, c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts for _, ctx := range rule.StatusCheckContexts { var found bool @@ -130,7 +131,6 @@ func SettingsProtectedBranch(c *context.Context) { } c.Data["branch_status_check_contexts"] = contexts - c.Data["branch_status_check_pattern"] = "" if c.Repo.Owner.IsOrganization() { teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead) @@ -240,8 +240,10 @@ func SettingsProtectedBranchPost(ctx *context.Context) { protectBranch.EnableStatusCheck = f.EnableStatusCheck if f.EnableStatusCheck { protectBranch.StatusCheckContexts = f.StatusCheckContexts + protectBranch.StatusCheckPattern = f.StatusCheckPattern } else { protectBranch.StatusCheckContexts = nil + protectBranch.StatusCheckPattern = "" } protectBranch.RequiredApprovals = f.RequiredApprovals diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index bfdb3f7291b82..a49cd9216b1ca 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -10,15 +10,30 @@ import ( "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/structs" + "github.com/gobwas/glob" "github.com/pkg/errors" ) // MergeRequiredContextsCommitStatus returns a commit status state for given required contexts -func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, requiredContexts []string) structs.CommitStatusState { - if len(requiredContexts) == 0 { +func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, requiredContexts []string, statusCheckPattern string) structs.CommitStatusState { + allRequiredContexts := container.SetOf(requiredContexts...) + if len(statusCheckPattern) > 0 { + if gp, err := glob.Compile(statusCheckPattern); err != nil { + return structs.CommitStatusError + } else { + for _, commitStatus := range commitStatuses { + if gp.Match(commitStatus.Context) { + allRequiredContexts.Add(commitStatus.Context) + } + } + } + } + + if len(allRequiredContexts) == 0 { status := git_model.CalcCommitStatus(commitStatuses) if status != nil { return status.State @@ -27,7 +42,7 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, } returnedStatus := structs.CommitStatusSuccess - for _, ctx := range requiredContexts { + for _, ctx := range allRequiredContexts.Values() { var targetStatus structs.CommitStatusState for _, commitStatus := range commitStatuses { if commitStatus.Context == ctx { @@ -147,5 +162,5 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR requiredContexts = pb.StatusCheckContexts } - return MergeRequiredContextsCommitStatus(commitStatuses, requiredContexts), nil + return MergeRequiredContextsCommitStatus(commitStatuses, requiredContexts, pb.StatusCheckPattern), nil } diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 5f6d2becb3fcf..f97a088d53c84 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -158,7 +158,7 @@
- +
From a1d40c67af589634cd1d34121fbc92927203d9c3 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 10 May 2023 17:19:19 +0800 Subject: [PATCH 04/29] format --- routers/web/repo/setting_protected_branch.go | 1 - 1 file changed, 1 deletion(-) diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 4726abcf4e7bc..84512db5f9b9a 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -131,7 +131,6 @@ func SettingsProtectedBranch(c *context.Context) { } c.Data["branch_status_check_contexts"] = contexts - if c.Repo.Owner.IsOrganization() { teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead) if err != nil { From e9245b61aeb2ee6b404371108c7852db7a4ce12d Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Thu, 11 May 2023 10:03:05 +0800 Subject: [PATCH 05/29] update js --- templates/repo/settings/protected_branch.tmpl | 2 +- web_src/js/features/repo-settings.js | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index f97a088d53c84..299f2a457657c 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -158,7 +158,7 @@
- +
diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 7105a14b30265..840d583fc7bd6 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import {debounce} from 'throttle-debounce'; import {createMonaco} from './codeeditor.js'; const {appSubUrl, csrfToken} = window.config; @@ -81,4 +82,12 @@ export function initRepoSettingBranches() { const $target = $($(this).attr('data-target')); if (this.checked) $target.addClass('disabled'); // only disable, do not auto enable }); + + // mark all status checks that match the pattern + const statusCheckPatternInput = document.getElementById('status_check_pattern'); + if (statusCheckPatternInput) { + statusCheckPatternInput.addEventListener('input', debounce(200, (e) => { + // TODO + })); + } } From 9facea89e4dca30ad626fd243d75ebec13b23c29 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Thu, 11 May 2023 11:01:18 +0800 Subject: [PATCH 06/29] reuse StatusCheckContexts --- models/git/protected_branch.go | 1 - models/migrations/migrations.go | 2 -- models/migrations/v1_20/v257.go | 14 -------- routers/web/repo/pull.go | 10 +++--- routers/web/repo/setting_protected_branch.go | 4 +-- services/pull/commit_status.go | 34 ++++++++----------- templates/repo/settings/protected_branch.tmpl | 5 ++- 7 files changed, 24 insertions(+), 46 deletions(-) delete mode 100644 models/migrations/v1_20/v257.go diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go index 67239019b771b..eef7e3935af87 100644 --- a/models/git/protected_branch.go +++ b/models/git/protected_branch.go @@ -44,7 +44,6 @@ type ProtectedBranch struct { MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"` MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"` EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"` - StatusCheckPattern string `xorm:"TEXT"` StatusCheckContexts []string `xorm:"JSON TEXT"` EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"` ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"` diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index ef976fa3f93aa..0e84ae9f0e29a 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -491,8 +491,6 @@ var migrations = []Migration{ NewMigration("Add ArchivedUnix Column", v1_20.AddArchivedUnixToRepository), // v256 -> v257 NewMigration("Add is_internal column to package", v1_20.AddIsInternalColumnToPackage), - // v257 -> v258 - NewMigration("Add status_check_pattern to protected_branch", v1_20.AddStatusCheckPatternToProtectedBranch), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_20/v257.go b/models/migrations/v1_20/v257.go deleted file mode 100644 index 4664f8c6c9fee..0000000000000 --- a/models/migrations/v1_20/v257.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_20 //nolint - -import "xorm.io/xorm" - -func AddStatusCheckPatternToProtectedBranch(x *xorm.Engine) error { - type ProtectedBranch struct { - StatusCheckPattern string `xorm:"TEXT"` - } - - return x.Sync(new(ProtectedBranch)) -} diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 203188c205ed3..e36f60dc50783 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -577,16 +577,14 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C if pb != nil && pb.EnableStatusCheck { ctx.Data["is_context_required"] = func(context string) bool { for _, c := range pb.StatusCheckContexts { - if c == context { - return true + if gp, err := glob.Compile(c); err == nil { + return gp.Match(context) } } - if gp, err := glob.Compile(pb.StatusCheckPattern); err == nil { - return gp.Match(context) - } + return false } - ctx.Data["RequiredStatusCheckState"] = pull_service.MergeRequiredContextsCommitStatus(commitStatuses, pb.StatusCheckContexts, pb.StatusCheckPattern) + ctx.Data["RequiredStatusCheckState"] = pull_service.MergeRequiredContextsCommitStatus(commitStatuses, pb.StatusCheckContexts) } ctx.Data["HeadBranchMovedOn"] = headBranchSha != sha diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 84512db5f9b9a..3a31f52f33597 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -115,7 +115,7 @@ func SettingsProtectedBranch(c *context.Context) { c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(rule.WhitelistUserIDs), ",") c.Data["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistUserIDs), ",") c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",") - c.Data["status_check_pattern"] = rule.StatusCheckPattern + c.Data["status_check_pattern"] = strings.Join(rule.StatusCheckContexts, ";") contexts, _ := git_model.FindRepoRecentCommitStatusContexts(c, c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts for _, ctx := range rule.StatusCheckContexts { var found bool @@ -239,10 +239,8 @@ func SettingsProtectedBranchPost(ctx *context.Context) { protectBranch.EnableStatusCheck = f.EnableStatusCheck if f.EnableStatusCheck { protectBranch.StatusCheckContexts = f.StatusCheckContexts - protectBranch.StatusCheckPattern = f.StatusCheckPattern } else { protectBranch.StatusCheckContexts = nil - protectBranch.StatusCheckPattern = "" } protectBranch.RequiredApprovals = f.RequiredApprovals diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index a49cd9216b1ca..b756bab7ceab1 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -10,8 +10,8 @@ import ( "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" "github.com/gobwas/glob" @@ -19,21 +19,8 @@ import ( ) // MergeRequiredContextsCommitStatus returns a commit status state for given required contexts -func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, requiredContexts []string, statusCheckPattern string) structs.CommitStatusState { - allRequiredContexts := container.SetOf(requiredContexts...) - if len(statusCheckPattern) > 0 { - if gp, err := glob.Compile(statusCheckPattern); err != nil { - return structs.CommitStatusError - } else { - for _, commitStatus := range commitStatuses { - if gp.Match(commitStatus.Context) { - allRequiredContexts.Add(commitStatus.Context) - } - } - } - } - - if len(allRequiredContexts) == 0 { +func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, requiredContexts []string) structs.CommitStatusState { + if len(requiredContexts) == 0 { status := git_model.CalcCommitStatus(commitStatuses) if status != nil { return status.State @@ -41,11 +28,20 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, return structs.CommitStatusSuccess } + requiredContextsGlob := make(map[string]glob.Glob, len(requiredContexts)) + for _, ctx := range requiredContexts { + if gp, err := glob.Compile(ctx); err != nil { + log.Error("") + } else { + requiredContextsGlob[ctx] = gp + } + } + returnedStatus := structs.CommitStatusSuccess - for _, ctx := range allRequiredContexts.Values() { + for ctx, gp := range requiredContextsGlob { var targetStatus structs.CommitStatusState for _, commitStatus := range commitStatuses { - if commitStatus.Context == ctx { + if gp.Match(commitStatus.Context) { targetStatus = commitStatus.State break } @@ -162,5 +158,5 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR requiredContexts = pb.StatusCheckContexts } - return MergeRequiredContextsCommitStatus(commitStatuses, requiredContexts, pb.StatusCheckPattern), nil + return MergeRequiredContextsCommitStatus(commitStatuses, requiredContexts), nil } diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 299f2a457657c..aeb2fca33e3d9 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -158,7 +158,7 @@
- +
@@ -169,10 +169,13 @@ {{range $.branch_status_check_contexts}} {{else}} From 5de7b0155d6a89d9625d5d658a4d958f0893ca10 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Thu, 11 May 2023 12:14:18 +0800 Subject: [PATCH 07/29] check status with patterns --- options/locale/locale_en-US.ini | 1 + routers/web/repo/pull.go | 5 ++-- routers/web/repo/setting_protected_branch.go | 23 +++++++++++++++++-- services/forms/repo_form.go | 3 +-- templates/repo/settings/protected_branch.tmpl | 8 +------ 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index f825220839fa7..333afabc72b29 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2223,6 +2223,7 @@ settings.edit_protected_branch = Edit settings.protected_branch_required_rule_name = Required rule name settings.protected_branch_duplicate_rule_name = Duplicate rule name settings.protected_branch_required_approvals_min = Required approvals cannot be negative. +settings.protected_branch_invalid_status_check_pattern = Invalid status check pattern: "%s". settings.tags = Tags settings.tags.protection = Tag Protection settings.tags.protection.pattern = Tag Pattern diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index e36f60dc50783..85f19ce22c0dc 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -578,10 +578,11 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C ctx.Data["is_context_required"] = func(context string) bool { for _, c := range pb.StatusCheckContexts { if gp, err := glob.Compile(c); err == nil { - return gp.Match(context) + if gp.Match(context) { + return true + } } } - return false } ctx.Data["RequiredStatusCheckState"] = pull_service.MergeRequiredContextsCommitStatus(commitStatuses, pb.StatusCheckContexts) diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 3a31f52f33597..0dba7c27c65c3 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -23,6 +23,8 @@ import ( "code.gitea.io/gitea/services/forms" pull_service "code.gitea.io/gitea/services/pull" "code.gitea.io/gitea/services/repository" + + "github.com/gobwas/glob" ) const ( @@ -118,9 +120,13 @@ func SettingsProtectedBranch(c *context.Context) { c.Data["status_check_pattern"] = strings.Join(rule.StatusCheckContexts, ";") contexts, _ := git_model.FindRepoRecentCommitStatusContexts(c, c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts for _, ctx := range rule.StatusCheckContexts { + gb, err := glob.Compile(ctx) + if err != nil { + log.Error("") + } var found bool for i := range contexts { - if contexts[i] == ctx { + if gb.Match(contexts[i]) { found = true break } @@ -238,7 +244,20 @@ func SettingsProtectedBranchPost(ctx *context.Context) { protectBranch.EnableStatusCheck = f.EnableStatusCheck if f.EnableStatusCheck { - protectBranch.StatusCheckContexts = f.StatusCheckContexts + statusCheckContexts := strings.Split(f.StatusCheckContexts, ";") + validStatusCheckContexts := make([]string, 0, len(statusCheckContexts)) + for _, c := range statusCheckContexts { + if c == "" { + continue + } + if _, err := glob.Compile(c); err != nil { + ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_invalid_status_check_pattern", c)) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, protectBranch.RuleName)) + return + } + validStatusCheckContexts = append(validStatusCheckContexts, c) + } + protectBranch.StatusCheckContexts = validStatusCheckContexts } else { protectBranch.StatusCheckContexts = nil } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 7a8024de6628a..d705ecad3f7db 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -199,8 +199,7 @@ type ProtectBranchForm struct { MergeWhitelistUsers string MergeWhitelistTeams string EnableStatusCheck bool - StatusCheckPattern string - StatusCheckContexts []string + StatusCheckContexts string RequiredApprovals int64 EnableApprovalsWhitelist bool ApprovalsWhitelistUsers string diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index aeb2fca33e3d9..a5daa8e620046 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -158,7 +158,7 @@
- +
+ +
@@ -170,12 +170,6 @@ {{else}} From 4d2bbede09a6dc6bf3b85d7be3ce9135ad56f0bb Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Thu, 11 May 2023 12:28:46 +0800 Subject: [PATCH 08/29] fix error log --- routers/web/repo/pull.go | 8 ++++---- services/pull/commit_status.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 85f19ce22c0dc..dad84a0dd198f 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -577,10 +577,10 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C if pb != nil && pb.EnableStatusCheck { ctx.Data["is_context_required"] = func(context string) bool { for _, c := range pb.StatusCheckContexts { - if gp, err := glob.Compile(c); err == nil { - if gp.Match(context) { - return true - } + if gp, err := glob.Compile(c); err != nil { + log.Error("glob.Compile %s failed. Error: %v", c, err) + } else if gp.Match(context) { + return true } } return false diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index b756bab7ceab1..704b012596534 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -31,7 +31,7 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, requiredContextsGlob := make(map[string]glob.Glob, len(requiredContexts)) for _, ctx := range requiredContexts { if gp, err := glob.Compile(ctx); err != nil { - log.Error("") + log.Error("glob.Compile %s failed. Error: %v", ctx, err) } else { requiredContextsGlob[ctx] = gp } From ea328409e313400d3d15466e8ba774ce785ba102 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Thu, 11 May 2023 17:17:02 +0800 Subject: [PATCH 09/29] improve js --- options/locale/locale_en-US.ini | 3 +- package-lock.json | 197 +++++++++++++++--- package.json | 1 + routers/web/repo/setting_protected_branch.go | 7 +- templates/repo/settings/protected_branch.tmpl | 5 +- web_src/css/repository.css | 5 + web_src/js/features/repo-settings.js | 31 ++- 7 files changed, 204 insertions(+), 45 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 333afabc72b29..de593fbcf8c3b 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2185,6 +2185,8 @@ settings.protect_merge_whitelist_teams = Whitelisted teams for merging: settings.protect_check_status_contexts = Enable Status Check settings.protect_check_status_contexts_desc = Require status checks to pass before merging. Choose which status checks must pass before branches can be merged into a branch that matches this rule. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are selected, the last commit must be successful regardless of context. settings.protect_check_status_contexts_list = Status checks found in the last week for this repository +settings.protect_status_check_matched = Matched +settings.protect_invalid_status_check_pattern = Invalid status check pattern: "%s". settings.protect_required_approvals = Required approvals: settings.protect_required_approvals_desc = Allow only to merge pull request with enough positive reviews. settings.protect_approvals_whitelist_enabled = Restrict approvals to whitelisted users or teams @@ -2223,7 +2225,6 @@ settings.edit_protected_branch = Edit settings.protected_branch_required_rule_name = Required rule name settings.protected_branch_duplicate_rule_name = Duplicate rule name settings.protected_branch_required_approvals_min = Required approvals cannot be negative. -settings.protected_branch_invalid_status_check_pattern = Invalid status check pattern: "%s". settings.tags = Tags settings.tags.protection = Tag Protection settings.tags.protection.pattern = Tag Pattern diff --git a/package-lock.json b/package-lock.json index 93c5775e00ba0..ddf1cdc3d48f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "license-checker-webpack-plugin": "0.2.1", "mermaid": "10.1.0", "mini-css-extract-plugin": "2.7.5", + "minimatch": "9.0.0", "monaco-editor": "0.38.0", "monaco-editor-webpack-plugin": "7.0.1", "pretty-ms": "8.0.0", @@ -828,12 +829,34 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { "version": "8.40.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz", @@ -880,6 +903,28 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1453,6 +1498,28 @@ "node": "^12.20 || >=14.13" } }, + "node_modules/@stoplight/spectral-core/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@stoplight/spectral-core/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@stoplight/spectral-formats": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.5.0.tgz", @@ -2624,12 +2691,11 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -4530,6 +4596,16 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -4551,6 +4627,18 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -4710,12 +4798,34 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/espree": { "version": "9.5.2", "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", @@ -5216,6 +5326,26 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -6324,6 +6454,26 @@ "webpack": "^4.4.0 || ^5.4.0" } }, + "node_modules/license-checker-webpack-plugin/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/license-checker-webpack-plugin/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/license-checker-webpack-plugin/node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -6572,15 +6722,6 @@ "node": ">=14" } }, - "node_modules/markdownlint-cli/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/markdownlint-cli/node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -6618,21 +6759,6 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, - "node_modules/markdownlint-cli/node_modules/minimatch": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", - "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/markdownlint-micromark": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.2.tgz", @@ -6866,14 +6992,17 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz", + "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { diff --git a/package.json b/package.json index 9d9c0923ef8ed..e75f5d07b9d28 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "license-checker-webpack-plugin": "0.2.1", "mermaid": "10.1.0", "mini-css-extract-plugin": "2.7.5", + "minimatch": "9.0.0", "monaco-editor": "0.38.0", "monaco-editor-webpack-plugin": "7.0.1", "pretty-ms": "8.0.0", diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 0dba7c27c65c3..a3d03443c6953 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -117,12 +117,13 @@ func SettingsProtectedBranch(c *context.Context) { c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(rule.WhitelistUserIDs), ",") c.Data["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistUserIDs), ",") c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",") - c.Data["status_check_pattern"] = strings.Join(rule.StatusCheckContexts, ";") + c.Data["status_check_contexts"] = strings.Join(rule.StatusCheckContexts, ";") contexts, _ := git_model.FindRepoRecentCommitStatusContexts(c, c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts for _, ctx := range rule.StatusCheckContexts { gb, err := glob.Compile(ctx) if err != nil { - log.Error("") + log.Error("glob.Compile %s failed. Error: %v", ctx, err) + continue } var found bool for i := range contexts { @@ -251,7 +252,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) { continue } if _, err := glob.Compile(c); err != nil { - ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_invalid_status_check_pattern", c)) + ctx.Flash.Error(ctx.Tr("repo.settings.protect_invalid_status_check_pattern", c)) ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, protectBranch.RuleName)) return } diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index a5daa8e620046..5a2b289aa2c8d 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -158,7 +158,7 @@
- +
-
@@ -169,7 +169,8 @@ {{range $.branch_status_check_contexts}} {{else}} diff --git a/web_src/css/repository.css b/web_src/css/repository.css index 0d81d65110689..00b26f7db3149 100644 --- a/web_src/css/repository.css +++ b/web_src/css/repository.css @@ -2013,6 +2013,11 @@ padding-left: 26px; } +.repository.settings.branches .branch-protection .status-check-matched-mark { + font-weight: var(--font-weight-bold); + font-style: italic; +} + .repository.settings.webhook .events .column { padding-bottom: 0; } diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 840d583fc7bd6..d7fa9660ab1b9 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -1,5 +1,6 @@ import $ from 'jquery'; import {debounce} from 'throttle-debounce'; +import {minimatch} from 'minimatch'; import {createMonaco} from './codeeditor.js'; const {appSubUrl, csrfToken} = window.config; @@ -83,11 +84,31 @@ export function initRepoSettingBranches() { if (this.checked) $target.addClass('disabled'); // only disable, do not auto enable }); - // mark all status checks that match the pattern - const statusCheckPatternInput = document.getElementById('status_check_pattern'); + // show the `Matched` mark for the status check contexts that match the pattern + const markMatchedStatusCheckContexts = function() { + const matchedMarks = document.getElementsByClassName('status-check-matched-mark'); + const statusCheckContexts = Array.from(matchedMarks).map(el => el.getAttribute('data-status-check-context')); + const patterns = document.getElementById('status_check_contexts').value.split(';'); + const matchedStatusCheckContexts = new Set(); + for (const context of statusCheckContexts) { + for (const pattern of patterns) { + if (minimatch(context, pattern)) { + matchedStatusCheckContexts.add(context); + break; + } + } + } + for (const el of matchedMarks) { + if (matchedStatusCheckContexts.has(el.getAttribute('data-status-check-context'))) { + el.classList.remove('gt-hidden'); + } else { + el.classList.add('gt-hidden'); + } + } + }; + markMatchedStatusCheckContexts(); + const statusCheckPatternInput = document.getElementById('status_check_contexts'); if (statusCheckPatternInput) { - statusCheckPatternInput.addEventListener('input', debounce(200, (e) => { - // TODO - })); + statusCheckPatternInput.addEventListener('input', debounce(200, markMatchedStatusCheckContexts)); } } From d4fd091812c45c43168f18a4f92b1d289cd7769a Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Thu, 11 May 2023 17:52:06 +0800 Subject: [PATCH 10/29] lint --- web_src/js/features/repo-settings.js | 29 +++++++++++----------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index d7fa9660ab1b9..49f476ab7bff8 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -1,5 +1,4 @@ import $ from 'jquery'; -import {debounce} from 'throttle-debounce'; import {minimatch} from 'minimatch'; import {createMonaco} from './codeeditor.js'; @@ -85,30 +84,24 @@ export function initRepoSettingBranches() { }); // show the `Matched` mark for the status check contexts that match the pattern - const markMatchedStatusCheckContexts = function() { - const matchedMarks = document.getElementsByClassName('status-check-matched-mark'); - const statusCheckContexts = Array.from(matchedMarks).map(el => el.getAttribute('data-status-check-context')); - const patterns = document.getElementById('status_check_contexts').value.split(';'); - const matchedStatusCheckContexts = new Set(); - for (const context of statusCheckContexts) { + const statusCheckPatternInput = document.getElementById('status_check_contexts'); + statusCheckPatternInput?.addEventListener('input', (e) => { + const patterns = e.target.value.split(';'); + const marks = document.getElementsByClassName('status-check-matched-mark'); + for (const el of marks) { + let matched = false; + const statusCheckContext = el.getAttribute('data-status-check-context'); for (const pattern of patterns) { - if (minimatch(context, pattern)) { - matchedStatusCheckContexts.add(context); + if (minimatch(statusCheckContext, pattern)) { + matched = true; break; } } - } - for (const el of matchedMarks) { - if (matchedStatusCheckContexts.has(el.getAttribute('data-status-check-context'))) { + if (matched) { el.classList.remove('gt-hidden'); } else { el.classList.add('gt-hidden'); } } - }; - markMatchedStatusCheckContexts(); - const statusCheckPatternInput = document.getElementById('status_check_contexts'); - if (statusCheckPatternInput) { - statusCheckPatternInput.addEventListener('input', debounce(200, markMatchedStatusCheckContexts)); - } + }); } From c4bd798477aa8b472d588a63922ccf563e6a87b6 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Thu, 11 May 2023 18:07:40 +0800 Subject: [PATCH 11/29] add can_context_match --- routers/web/repo/setting_protected_branch.go | 23 +++++++------------ templates/repo/settings/protected_branch.tmpl | 2 +- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index a3d03443c6953..4d7723597f7ad 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -119,25 +119,18 @@ func SettingsProtectedBranch(c *context.Context) { c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",") c.Data["status_check_contexts"] = strings.Join(rule.StatusCheckContexts, ";") contexts, _ := git_model.FindRepoRecentCommitStatusContexts(c, c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts - for _, ctx := range rule.StatusCheckContexts { - gb, err := glob.Compile(ctx) - if err != nil { - log.Error("glob.Compile %s failed. Error: %v", ctx, err) - continue - } - var found bool - for i := range contexts { - if gb.Match(contexts[i]) { - found = true - break + c.Data["branch_status_check_contexts"] = contexts + c.Data["can_context_match"] = func(context string) bool { + for _, c := range rule.StatusCheckContexts { + if gp, err := glob.Compile(c); err != nil { + log.Error("glob.Compile %s failed. Error: %v", c, err) + } else if gp.Match(context) { + return true } } - if !found { - contexts = append(contexts, ctx) - } + return false } - c.Data["branch_status_check_contexts"] = contexts if c.Repo.Owner.IsOrganization() { teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead) if err != nil { diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 5a2b289aa2c8d..58a37948210b9 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -170,7 +170,7 @@ {{else}} From 8f73c908ddf8fedbc850b39d813e6a4f3bf0b692 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Thu, 11 May 2023 18:29:38 +0800 Subject: [PATCH 12/29] update translation --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index de593fbcf8c3b..cdcec6747ab32 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2183,7 +2183,7 @@ settings.protect_merge_whitelist_committers_desc = Allow only whitelisted users settings.protect_merge_whitelist_users = Whitelisted users for merging: settings.protect_merge_whitelist_teams = Whitelisted teams for merging: settings.protect_check_status_contexts = Enable Status Check -settings.protect_check_status_contexts_desc = Require status checks to pass before merging. Choose which status checks must pass before branches can be merged into a branch that matches this rule. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are selected, the last commit must be successful regardless of context. +settings.protect_check_status_contexts_desc = "Require status checks to pass before merging. Enter patterns to specify which status checks must pass before branches can be merged into a branch that matches this rule. Multiple patterns can be separated using semicolon (';'). When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are selected, the last commit must be successful regardless of context." settings.protect_check_status_contexts_list = Status checks found in the last week for this repository settings.protect_status_check_matched = Matched settings.protect_invalid_status_check_pattern = Invalid status check pattern: "%s". From af76f0618ea82b3b5c291b0c88ccca33e27b732a Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 11:03:06 +0800 Subject: [PATCH 13/29] replace input with textarea --- routers/web/repo/setting_protected_branch.go | 18 ++++++++++-------- templates/repo/settings/protected_branch.tmpl | 2 +- web_src/js/features/repo-settings.js | 9 +++++---- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 4d7723597f7ad..1169a031e3462 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -117,7 +117,7 @@ func SettingsProtectedBranch(c *context.Context) { c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(rule.WhitelistUserIDs), ",") c.Data["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistUserIDs), ",") c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",") - c.Data["status_check_contexts"] = strings.Join(rule.StatusCheckContexts, ";") + c.Data["status_check_contexts"] = strings.Join(rule.StatusCheckContexts, "\n") contexts, _ := git_model.FindRepoRecentCommitStatusContexts(c, c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts c.Data["branch_status_check_contexts"] = contexts c.Data["can_context_match"] = func(context string) bool { @@ -238,20 +238,22 @@ func SettingsProtectedBranchPost(ctx *context.Context) { protectBranch.EnableStatusCheck = f.EnableStatusCheck if f.EnableStatusCheck { - statusCheckContexts := strings.Split(f.StatusCheckContexts, ";") - validStatusCheckContexts := make([]string, 0, len(statusCheckContexts)) - for _, c := range statusCheckContexts { - if c == "" { + replacer := strings.NewReplacer("\r\n", "\n", "\r", "\n") + patterns := strings.Split(replacer.Replace(f.StatusCheckContexts), "\n") + validPatterns := make([]string, 0, len(patterns)) + for _, c := range patterns { + trimmed := strings.TrimSpace(c) + if trimmed == "" { continue } - if _, err := glob.Compile(c); err != nil { + if _, err := glob.Compile(trimmed); err != nil { ctx.Flash.Error(ctx.Tr("repo.settings.protect_invalid_status_check_pattern", c)) ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, protectBranch.RuleName)) return } - validStatusCheckContexts = append(validStatusCheckContexts, c) + validPatterns = append(validPatterns, trimmed) } - protectBranch.StatusCheckContexts = validStatusCheckContexts + protectBranch.StatusCheckContexts = validPatterns } else { protectBranch.StatusCheckContexts = nil } diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 58a37948210b9..6b87748fb44fc 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -158,7 +158,7 @@
- +
- + {{.}} + {{$.locale.Tr "repo.settings.protect_status_check_matched"}}
{{.}} - {{$.locale.Tr "repo.settings.protect_status_check_matched"}} + {{$.locale.Tr "repo.settings.protect_status_check_matched"}}
diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 49f476ab7bff8..815ccade436f2 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -84,14 +84,15 @@ export function initRepoSettingBranches() { }); // show the `Matched` mark for the status check contexts that match the pattern - const statusCheckPatternInput = document.getElementById('status_check_contexts'); - statusCheckPatternInput?.addEventListener('input', (e) => { - const patterns = e.target.value.split(';'); + const statusCheckPatternTextarea = document.getElementById('status_check_contexts'); + statusCheckPatternTextarea?.addEventListener('input', (e) => { + const patterns = e.target.value.split(/[\r\n]+/); + const validPatterns = patterns.map(item => item.trim()).filter(item => item.length > 0); const marks = document.getElementsByClassName('status-check-matched-mark'); for (const el of marks) { let matched = false; const statusCheckContext = el.getAttribute('data-status-check-context'); - for (const pattern of patterns) { + for (const pattern of validPatterns) { if (minimatch(statusCheckContext, pattern)) { matched = true; break; From 872af3a72f671499fdbb9d74372fbb86aae9ef6a Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 11:31:13 +0800 Subject: [PATCH 14/29] improve desc text --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index cdcec6747ab32..c01b78cf1b7b6 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2183,7 +2183,7 @@ settings.protect_merge_whitelist_committers_desc = Allow only whitelisted users settings.protect_merge_whitelist_users = Whitelisted users for merging: settings.protect_merge_whitelist_teams = Whitelisted teams for merging: settings.protect_check_status_contexts = Enable Status Check -settings.protect_check_status_contexts_desc = "Require status checks to pass before merging. Enter patterns to specify which status checks must pass before branches can be merged into a branch that matches this rule. Multiple patterns can be separated using semicolon (';'). When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are selected, the last commit must be successful regardless of context." +settings.protect_check_status_contexts_desc = "Require status checks to pass before merging. Enter pattern(s) to specify which status checks must pass before branches can be merged into a branch that matches this rule. Multiple patterns can be separated using semicolon (';').When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are selected, the last commit must be successful regardless of context." settings.protect_check_status_contexts_list = Status checks found in the last week for this repository settings.protect_status_check_matched = Matched settings.protect_invalid_status_check_pattern = Invalid status check pattern: "%s". From efa21a1c2d58e4a02e398ab34616be66295235f3 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 11:34:46 +0800 Subject: [PATCH 15/29] improve desc text --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c01b78cf1b7b6..480b0db968cba 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2183,7 +2183,7 @@ settings.protect_merge_whitelist_committers_desc = Allow only whitelisted users settings.protect_merge_whitelist_users = Whitelisted users for merging: settings.protect_merge_whitelist_teams = Whitelisted teams for merging: settings.protect_check_status_contexts = Enable Status Check -settings.protect_check_status_contexts_desc = "Require status checks to pass before merging. Enter pattern(s) to specify which status checks must pass before branches can be merged into a branch that matches this rule. Multiple patterns can be separated using semicolon (';').When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are selected, the last commit must be successful regardless of context." +settings.protect_check_status_contexts_desc = Require status checks to pass before merging. Enter pattern(s) to specify which status checks must pass before branches can be merged into a branch that matches this rule. Each line specifies a pattern. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are selected, the last commit must be successful regardless of context. settings.protect_check_status_contexts_list = Status checks found in the last week for this repository settings.protect_status_check_matched = Matched settings.protect_invalid_status_check_pattern = Invalid status check pattern: "%s". From 5818403c1e6e85a973968010dfe6f31fa850e4e3 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 12:14:07 +0800 Subject: [PATCH 16/29] add desc for patterns textarea --- options/locale/locale_en-US.ini | 4 +++- templates/repo/settings/protected_branch.tmpl | 3 ++- web_src/js/features/repo-settings.js | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 480b0db968cba..5c9bd7919ecfa 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2183,7 +2183,9 @@ settings.protect_merge_whitelist_committers_desc = Allow only whitelisted users settings.protect_merge_whitelist_users = Whitelisted users for merging: settings.protect_merge_whitelist_teams = Whitelisted teams for merging: settings.protect_check_status_contexts = Enable Status Check -settings.protect_check_status_contexts_desc = Require status checks to pass before merging. Enter pattern(s) to specify which status checks must pass before branches can be merged into a branch that matches this rule. Each line specifies a pattern. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are selected, the last commit must be successful regardless of context. +settings.protect_status_check_patterns = Status check patterns: +settings.protect_status_check_patterns_desc = Enter pattern(s) to specify which status checks must pass before branches can be merged into a branch that matches this rule. Each line specifies a pattern +settings.protect_check_status_contexts_desc = Require status checks to pass before merging. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are matched, the last commit must be successful regardless of context. settings.protect_check_status_contexts_list = Status checks found in the last week for this repository settings.protect_status_check_matched = Matched settings.protect_invalid_status_check_pattern = Invalid status check pattern: "%s". diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 6b87748fb44fc..6990757fe5a1a 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -157,8 +157,9 @@
- + +

{{.locale.Tr "repo.settings.protect_status_check_patterns_desc"}}

diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 815ccade436f2..a54972d0714b2 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -87,7 +87,7 @@ export function initRepoSettingBranches() { const statusCheckPatternTextarea = document.getElementById('status_check_contexts'); statusCheckPatternTextarea?.addEventListener('input', (e) => { const patterns = e.target.value.split(/[\r\n]+/); - const validPatterns = patterns.map(item => item.trim()).filter(item => item.length > 0); + const validPatterns = patterns.map((item) => item.trim()).filter((item) => item.length > 0); const marks = document.getElementsByClassName('status-check-matched-mark'); for (const el of marks) { let matched = false; From fb9d67d2403c8edea47c4ca82acc1fd25467958d Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 13:40:20 +0800 Subject: [PATCH 17/29] remove can_context_match --- routers/web/repo/setting_protected_branch.go | 10 ---------- templates/repo/settings/protected_branch.tmpl | 2 +- web_src/js/features/repo-settings.js | 9 +++++---- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 1169a031e3462..950c7274b1e60 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -120,16 +120,6 @@ func SettingsProtectedBranch(c *context.Context) { c.Data["status_check_contexts"] = strings.Join(rule.StatusCheckContexts, "\n") contexts, _ := git_model.FindRepoRecentCommitStatusContexts(c, c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts c.Data["branch_status_check_contexts"] = contexts - c.Data["can_context_match"] = func(context string) bool { - for _, c := range rule.StatusCheckContexts { - if gp, err := glob.Compile(c); err != nil { - log.Error("glob.Compile %s failed. Error: %v", c, err) - } else if gp.Match(context) { - return true - } - } - return false - } if c.Repo.Owner.IsOrganization() { teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead) diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 6990757fe5a1a..9f8733259607f 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -171,7 +171,7 @@ {{else}} diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index a54972d0714b2..2a5a5a92a99ac 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -84,9 +84,8 @@ export function initRepoSettingBranches() { }); // show the `Matched` mark for the status check contexts that match the pattern - const statusCheckPatternTextarea = document.getElementById('status_check_contexts'); - statusCheckPatternTextarea?.addEventListener('input', (e) => { - const patterns = e.target.value.split(/[\r\n]+/); + const markMatchedCheckStatusContexts = () => { + const patterns = document.getElementById('status_check_contexts')?.value.split(/[\r\n]+/); const validPatterns = patterns.map((item) => item.trim()).filter((item) => item.length > 0); const marks = document.getElementsByClassName('status-check-matched-mark'); for (const el of marks) { @@ -104,5 +103,7 @@ export function initRepoSettingBranches() { el.classList.add('gt-hidden'); } } - }); + } + markMatchedCheckStatusContexts() + document.getElementById('status_check_contexts')?.addEventListener('input', markMatchedCheckStatusContexts); } From 1645f25d5798a7123192c8d58a506e638c2ed7bc Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 13:58:22 +0800 Subject: [PATCH 18/29] fix replace and escape --- routers/web/repo/setting_protected_branch.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 950c7274b1e60..81cb103feec9b 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -6,6 +6,7 @@ package repo import ( "fmt" "net/http" + "net/url" "strings" "time" @@ -228,17 +229,16 @@ func SettingsProtectedBranchPost(ctx *context.Context) { protectBranch.EnableStatusCheck = f.EnableStatusCheck if f.EnableStatusCheck { - replacer := strings.NewReplacer("\r\n", "\n", "\r", "\n") - patterns := strings.Split(replacer.Replace(f.StatusCheckContexts), "\n") + patterns := strings.Split(strings.ReplaceAll(f.StatusCheckContexts, "\r", "\n"), "\n") validPatterns := make([]string, 0, len(patterns)) - for _, c := range patterns { - trimmed := strings.TrimSpace(c) + for _, pattern := range patterns { + trimmed := strings.TrimSpace(pattern) if trimmed == "" { continue } if _, err := glob.Compile(trimmed); err != nil { - ctx.Flash.Error(ctx.Tr("repo.settings.protect_invalid_status_check_pattern", c)) - ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, protectBranch.RuleName)) + ctx.Flash.Error(ctx.Tr("repo.settings.protect_invalid_status_check_pattern", pattern)) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, url.PathEscape(protectBranch.RuleName))) return } validPatterns = append(validPatterns, trimmed) From 087061d9b330b1bc2b126d7028fcb06b799fba14 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 14:12:09 +0800 Subject: [PATCH 19/29] rename --- routers/web/repo/setting_protected_branch.go | 2 +- templates/repo/settings/protected_branch.tmpl | 4 ++-- web_src/js/features/repo-settings.js | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 81cb103feec9b..f96cc6c8512ca 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -120,7 +120,7 @@ func SettingsProtectedBranch(c *context.Context) { c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",") c.Data["status_check_contexts"] = strings.Join(rule.StatusCheckContexts, "\n") contexts, _ := git_model.FindRepoRecentCommitStatusContexts(c, c.Repo.Repository.ID, 7*24*time.Hour) // Find last week status check contexts - c.Data["branch_status_check_contexts"] = contexts + c.Data["recent_status_checks"] = contexts if c.Repo.Owner.IsOrganization() { teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead) diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 9f8733259607f..cad98f8a7f69e 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -167,11 +167,11 @@ - {{range $.branch_status_check_contexts}} + {{range $.recent_status_checks}} {{else}} diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 2a5a5a92a99ac..8ec35cb25c62c 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -83,16 +83,16 @@ export function initRepoSettingBranches() { if (this.checked) $target.addClass('disabled'); // only disable, do not auto enable }); - // show the `Matched` mark for the status check contexts that match the pattern - const markMatchedCheckStatusContexts = () => { + // show the `Matched` mark for the status checks that match the pattern + const markMatchedStatusChecks = () => { const patterns = document.getElementById('status_check_contexts')?.value.split(/[\r\n]+/); const validPatterns = patterns.map((item) => item.trim()).filter((item) => item.length > 0); const marks = document.getElementsByClassName('status-check-matched-mark'); for (const el of marks) { let matched = false; - const statusCheckContext = el.getAttribute('data-status-check-context'); + const statusCheck = el.getAttribute('data-status-check'); for (const pattern of validPatterns) { - if (minimatch(statusCheckContext, pattern)) { + if (minimatch(statusCheck, pattern)) { matched = true; break; } @@ -104,6 +104,6 @@ export function initRepoSettingBranches() { } } } - markMatchedCheckStatusContexts() - document.getElementById('status_check_contexts')?.addEventListener('input', markMatchedCheckStatusContexts); + markMatchedStatusChecks(); + document.getElementById('status_check_contexts')?.addEventListener('input', markMatchedStatusChecks); } From 11aebb8bdc941107cd678a7bf8e69b71b5ea5e17 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 14:36:32 +0800 Subject: [PATCH 20/29] fix semicolon --- web_src/js/features/repo-settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 8ec35cb25c62c..17b020f1aec55 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -103,7 +103,7 @@ export function initRepoSettingBranches() { el.classList.add('gt-hidden'); } } - } + }; markMatchedStatusChecks(); document.getElementById('status_check_contexts')?.addEventListener('input', markMatchedStatusChecks); } From 25e29b5a8ef9fa3edb4767c2333d4b534c4c4ec2 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 15:36:51 +0800 Subject: [PATCH 21/29] disallow empty patterns --- options/locale/locale_en-US.ini | 3 ++- routers/web/repo/setting_protected_branch.go | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 2e9dd466f0294..6585e7008d934 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2186,11 +2186,12 @@ settings.protect_merge_whitelist_users = Whitelisted users for merging: settings.protect_merge_whitelist_teams = Whitelisted teams for merging: settings.protect_check_status_contexts = Enable Status Check settings.protect_status_check_patterns = Status check patterns: -settings.protect_status_check_patterns_desc = Enter pattern(s) to specify which status checks must pass before branches can be merged into a branch that matches this rule. Each line specifies a pattern +settings.protect_status_check_patterns_desc = Enter patterns to specify which status checks must pass before branches can be merged into a branch that matches this rule. Each line specifies a pattern. Patterns cannot be empty. settings.protect_check_status_contexts_desc = Require status checks to pass before merging. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are matched, the last commit must be successful regardless of context. settings.protect_check_status_contexts_list = Status checks found in the last week for this repository settings.protect_status_check_matched = Matched settings.protect_invalid_status_check_pattern = Invalid status check pattern: "%s". +settings.protect_no_valid_status_check_patterns = No valid status check patterns. settings.protect_required_approvals = Required approvals: settings.protect_required_approvals_desc = Allow only to merge pull request with enough positive reviews. settings.protect_approvals_whitelist_enabled = Restrict approvals to whitelisted users or teams diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index f96cc6c8512ca..944f323bb96da 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -243,6 +243,12 @@ func SettingsProtectedBranchPost(ctx *context.Context) { } validPatterns = append(validPatterns, trimmed) } + if len(validPatterns) == 0 { + // if status check is enabled, patterns slice is not allowed to be empty + ctx.Flash.Error(ctx.Tr("repo.settings.no_valid_status_check_patterns")) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, url.PathEscape(protectBranch.RuleName))) + return + } protectBranch.StatusCheckContexts = validPatterns } else { protectBranch.StatusCheckContexts = nil From 74958d750c023f4395c1054893f1493d8f2b7e57 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 15:42:28 +0800 Subject: [PATCH 22/29] fix locales and escape --- routers/web/repo/setting_protected_branch.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 944f323bb96da..1a944799c23fc 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -238,15 +238,15 @@ func SettingsProtectedBranchPost(ctx *context.Context) { } if _, err := glob.Compile(trimmed); err != nil { ctx.Flash.Error(ctx.Tr("repo.settings.protect_invalid_status_check_pattern", pattern)) - ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, url.PathEscape(protectBranch.RuleName))) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, url.QueryEscape(protectBranch.RuleName))) return } validPatterns = append(validPatterns, trimmed) } if len(validPatterns) == 0 { // if status check is enabled, patterns slice is not allowed to be empty - ctx.Flash.Error(ctx.Tr("repo.settings.no_valid_status_check_patterns")) - ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, url.PathEscape(protectBranch.RuleName))) + ctx.Flash.Error(ctx.Tr("repo.settings.protect_no_valid_status_check_patterns")) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, url.QueryEscape(protectBranch.RuleName))) return } protectBranch.StatusCheckContexts = validPatterns From 49746b1873135c78e4c87e932c35d8030ba688a5 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Fri, 12 May 2023 16:29:51 +0800 Subject: [PATCH 23/29] improve MergeRequiredContextsCommitStatus --- services/pull/commit_status.go | 61 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index 704b012596534..e7a86efb25aa9 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -20,45 +20,44 @@ import ( // MergeRequiredContextsCommitStatus returns a commit status state for given required contexts func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, requiredContexts []string) structs.CommitStatusState { - if len(requiredContexts) == 0 { - status := git_model.CalcCommitStatus(commitStatuses) - if status != nil { - return status.State - } - return structs.CommitStatusSuccess - } + // matchedCount is the number of `CommitStatus.Context` that match any context of `requiredContexts` + matchedCount := 0 + returnedStatus := structs.CommitStatusSuccess - requiredContextsGlob := make(map[string]glob.Glob, len(requiredContexts)) - for _, ctx := range requiredContexts { - if gp, err := glob.Compile(ctx); err != nil { - log.Error("glob.Compile %s failed. Error: %v", ctx, err) - } else { - requiredContextsGlob[ctx] = gp + if len(requiredContexts) > 0 { + requiredContextsGlob := make(map[string]glob.Glob, len(requiredContexts)) + for _, ctx := range requiredContexts { + if gp, err := glob.Compile(ctx); err != nil { + log.Error("glob.Compile %s failed. Error: %v", ctx, err) + } else { + requiredContextsGlob[ctx] = gp + } } - } - returnedStatus := structs.CommitStatusSuccess - for ctx, gp := range requiredContextsGlob { - var targetStatus structs.CommitStatusState - for _, commitStatus := range commitStatuses { - if gp.Match(commitStatus.Context) { - targetStatus = commitStatus.State - break + for _, gp := range requiredContextsGlob { + var targetStatus structs.CommitStatusState + for _, commitStatus := range commitStatuses { + if gp.Match(commitStatus.Context) { + targetStatus = commitStatus.State + matchedCount++ + break + } } - } - if targetStatus == "" { - targetStatus = structs.CommitStatusPending - commitStatuses = append(commitStatuses, &git_model.CommitStatus{ - State: targetStatus, - Context: ctx, - Description: "Pending", - }) + if targetStatus != "" && targetStatus.NoBetterThan(returnedStatus) { + returnedStatus = targetStatus + } } - if targetStatus.NoBetterThan(returnedStatus) { - returnedStatus = targetStatus + } + + if matchedCount == 0 { + status := git_model.CalcCommitStatus(commitStatuses) + if status != nil { + return status.State } + return structs.CommitStatusSuccess } + return returnedStatus } From 2f4c1a3c0ff26ffb3f7541ba1b490bd64b1f8290 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Mon, 15 May 2023 09:26:59 +0800 Subject: [PATCH 24/29] fix comments --- web_src/js/features/repo-settings.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 17b020f1aec55..074945153def0 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -1,6 +1,7 @@ import $ from 'jquery'; import {minimatch} from 'minimatch'; import {createMonaco} from './codeeditor.js'; +import {onInputDebounce} from '../utils/dom.js'; const {appSubUrl, csrfToken} = window.config; @@ -86,7 +87,7 @@ export function initRepoSettingBranches() { // show the `Matched` mark for the status checks that match the pattern const markMatchedStatusChecks = () => { const patterns = document.getElementById('status_check_contexts')?.value.split(/[\r\n]+/); - const validPatterns = patterns.map((item) => item.trim()).filter((item) => item.length > 0); + const validPatterns = patterns.map((item) => item.trim()).filter(Boolean); const marks = document.getElementsByClassName('status-check-matched-mark'); for (const el of marks) { let matched = false; @@ -105,5 +106,5 @@ export function initRepoSettingBranches() { } }; markMatchedStatusChecks(); - document.getElementById('status_check_contexts')?.addEventListener('input', markMatchedStatusChecks); + document.getElementById('status_check_contexts')?.addEventListener('input', onInputDebounce(markMatchedStatusChecks)); } From 2607a4f4dd2441bf8016cf13d0ae3ecfebec9529 Mon Sep 17 00:00:00 2001 From: silverwind Date: Mon, 15 May 2023 12:44:54 +0200 Subject: [PATCH 25/29] Add safeguard --- web_src/js/features/repo-settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 074945153def0..1f68bda1a930c 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -86,7 +86,7 @@ export function initRepoSettingBranches() { // show the `Matched` mark for the status checks that match the pattern const markMatchedStatusChecks = () => { - const patterns = document.getElementById('status_check_contexts')?.value.split(/[\r\n]+/); + const patterns = (document.getElementById('status_check_contexts')?.value || '').split(/[\r\n]+/); const validPatterns = patterns.map((item) => item.trim()).filter(Boolean); const marks = document.getElementsByClassName('status-check-matched-mark'); for (const el of marks) { From 3b42ff6f5235e967adf92f409960380246cd2bb5 Mon Sep 17 00:00:00 2001 From: silverwind Date: Mon, 15 May 2023 12:47:23 +0200 Subject: [PATCH 26/29] Update web_src/js/features/repo-settings.js --- web_src/js/features/repo-settings.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 1f68bda1a930c..c50c2ce2b23ec 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -89,6 +89,7 @@ export function initRepoSettingBranches() { const patterns = (document.getElementById('status_check_contexts')?.value || '').split(/[\r\n]+/); const validPatterns = patterns.map((item) => item.trim()).filter(Boolean); const marks = document.getElementsByClassName('status-check-matched-mark'); + for (const el of marks) { let matched = false; const statusCheck = el.getAttribute('data-status-check'); From 606394ce2087d8cd21841bb078a3cda13cb71c6d Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Mon, 15 May 2023 18:52:57 +0800 Subject: [PATCH 27/29] use toggleElem --- web_src/js/features/repo-settings.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index c50c2ce2b23ec..5f5077b25d71b 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import {minimatch} from 'minimatch'; import {createMonaco} from './codeeditor.js'; -import {onInputDebounce} from '../utils/dom.js'; +import {onInputDebounce, toggleElem} from '../utils/dom.js'; const {appSubUrl, csrfToken} = window.config; @@ -99,11 +99,8 @@ export function initRepoSettingBranches() { break; } } - if (matched) { - el.classList.remove('gt-hidden'); - } else { - el.classList.add('gt-hidden'); - } + + toggleElem(el, matched); } }; markMatchedStatusChecks(); From 8e190b036d66065b40e4d88ad49ea0ef0c0c578e Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 17 May 2023 11:48:29 +0800 Subject: [PATCH 28/29] fix comments --- routers/web/repo/pull.go | 4 +--- web_src/js/features/repo-settings.js | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index dad84a0dd198f..8821e74c951e4 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -577,9 +577,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C if pb != nil && pb.EnableStatusCheck { ctx.Data["is_context_required"] = func(context string) bool { for _, c := range pb.StatusCheckContexts { - if gp, err := glob.Compile(c); err != nil { - log.Error("glob.Compile %s failed. Error: %v", c, err) - } else if gp.Match(context) { + if gp, err := glob.Compile(c); err == nil && gp.Match(context) { return true } } diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 5f5077b25d71b..8cc016fdc27ea 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -86,7 +86,7 @@ export function initRepoSettingBranches() { // show the `Matched` mark for the status checks that match the pattern const markMatchedStatusChecks = () => { - const patterns = (document.getElementById('status_check_contexts')?.value || '').split(/[\r\n]+/); + const patterns = (document.getElementById('status_check_contexts').value || '').split(/[\r\n]+/); const validPatterns = patterns.map((item) => item.trim()).filter(Boolean); const marks = document.getElementsByClassName('status-check-matched-mark'); @@ -104,5 +104,5 @@ export function initRepoSettingBranches() { } }; markMatchedStatusChecks(); - document.getElementById('status_check_contexts')?.addEventListener('input', onInputDebounce(markMatchedStatusChecks)); + document.getElementById('status_check_contexts').addEventListener('input', onInputDebounce(markMatchedStatusChecks)); } From 2b82824399f91fa82c62e5eb00abf8c8b4e2d772 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 17 May 2023 12:00:28 +0800 Subject: [PATCH 29/29] fix status check --- services/pull/commit_status.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index e7a86efb25aa9..51ba06da27586 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -34,9 +34,9 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, } } - for _, gp := range requiredContextsGlob { + for _, commitStatus := range commitStatuses { var targetStatus structs.CommitStatusState - for _, commitStatus := range commitStatuses { + for _, gp := range requiredContextsGlob { if gp.Match(commitStatus.Context) { targetStatus = commitStatus.State matchedCount++
{{.}} - {{$.locale.Tr "repo.settings.protect_status_check_matched"}} + {{$.locale.Tr "repo.settings.protect_status_check_matched"}}
{{.}} - {{$.locale.Tr "repo.settings.protect_status_check_matched"}} + {{$.locale.Tr "repo.settings.protect_status_check_matched"}}