Sfoglia il codice sorgente

webhook: able to detect delete branch or tag (#2315)

Unknwon 8 anni fa
parent
commit
f0086e66ae

+ 3 - 1
conf/locale/locale_en-US.ini

@@ -755,7 +755,9 @@ settings.event_push_only = Just the <code>push</code> event.
 settings.event_send_everything = I need <strong>everything</strong>.
 settings.event_choose = Let me choose what I need.
 settings.event_create = Create
-settings.event_create_desc = Branch, or tag created
+settings.event_create_desc = Branch or tag created
+settings.event_delete = Delete
+settings.event_delete_desc = Branch or tag deleted
 settings.event_pull_request = Pull Request
 settings.event_pull_request_desc = Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
 settings.event_push = Push

+ 52 - 22
models/action.go

@@ -458,18 +458,16 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
 		return fmt.Errorf("UpdateRepository: %v", err)
 	}
 
-	isNewBranch := false
+	isNewRef := opts.OldCommitID == git.EMPTY_SHA
+	isDelRef := opts.NewCommitID == git.EMPTY_SHA
+
 	opType := ACTION_COMMIT_REPO
-	// Check it's tag push or branch.
+	// Check if it's tag push or branch.
 	if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
 		opType = ACTION_PUSH_TAG
-		opts.Commits = &PushCommits{}
 	} else {
-		// TODO: detect branch deletion
 		// if not the first commit, set the compare URL.
-		if opts.OldCommitID == git.EMPTY_SHA {
-			isNewBranch = true
-		} else {
+		if !isNewRef && !isDelRef {
 			opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
 		}
 
@@ -506,20 +504,36 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
 		go HookQueue.Add(repo.ID)
 	}()
 
-	apiPusher := pusher.APIFormat()
 	apiRepo := repo.APIFormat(nil)
+	apiPusher := pusher.APIFormat()
 	switch opType {
 	case ACTION_COMMIT_REPO: // Push
+		if isDelRef {
+			if err = PrepareWebhooks(repo, HOOK_EVENT_DELETE, &api.DeletePayload{
+				Ref:        refName,
+				RefType:    "branch",
+				PusherType: api.PUSHER_TYPE_USER,
+				Repo:       apiRepo,
+				Sender:     apiPusher,
+			}); err != nil {
+				return fmt.Errorf("PrepareWebhooks.(delete branch): %v", err)
+			}
+
+			// Delete branch doesn't have anything to push or compare
+			return nil
+		}
+
 		compareURL := setting.AppUrl + opts.Commits.CompareURL
-		if isNewBranch {
+		if isNewRef {
 			compareURL = ""
 			if err = PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
-				Ref:     refName,
-				RefType: "branch",
-				Repo:    apiRepo,
-				Sender:  apiPusher,
+				Ref:           refName,
+				RefType:       "branch",
+				DefaultBranch: repo.DefaultBranch,
+				Repo:          apiRepo,
+				Sender:        apiPusher,
 			}); err != nil {
-				return fmt.Errorf("PrepareWebhooks (new branch): %v", err)
+				return fmt.Errorf("PrepareWebhooks.(new branch): %v", err)
 			}
 		}
 
@@ -533,16 +547,32 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
 			Pusher:     apiPusher,
 			Sender:     apiPusher,
 		}); err != nil {
-			return fmt.Errorf("PrepareWebhooks (new commit): %v", err)
+			return fmt.Errorf("PrepareWebhooks.(new commit): %v", err)
 		}
 
-	case ACTION_PUSH_TAG: // Create
-		return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
-			Ref:     refName,
-			RefType: "tag",
-			Repo:    apiRepo,
-			Sender:  apiPusher,
-		})
+	case ACTION_PUSH_TAG: // Tag
+		if isDelRef {
+			if err = PrepareWebhooks(repo, HOOK_EVENT_DELETE, &api.DeletePayload{
+				Ref:        refName,
+				RefType:    "tag",
+				PusherType: api.PUSHER_TYPE_USER,
+				Repo:       apiRepo,
+				Sender:     apiPusher,
+			}); err != nil {
+				return fmt.Errorf("PrepareWebhooks.(delete tag): %v", err)
+			}
+			return nil
+		}
+
+		if err = PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
+			Ref:           refName,
+			RefType:       "tag",
+			DefaultBranch: repo.DefaultBranch,
+			Repo:          apiRepo,
+			Sender:        apiPusher,
+		}); err != nil {
+			return fmt.Errorf("PrepareWebhooks.(new tag): %v", err)
+		}
 	}
 
 	return nil

+ 22 - 23
models/update.go

@@ -10,8 +10,6 @@ import (
 	"os/exec"
 	"strings"
 
-	log "gopkg.in/clog.v1"
-
 	git "github.com/gogits/git-module"
 )
 
@@ -29,6 +27,10 @@ func CommitToPushCommit(commit *git.Commit) *PushCommit {
 }
 
 func ListToPushCommits(l *list.List) *PushCommits {
+	if l == nil {
+		return &PushCommits{}
+	}
+
 	commits := make([]*PushCommit, 0)
 	var actEmail string
 	for e := l.Front(); e != nil; e = e.Next() {
@@ -68,12 +70,6 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
 		return fmt.Errorf("Fail to call 'git update-server-info': %v", err)
 	}
 
-	if isDelRef {
-		log.Trace("Reference '%s' has been deleted from '%s/%s' by %s",
-			opts.RefFullName, opts.RepoUserName, opts.RepoName, opts.PusherName)
-		return nil
-	}
-
 	gitRepo, err := git.OpenRepository(repoPath)
 	if err != nil {
 		return fmt.Errorf("OpenRepository: %v", err)
@@ -100,27 +96,30 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
 			NewCommitID: opts.NewCommitID,
 			Commits:     &PushCommits{},
 		}); err != nil {
-			return fmt.Errorf("CommitRepoAction (tag): %v", err)
+			return fmt.Errorf("CommitRepoAction.(tag): %v", err)
 		}
 		return nil
 	}
 
-	newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
-	if err != nil {
-		return fmt.Errorf("gitRepo.GetCommit: %v", err)
-	}
-
-	// Push new branch.
 	var l *list.List
-	if isNewRef {
-		l, err = newCommit.CommitsBeforeLimit(10)
+	// Skip read parent commits when delete branch
+	if !isDelRef {
+		// Push new branch.
+		newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
 		if err != nil {
-			return fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
+			return fmt.Errorf("GetCommit [commit_id: %s]: %v", opts.NewCommitID, err)
 		}
-	} else {
-		l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
-		if err != nil {
-			return fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
+
+		if isNewRef {
+			l, err = newCommit.CommitsBeforeLimit(10)
+			if err != nil {
+				return fmt.Errorf("CommitsBeforeLimit [commit_id: %s]: %v", newCommit.ID, err)
+			}
+		} else {
+			l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
+			if err != nil {
+				return fmt.Errorf("CommitsBeforeUntil [commit_id: %s]: %v", opts.OldCommitID, err)
+			}
 		}
 	}
 
@@ -133,7 +132,7 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
 		NewCommitID: opts.NewCommitID,
 		Commits:     ListToPushCommits(l),
 	}); err != nil {
-		return fmt.Errorf("CommitRepoAction (branch): %v", err)
+		return fmt.Errorf("CommitRepoAction.(branch): %v", err)
 	}
 	return nil
 }

+ 12 - 0
models/webhook.go

@@ -63,6 +63,7 @@ func IsValidHookContentType(name string) bool {
 
 type HookEvents struct {
 	Create      bool `json:"create"`
+	Delete      bool `json:"delete"`
 	Push        bool `json:"push"`
 	PullRequest bool `json:"pull_request"`
 }
@@ -156,6 +157,12 @@ func (w *Webhook) HasCreateEvent() bool {
 		(w.ChooseEvents && w.HookEvents.Create)
 }
 
+// HasDeleteEvent returns true if hook enabled delete event.
+func (w *Webhook) HasDeleteEvent() bool {
+	return w.SendEverything ||
+		(w.ChooseEvents && w.HookEvents.Delete)
+}
+
 // HasPushEvent returns true if hook enabled push event.
 func (w *Webhook) HasPushEvent() bool {
 	return w.PushOnly || w.SendEverything ||
@@ -337,6 +344,7 @@ type HookEventType string
 
 const (
 	HOOK_EVENT_CREATE       HookEventType = "create"
+	HOOK_EVENT_DELETE       HookEventType = "delete"
 	HOOK_EVENT_PUSH         HookEventType = "push"
 	HOOK_EVENT_PULL_REQUEST HookEventType = "pull_request"
 )
@@ -462,6 +470,10 @@ func prepareWebhooks(repo *Repository, event HookEventType, p api.Payloader, web
 			if !w.HasCreateEvent() {
 				continue
 			}
+		case HOOK_EVENT_DELETE:
+			if !w.HasDeleteEvent() {
+				continue
+			}
 		case HOOK_EVENT_PUSH:
 			if !w.HasPushEvent() {
 				continue

+ 38 - 15
models/webhook_discord.go

@@ -68,22 +68,35 @@ func DiscordSHALinkFormatter(url string, text string) string {
 	return fmt.Sprintf("[`%s`](%s)", text, url)
 }
 
-func getDiscordCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*DiscordPayload, error) {
-	// Created tag/branch
+// getDiscordCreatePayload composes Discord payload for create new branch or tag.
+func getDiscordCreatePayload(p *api.CreatePayload) (*DiscordPayload, error) {
 	refName := git.RefEndName(p.Ref)
-
 	repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
 	refLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
 	content := fmt.Sprintf("Created new %s: %s/%s", p.RefType, repoLink, refLink)
 
-	color, _ := strconv.ParseInt(strings.TrimLeft(slack.Color, "#"), 16, 32)
 	return &DiscordPayload{
-		Username:  slack.Username,
-		AvatarURL: slack.IconURL,
 		Embeds: []*DiscordEmbedObject{{
 			Description: content,
 			URL:         setting.AppUrl + p.Sender.UserName,
-			Color:       int(color),
+			Author: &DiscordEmbedAuthorObject{
+				Name:    p.Sender.UserName,
+				IconURL: p.Sender.AvatarUrl,
+			},
+		}},
+	}, nil
+}
+
+// getDiscordDeletePayload composes Discord payload for delete a branch or tag.
+func getDiscordDeletePayload(p *api.DeletePayload) (*DiscordPayload, error) {
+	refName := git.RefEndName(p.Ref)
+	repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
+	content := fmt.Sprintf("Deleted %s: %s/%s", p.RefType, repoLink, refName)
+
+	return &DiscordPayload{
+		Embeds: []*DiscordEmbedObject{{
+			Description: content,
+			URL:         setting.AppUrl + p.Sender.UserName,
 			Author: &DiscordEmbedAuthorObject{
 				Name:    p.Sender.UserName,
 				IconURL: p.Sender.AvatarUrl,
@@ -206,22 +219,32 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (
 	}, nil
 }
 
-func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*DiscordPayload, error) {
-	d := new(DiscordPayload)
-
+func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (payload *DiscordPayload, err error) {
 	slack := &SlackMeta{}
 	if err := json.Unmarshal([]byte(meta), &slack); err != nil {
-		return d, fmt.Errorf("GetDiscordPayload meta json: %v", err)
+		return nil, fmt.Errorf("json.Unmarshal: %v", err)
 	}
 
 	switch event {
 	case HOOK_EVENT_CREATE:
-		return getDiscordCreatePayload(p.(*api.CreatePayload), slack)
+		payload, err = getDiscordCreatePayload(p.(*api.CreatePayload))
+	case HOOK_EVENT_DELETE:
+		payload, err = getDiscordDeletePayload(p.(*api.DeletePayload))
 	case HOOK_EVENT_PUSH:
-		return getDiscordPushPayload(p.(*api.PushPayload), slack)
+		payload, err = getDiscordPushPayload(p.(*api.PushPayload), slack)
 	case HOOK_EVENT_PULL_REQUEST:
-		return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), slack)
+		payload, err = getDiscordPullRequestPayload(p.(*api.PullRequestPayload), slack)
+	}
+	if err != nil {
+		return nil, fmt.Errorf("event '%s': %v", event, err)
+	}
+
+	payload.Username = slack.Username
+	payload.AvatarURL = slack.IconURL
+	if len(payload.Embeds) > 0 {
+		color, _ := strconv.ParseInt(strings.TrimLeft(slack.Color, "#"), 16, 32)
+		payload.Embeds[0].Color = int(color)
 	}
 
-	return d, nil
+	return payload, nil
 }

+ 30 - 15
models/webhook_slack.go

@@ -69,19 +69,24 @@ func SlackLinkFormatter(url string, text string) string {
 	return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text))
 }
 
-func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) {
-	// Created tag/branch
+// getSlackCreatePayload composes Slack payload for create new branch or tag.
+func getSlackCreatePayload(p *api.CreatePayload) (*SlackPayload, error) {
 	refName := git.RefEndName(p.Ref)
-
 	repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
 	refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
 	text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName)
+	return &SlackPayload{
+		Text: text,
+	}, nil
+}
 
+// getSlackDeletePayload composes Slack payload for delete a branch or tag.
+func getSlackDeletePayload(p *api.DeletePayload) (*SlackPayload, error) {
+	refName := git.RefEndName(p.Ref)
+	repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
+	text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName)
 	return &SlackPayload{
-		Channel:  slack.Channel,
-		Text:     text,
-		Username: slack.Username,
-		IconURL:  slack.IconURL,
+		Text: text,
 	}, nil
 }
 
@@ -178,22 +183,32 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S
 	}, nil
 }
 
-func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) {
-	s := new(SlackPayload)
-
+func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (payload *SlackPayload, err error) {
 	slack := &SlackMeta{}
 	if err := json.Unmarshal([]byte(meta), &slack); err != nil {
-		return s, fmt.Errorf("GetSlackPayload meta json: %v", err)
+		return nil, fmt.Errorf("json.Unmarshal: %v", err)
 	}
 
 	switch event {
 	case HOOK_EVENT_CREATE:
-		return getSlackCreatePayload(p.(*api.CreatePayload), slack)
+		payload, err = getSlackCreatePayload(p.(*api.CreatePayload))
+	case HOOK_EVENT_DELETE:
+		payload, err = getSlackDeletePayload(p.(*api.DeletePayload))
 	case HOOK_EVENT_PUSH:
-		return getSlackPushPayload(p.(*api.PushPayload), slack)
+		payload, err = getSlackPushPayload(p.(*api.PushPayload), slack)
 	case HOOK_EVENT_PULL_REQUEST:
-		return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
+		payload, err = getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
+	}
+	if err != nil {
+		return nil, fmt.Errorf("event '%s': %v", event, err)
+	}
+
+	payload.Channel = slack.Channel
+	payload.Username = slack.Username
+	payload.IconURL = slack.IconURL
+	if len(payload.Attachments) > 0 {
+		payload.Attachments[0].Color = slack.Color
 	}
 
-	return s, nil
+	return payload, nil
 }

File diff suppressed because it is too large
+ 0 - 0
modules/bindata/bindata.go


+ 1 - 0
modules/form/repo.go

@@ -135,6 +135,7 @@ func (f *ProtectBranch) Validate(ctx *macaron.Context, errs binding.Errors) bind
 type Webhook struct {
 	Events      string
 	Create      bool
+	Delete      bool
 	Push        bool
 	PullRequest bool
 	Active      bool

+ 1 - 0
routers/repo/webhook.go

@@ -110,6 +110,7 @@ func ParseHookEvent(f form.Webhook) *models.HookEvent {
 		ChooseEvents:   f.ChooseEvents(),
 		HookEvents: models.HookEvents{
 			Create:      f.Create,
+			Delete:      f.Delete,
 			Push:        f.Push,
 			PullRequest: f.PullRequest,
 		},

+ 10 - 0
templates/repo/settings/webhook_settings.tmpl

@@ -32,6 +32,16 @@
 				</div>
 			</div>
 		</div>
+		<!-- Delete -->
+		<div class="seven wide column">
+			<div class="field">
+				<div class="ui checkbox">
+					<input class="hidden" name="delete" type="checkbox" tabindex="0" {{if .Webhook.Delete}}checked{{end}}>
+					<label>{{.i18n.Tr "repo.settings.event_delete"}}</label>
+					<span class="help">{{.i18n.Tr "repo.settings.event_delete_desc"}}</span>
+				</div>
+			</div>
+		</div>
 		<!-- Push -->
 		<div class="seven wide column">
 			<div class="field">

+ 1 - 1
vendor/github.com/gogits/go-gogs-client/gogs.go

@@ -14,7 +14,7 @@ import (
 )
 
 func Version() string {
-	return "0.12.6"
+	return "0.12.7"
 }
 
 // Client represents a Gogs API client.

+ 31 - 4
vendor/github.com/gogits/go-gogs-client/repo_hook.go

@@ -91,6 +91,7 @@ type PayloadCommit struct {
 
 var (
 	_ Payloader = &CreatePayload{}
+	_ Payloader = &DeletePayload{}
 	_ Payloader = &PushPayload{}
 	_ Payloader = &PullRequestPayload{}
 )
@@ -103,10 +104,11 @@ var (
 //         \/             \/     \/          \/
 
 type CreatePayload struct {
-	Ref     string      `json:"ref"`
-	RefType string      `json:"ref_type"`
-	Repo    *Repository `json:"repository"`
-	Sender  *User       `json:"sender"`
+	Ref           string      `json:"ref"`
+	RefType       string      `json:"ref_type"`
+	DefaultBranch string      `json:"default_branch"`
+	Repo          *Repository `json:"repository"`
+	Sender        *User       `json:"sender"`
 }
 
 func (p *CreatePayload) JSONPayload() ([]byte, error) {
@@ -133,6 +135,31 @@ func ParseCreateHook(raw []byte) (*CreatePayload, error) {
 	return hook, nil
 }
 
+// ________         .__          __
+// \______ \   ____ |  |   _____/  |_  ____
+//  |    |  \_/ __ \|  | _/ __ \   __\/ __ \
+//  |    `   \  ___/|  |_\  ___/|  | \  ___/
+// /_______  /\___  >____/\___  >__|  \___  >
+//         \/     \/          \/          \/
+
+type PusherType string
+
+const (
+	PUSHER_TYPE_USER PusherType = "user"
+)
+
+type DeletePayload struct {
+	Ref        string      `json:"ref"`
+	RefType    string      `json:"ref_type"`
+	PusherType PusherType  `json:"pusher_type"`
+	Repo       *Repository `json:"repository"`
+	Sender     *User       `json:"sender"`
+}
+
+func (p *DeletePayload) JSONPayload() ([]byte, error) {
+	return json.MarshalIndent(p, "", "  ")
+}
+
 // __________             .__
 // \______   \__ __  _____|  |__
 //  |     ___/  |  \/  ___/  |  \

+ 3 - 3
vendor/vendor.json

@@ -165,10 +165,10 @@
 			"revisionTime": "2017-02-19T18:16:29Z"
 		},
 		{
-			"checksumSHA1": "exKX51W/Hieq7OOmYK2gYn+Huuw=",
+			"checksumSHA1": "rtJ+nZ9VHh2X2Zon7wLczPAAc/s=",
 			"path": "github.com/gogits/go-gogs-client",
-			"revision": "f12fbacb5495120dc62dae7cfdf140d39bf6f715",
-			"revisionTime": "2017-02-24T06:16:35Z"
+			"revision": "ba630f557c8349952183305373fa89b155202bac",
+			"revisionTime": "2017-02-24T20:25:47Z"
 		},
 		{
 			"checksumSHA1": "p4yoFWgDiTfpu1JYgh26t6+VDTk=",

Some files were not shown because too many files changed in this diff