aboutsummaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorWGH <wgh@torlan.ru>2019-09-09 08:48:21 +0300
committerLauris BH <lauris.buksis@zzdats.lv>2019-09-09 08:48:21 +0300
commit6ddd3b0b470d16dfe62caf5fff21011cfff44a76 (patch)
tree05d4c7fedf8af21b489003890be000f839f69a51 /models
parent0118b6aaf8ada3edd67cb975c776f6f124178ad2 (diff)
downloadgitea-6ddd3b0b470d16dfe62caf5fff21011cfff44a76.tar.gz
gitea-6ddd3b0b470d16dfe62caf5fff21011cfff44a76.zip
Implement webhook branch filter (#7791)
* Fix validate() function to handle errors in embedded anon structs * Implement webhook branch filter See #2025, #3998.
Diffstat (limited to 'models')
-rw-r--r--models/fixtures/webhook.yml7
-rw-r--r--models/webhook.go52
-rw-r--r--models/webhook_test.go34
3 files changed, 90 insertions, 3 deletions
diff --git a/models/fixtures/webhook.yml b/models/fixtures/webhook.yml
index 11d7439cf4..5563dcada7 100644
--- a/models/fixtures/webhook.yml
+++ b/models/fixtures/webhook.yml
@@ -22,3 +22,10 @@
content_type: 1 # json
events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}'
is_active: true
+-
+ id: 4
+ repo_id: 2
+ url: www.example.com/url4
+ content_type: 1 # json
+ events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
+ is_active: true
diff --git a/models/webhook.go b/models/webhook.go
index 4eda08fdb5..67ae783759 100644
--- a/models/webhook.go
+++ b/models/webhook.go
@@ -19,12 +19,14 @@ import (
"strings"
"time"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/timeutil"
+ "github.com/gobwas/glob"
gouuid "github.com/satori/go.uuid"
"github.com/unknwon/com"
)
@@ -84,9 +86,10 @@ type HookEvents struct {
// HookEvent represents events that will delivery hook.
type HookEvent struct {
- PushOnly bool `json:"push_only"`
- SendEverything bool `json:"send_everything"`
- ChooseEvents bool `json:"choose_events"`
+ PushOnly bool `json:"push_only"`
+ SendEverything bool `json:"send_everything"`
+ ChooseEvents bool `json:"choose_events"`
+ BranchFilter string `json:"branch_filter"`
HookEvents `json:"events"`
}
@@ -256,6 +259,21 @@ func (w *Webhook) EventsArray() []string {
return events
}
+func (w *Webhook) checkBranch(branch string) bool {
+ if w.BranchFilter == "" || w.BranchFilter == "*" {
+ return true
+ }
+
+ g, err := glob.Compile(w.BranchFilter)
+ if err != nil {
+ // should not really happen as BranchFilter is validated
+ log.Error("CheckBranch failed: %s", err)
+ return false
+ }
+
+ return g.Match(branch)
+}
+
// CreateWebhook creates a new web hook.
func CreateWebhook(w *Webhook) error {
return createWebhook(x, w)
@@ -651,6 +669,25 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay
return prepareWebhook(x, w, repo, event, p)
}
+// getPayloadBranch returns branch for hook event, if applicable.
+func getPayloadBranch(p api.Payloader) string {
+ switch pp := p.(type) {
+ case *api.CreatePayload:
+ if pp.RefType == "branch" {
+ return pp.Ref
+ }
+ case *api.DeletePayload:
+ if pp.RefType == "branch" {
+ return pp.Ref
+ }
+ case *api.PushPayload:
+ if strings.HasPrefix(pp.Ref, git.BranchPrefix) {
+ return pp.Ref[len(git.BranchPrefix):]
+ }
+ }
+ return ""
+}
+
func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType, p api.Payloader) error {
for _, e := range w.eventCheckers() {
if event == e.typ {
@@ -660,6 +697,15 @@ func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType,
}
}
+ // 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 !w.checkBranch(branch) {
+ log.Info("Branch %q doesn't match branch filter %q, skipping", branch, w.BranchFilter)
+ return nil
+ }
+ }
+
var payloader api.Payloader
var err error
// Use separate objects so modifications won't be made on payload on non-Gogs/Gitea type hooks.
diff --git a/models/webhook_test.go b/models/webhook_test.go
index d2fb3ea6ed..343000f5b5 100644
--- a/models/webhook_test.go
+++ b/models/webhook_test.go
@@ -270,6 +270,40 @@ func TestPrepareWebhooks(t *testing.T) {
}
}
+func TestPrepareWebhooksBranchFilterMatch(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ repo := AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
+ hookTasks := []*HookTask{
+ {RepoID: repo.ID, HookID: 4, EventType: HookEventPush},
+ }
+ for _, hookTask := range hookTasks {
+ AssertNotExistsBean(t, hookTask)
+ }
+ // this test also ensures that * doesn't handle / in any special way (like shell would)
+ assert.NoError(t, PrepareWebhooks(repo, HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791"}))
+ for _, hookTask := range hookTasks {
+ AssertExistsAndLoadBean(t, hookTask)
+ }
+}
+
+func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ repo := AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
+ hookTasks := []*HookTask{
+ {RepoID: repo.ID, HookID: 4, EventType: HookEventPush},
+ }
+ for _, hookTask := range hookTasks {
+ AssertNotExistsBean(t, hookTask)
+ }
+ assert.NoError(t, PrepareWebhooks(repo, HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"}))
+
+ for _, hookTask := range hookTasks {
+ AssertNotExistsBean(t, hookTask)
+ }
+}
+
// TODO TestHookTask_deliver
// TODO TestDeliverHooks