diff options
author | Unknwon <u@gogs.io> | 2017-02-24 15:35:20 -0500 |
---|---|---|
committer | Unknwon <u@gogs.io> | 2017-02-27 22:47:21 -0500 |
commit | f0086e66aef31573c7f00e0c3cd7725fb6d65f5c (patch) | |
tree | 865fd5787c6f714add7be9da1e8cf19d4bc887cc /models | |
parent | 7fe13e72d8f8c0e61abbc4068ecb965347fe0331 (diff) |
webhook: able to detect delete branch or tag (#2315)
Diffstat (limited to 'models')
-rw-r--r-- | models/action.go | 74 | ||||
-rw-r--r-- | models/update.go | 45 | ||||
-rw-r--r-- | models/webhook.go | 12 | ||||
-rw-r--r-- | models/webhook_discord.go | 53 | ||||
-rw-r--r-- | models/webhook_slack.go | 45 |
5 files changed, 154 insertions, 75 deletions
diff --git a/models/action.go b/models/action.go index d0a4b545..f10da4c7 100644 --- a/models/action.go +++ b/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 diff --git a/models/update.go b/models/update.go index 507150be..77336ac9 100644 --- a/models/update.go +++ b/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 } diff --git a/models/webhook.go b/models/webhook.go index feabf03b..a55fcfa7 100644 --- a/models/webhook.go +++ b/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 diff --git a/models/webhook_discord.go b/models/webhook_discord.go index 27b01bc3..3755d3b5 100644 --- a/models/webhook_discord.go +++ b/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 } diff --git a/models/webhook_slack.go b/models/webhook_slack.go index 5943a9e5..f785bb68 100644 --- a/models/webhook_slack.go +++ b/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 } |