mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Backport #35567 by Exgene Co-authored-by: Kausthubh J Rao <105716675+Exgene@users.noreply.github.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
		| @@ -2433,7 +2433,9 @@ settings.event_workflow_job_desc = Gitea Actions Workflow job queued, waiting, i | ||||
| settings.event_package = Package | ||||
| settings.event_package_desc = Package created or deleted in a repository. | ||||
| settings.branch_filter = Branch filter | ||||
| settings.branch_filter_desc = Branch whitelist for push, branch creation and branch deletion events, specified as glob pattern. If empty or <code>*</code>, events for all branches are reported. See <a href="%[1]s">%[2]s</a> documentation for syntax. Examples: <code>master</code>, <code>{master,release*}</code>. | ||||
| settings.branch_filter_desc_1 = Branch (and ref name) allowlist for push, branch creation and branch deletion events, specified as glob pattern. If empty or <code>*</code>, events for all branches and tags are reported. | ||||
| settings.branch_filter_desc_2 = Use <code>refs/heads/</code> or <code>refs/tags/</code> prefix to match full ref names. | ||||
| settings.branch_filter_desc_doc = See <a href="%[1]s">%[2]s</a> documentation for syntax. | ||||
| settings.authorization_header = Authorization Header | ||||
| settings.authorization_header_desc = Will be included as authorization header for requests when present. Examples: %s. | ||||
| settings.active = Active | ||||
|   | ||||
| @@ -8,7 +8,6 @@ import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| @@ -46,21 +45,25 @@ func IsValidHookTaskType(name string) bool { | ||||
| // hookQueue is a global queue of web hooks | ||||
| var hookQueue *queue.WorkerPoolQueue[int64] | ||||
|  | ||||
| // getPayloadBranch returns branch for hook event, if applicable. | ||||
| func getPayloadBranch(p api.Payloader) string { | ||||
| // getPayloadRef returns the full ref name for hook event, if applicable. | ||||
| func getPayloadRef(p api.Payloader) git.RefName { | ||||
| 	switch pp := p.(type) { | ||||
| 	case *api.CreatePayload: | ||||
| 		if pp.RefType == "branch" { | ||||
| 			return pp.Ref | ||||
| 		switch pp.RefType { | ||||
| 		case "branch": | ||||
| 			return git.RefNameFromBranch(pp.Ref) | ||||
| 		case "tag": | ||||
| 			return git.RefNameFromTag(pp.Ref) | ||||
| 		} | ||||
| 	case *api.DeletePayload: | ||||
| 		if pp.RefType == "branch" { | ||||
| 			return pp.Ref | ||||
| 		switch pp.RefType { | ||||
| 		case "branch": | ||||
| 			return git.RefNameFromBranch(pp.Ref) | ||||
| 		case "tag": | ||||
| 			return git.RefNameFromTag(pp.Ref) | ||||
| 		} | ||||
| 	case *api.PushPayload: | ||||
| 		if strings.HasPrefix(pp.Ref, git.BranchPrefix) { | ||||
| 			return pp.Ref[len(git.BranchPrefix):] | ||||
| 		} | ||||
| 		return git.RefName(pp.Ref) | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| @@ -108,19 +111,22 @@ func enqueueHookTask(taskID int64) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func checkBranch(w *webhook_model.Webhook, branch string) bool { | ||||
| 	if w.BranchFilter == "" || w.BranchFilter == "*" { | ||||
| func checkBranchFilter(branchFilter string, ref git.RefName) bool { | ||||
| 	if branchFilter == "" || branchFilter == "*" || branchFilter == "**" { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	g, err := glob.Compile(w.BranchFilter) | ||||
| 	g, err := glob.Compile(branchFilter) | ||||
| 	if err != nil { | ||||
| 		// should not really happen as BranchFilter is validated | ||||
| 		log.Error("CheckBranch failed: %s", err) | ||||
| 		log.Debug("checkBranchFilter failed to compile filer %q, err: %s", branchFilter, err) | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	return g.Match(branch) | ||||
| 	if ref.IsBranch() && g.Match(ref.BranchName()) { | ||||
| 		return true | ||||
| 	} | ||||
| 	return g.Match(ref.String()) | ||||
| } | ||||
|  | ||||
| // PrepareWebhook creates a hook task and enqueues it for processing. | ||||
| @@ -144,11 +150,10 @@ func PrepareWebhook(ctx context.Context, w *webhook_model.Webhook, event webhook | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// If payload has no associated branch (e.g. it's a new tag, issue, etc.), | ||||
| 	// branch filter has no effect. | ||||
| 	if branch := getPayloadBranch(p); branch != "" { | ||||
| 		if !checkBranch(w, branch) { | ||||
| 			log.Info("Branch %q doesn't match branch filter %q, skipping", branch, w.BranchFilter) | ||||
| 	// If payload has no associated branch (e.g. it's a new tag, issue, etc.), branch filter has no effect. | ||||
| 	if ref := getPayloadRef(p); ref != "" { | ||||
| 		// Check the payload's git ref against the webhook's branch filter. | ||||
| 		if !checkBranchFilter(w.BranchFilter, ref) { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	webhook_model "code.gitea.io/gitea/models/webhook" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	api "code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/test" | ||||
| @@ -90,3 +91,30 @@ func TestWebhookUserMail(t *testing.T) { | ||||
| 	assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(t.Context(), user, nil).Email) | ||||
| 	assert.Equal(t, user.Email, convert.ToUser(t.Context(), user, user).Email) | ||||
| } | ||||
|  | ||||
| func TestCheckBranchFilter(t *testing.T) { | ||||
| 	cases := []struct { | ||||
| 		filter string | ||||
| 		ref    git.RefName | ||||
| 		match  bool | ||||
| 	}{ | ||||
| 		{"", "any-ref", true}, | ||||
| 		{"*", "any-ref", true}, | ||||
| 		{"**", "any-ref", true}, | ||||
|  | ||||
| 		{"main", git.RefNameFromBranch("main"), true}, | ||||
| 		{"main", git.RefNameFromTag("main"), false}, | ||||
|  | ||||
| 		{"feature/*", git.RefNameFromBranch("feature"), false}, | ||||
| 		{"feature/*", git.RefNameFromBranch("feature/foo"), true}, | ||||
| 		{"feature/*", git.RefNameFromTag("feature/foo"), false}, | ||||
|  | ||||
| 		{"{refs/heads/feature/*,refs/tags/release/*}", git.RefNameFromBranch("feature/foo"), true}, | ||||
| 		{"{refs/heads/feature/*,refs/tags/release/*}", git.RefNameFromBranch("main"), false}, | ||||
| 		{"{refs/heads/feature/*,refs/tags/release/*}", git.RefNameFromTag("release/bar"), true}, | ||||
| 		{"{refs/heads/feature/*,refs/tags/release/*}", git.RefNameFromTag("dev"), false}, | ||||
| 	} | ||||
| 	for _, v := range cases { | ||||
| 		assert.Equal(t, v.match, checkBranchFilter(v.filter, v.ref), "filter: %q ref: %q", v.filter, v.ref) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -44,7 +44,16 @@ | ||||
| <div class="field"> | ||||
| 	<label>{{ctx.Locale.Tr "repo.settings.branch_filter"}}</label> | ||||
| 	<input name="branch_filter" type="text" value="{{or .Webhook.BranchFilter "*"}}"> | ||||
| 	<span class="help">{{ctx.Locale.Tr "repo.settings.branch_filter_desc" "https://pkg.go.dev/github.com/gobwas/glob#Compile" "github.com/gobwas/glob"}}</span> | ||||
| 	<span class="help"> | ||||
| 		{{ctx.Locale.Tr "repo.settings.branch_filter_desc_1"}} | ||||
| 		{{ctx.Locale.Tr "repo.settings.branch_filter_desc_2"}} | ||||
| 		{{ctx.Locale.Tr "repo.settings.branch_filter_desc_doc" "https://pkg.go.dev/github.com/gobwas/glob#Compile" "github.com/gobwas/glob"}} | ||||
| 		<ul> | ||||
| 			<li><code>main</code></li> | ||||
| 			<li><code>{main,feature/*}</code></li> | ||||
| 			<li><code>{refs/heads/feature/*,refs/tags/release/*}</code></li> | ||||
| 		</ul> | ||||
| 	</span> | ||||
| </div> | ||||
|  | ||||
| <div class="field"> | ||||
|   | ||||
| @@ -101,6 +101,13 @@ samp, | ||||
|   font-size: 0.95em; /* compensate for monospace fonts being usually slightly larger */ | ||||
| } | ||||
|  | ||||
| code { | ||||
|   padding: 1px 4px; | ||||
|   border-radius: var(--border-radius); | ||||
|   background-color: var(--color-label-bg); | ||||
|   color: var(--color-label-text); | ||||
| } | ||||
|  | ||||
| b, | ||||
| strong, | ||||
| h1, | ||||
| @@ -177,6 +184,11 @@ table { | ||||
|   border-collapse: collapse; | ||||
| } | ||||
|  | ||||
| ul { | ||||
|   margin: 0.5em 0; | ||||
|   padding: 0 0 0 1.5em; | ||||
| } | ||||
|  | ||||
| button { | ||||
|   cursor: pointer; | ||||
| } | ||||
| @@ -603,11 +615,6 @@ img.ui.avatar, | ||||
|   text-align: center; | ||||
| } | ||||
|  | ||||
| .ui .message > ul { | ||||
|   margin: 0; | ||||
|   padding: 0 1em; | ||||
| } | ||||
|  | ||||
| .ui .header > i + .content { | ||||
|   padding-left: 0.75rem; | ||||
|   vertical-align: middle; | ||||
|   | ||||
| @@ -218,11 +218,16 @@ textarea:focus, | ||||
|  | ||||
| .form .help { | ||||
|   color: var(--color-secondary-dark-5); | ||||
|   margin-top: 0.25em; | ||||
|   padding-bottom: 0.6em; | ||||
|   display: inline-block; | ||||
|   text-wrap: balance; | ||||
| } | ||||
|  | ||||
| .form .help code { | ||||
|   color: var(--color-text-light-1); | ||||
| } | ||||
|  | ||||
| .m-captcha-style { | ||||
|   width: 100%; | ||||
|   height: 5em; | ||||
|   | ||||
| @@ -84,10 +84,3 @@ | ||||
|   margin-right: 8px; | ||||
|   text-align: left; | ||||
| } | ||||
|  | ||||
| .label-filter-exclude-info code { | ||||
|   border: 1px solid var(--color-secondary); | ||||
|   border-radius: var(--border-radius); | ||||
|   padding: 1px 2px; | ||||
|   font-size: 11px; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user