diff options
author | Unknwon <u@gogs.io> | 2017-02-25 03:35:26 -0500 |
---|---|---|
committer | Unknwon <u@gogs.io> | 2017-02-27 22:48:18 -0500 |
commit | b06f2997489d58cc5a4375044e378c0565ea09d4 (patch) | |
tree | fd02710adc4ed574fe28aa173b2f02ac68bd6568 /models | |
parent | beea014343251fc9f379f996553c0b8030723464 (diff) |
webhook: add fork event
Diffstat (limited to 'models')
-rw-r--r-- | models/action.go | 61 | ||||
-rw-r--r-- | models/org.go | 8 | ||||
-rw-r--r-- | models/repo.go | 50 | ||||
-rw-r--r-- | models/webhook.go | 76 | ||||
-rw-r--r-- | models/webhook_discord.go | 19 | ||||
-rw-r--r-- | models/webhook_slack.go | 12 |
6 files changed, 143 insertions, 83 deletions
diff --git a/models/action.go b/models/action.go index bbed431c..b68e9a47 100644 --- a/models/action.go +++ b/models/action.go @@ -46,6 +46,7 @@ const ( ACTION_CREATE_BRANCH // 16 ACTION_DELETE_BRANCH // 17 ACTION_DELETE_TAG // 18 + ACTION_FORK_REPO // 19 ) var ( @@ -177,20 +178,20 @@ func (a *Action) GetIssueContent() string { } func newRepoAction(e Engine, doer, owner *User, repo *Repository) (err error) { - if err = notifyWatchers(e, &Action{ + opType := ACTION_CREATE_REPO + if repo.IsFork { + opType = ACTION_FORK_REPO + } + + return notifyWatchers(e, &Action{ ActUserID: doer.ID, ActUserName: doer.Name, - OpType: ACTION_CREATE_REPO, + OpType: opType, RepoID: repo.ID, RepoUserName: repo.Owner.Name, RepoName: repo.Name, IsPrivate: repo.IsPrivate, - }); err != nil { - return fmt.Errorf("notify watchers '%d/%d': %v", owner.ID, repo.ID, err) - } - - log.Trace("action.newRepoAction: %s/%s", owner.Name, repo.Name) - return err + }) } // NewRepoAction adds new action for creating repository. @@ -489,12 +490,6 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { return fmt.Errorf("Marshal: %v", err) } - defer func() { - // It's safe to fail when the whole function is called during hook execution - // because resource released after exit. - go HookQueue.Add(repo.ID) - }() - refName := git.RefEndName(opts.RefFullName) action := &Action{ ActUserID: pusher.ID, @@ -512,9 +507,6 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { switch opType { case ACTION_COMMIT_REPO: // Push if isDelRef { - action.OpType = ACTION_DELETE_BRANCH - MustNotifyWatchers(action) - if err = PrepareWebhooks(repo, HOOK_EVENT_DELETE, &api.DeletePayload{ Ref: refName, RefType: "branch", @@ -525,15 +517,17 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { return fmt.Errorf("PrepareWebhooks.(delete branch): %v", err) } + action.OpType = ACTION_DELETE_BRANCH + if err = NotifyWatchers(action); err != nil { + return fmt.Errorf("NotifyWatchers.(delete branch): %v", err) + } + // Delete branch doesn't have anything to push or compare return nil } compareURL := setting.AppUrl + opts.Commits.CompareURL if isNewRef { - action.OpType = ACTION_CREATE_BRANCH - MustNotifyWatchers(action) - compareURL = "" if err = PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ Ref: refName, @@ -544,10 +538,13 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { }); err != nil { return fmt.Errorf("PrepareWebhooks.(new branch): %v", err) } + + action.OpType = ACTION_CREATE_BRANCH + if err = NotifyWatchers(action); err != nil { + return fmt.Errorf("NotifyWatchers.(new branch): %v", err) + } } - action.OpType = ACTION_COMMIT_REPO - MustNotifyWatchers(action) if err = PrepareWebhooks(repo, HOOK_EVENT_PUSH, &api.PushPayload{ Ref: opts.RefFullName, Before: opts.OldCommitID, @@ -561,11 +558,13 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { return fmt.Errorf("PrepareWebhooks.(new commit): %v", err) } + action.OpType = ACTION_COMMIT_REPO + if err = NotifyWatchers(action); err != nil { + return fmt.Errorf("NotifyWatchers.(new commit): %v", err) + } + case ACTION_PUSH_TAG: // Tag if isDelRef { - action.OpType = ACTION_DELETE_TAG - MustNotifyWatchers(action) - if err = PrepareWebhooks(repo, HOOK_EVENT_DELETE, &api.DeletePayload{ Ref: refName, RefType: "tag", @@ -575,11 +574,14 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { }); err != nil { return fmt.Errorf("PrepareWebhooks.(delete tag): %v", err) } + + action.OpType = ACTION_DELETE_TAG + if err = NotifyWatchers(action); err != nil { + return fmt.Errorf("NotifyWatchers.(delete tag): %v", err) + } return nil } - action.OpType = ACTION_PUSH_TAG - MustNotifyWatchers(action) if err = PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ Ref: refName, RefType: "tag", @@ -589,6 +591,11 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { }); err != nil { return fmt.Errorf("PrepareWebhooks.(new tag): %v", err) } + + action.OpType = ACTION_PUSH_TAG + if err = NotifyWatchers(action); err != nil { + return fmt.Errorf("NotifyWatchers.(new tag): %v", err) + } } return nil diff --git a/models/org.go b/models/org.go index 0046aaa4..1deba7f4 100644 --- a/models/org.go +++ b/models/org.go @@ -20,8 +20,8 @@ var ( ) // IsOwnedBy returns true if given user is in the owner team. -func (org *User) IsOwnedBy(uid int64) bool { - return IsOrganizationOwner(org.ID, uid) +func (org *User) IsOwnedBy(userID int64) bool { + return IsOrganizationOwner(org.ID, userID) } // IsOrgMember returns true if given user is member of organization. @@ -246,8 +246,8 @@ type OrgUser struct { } // IsOrganizationOwner returns true if given user is in the owner team. -func IsOrganizationOwner(orgId, uid int64) bool { - has, _ := x.Where("is_owner=?", true).And("uid=?", uid).And("org_id=?", orgId).Get(new(OrgUser)) +func IsOrganizationOwner(orgID, userID int64) bool { + has, _ := x.Where("is_owner = ?", true).And("uid = ?", userID).And("org_id = ?", orgID).Get(new(OrgUser)) return has } diff --git a/models/repo.go b/models/repo.go index 37e8756b..7fee5154 100644 --- a/models/repo.go +++ b/models/repo.go @@ -2068,24 +2068,27 @@ func (repo *Repository) GetWatchers(page int) ([]*User, error) { func notifyWatchers(e Engine, act *Action) error { // Add feeds for user self and all watchers. - watches, err := getWatchers(e, act.RepoID) + watchers, err := getWatchers(e, act.RepoID) if err != nil { - return fmt.Errorf("get watchers: %v", err) + return fmt.Errorf("getWatchers: %v", err) } + // Reset ID to reuse Action object + act.ID = 0 + // Add feed for actioner. act.UserID = act.ActUserID if _, err = e.Insert(act); err != nil { - return fmt.Errorf("insert new actioner: %v", err) + return fmt.Errorf("insert new action: %v", err) } - for i := range watches { - if act.ActUserID == watches[i].UserID { + for i := range watchers { + if act.ActUserID == watchers[i].UserID { continue } act.ID = 0 - act.UserID = watches[i].UserID + act.UserID = watchers[i].UserID if _, err = e.Insert(act); err != nil { return fmt.Errorf("insert new action: %v", err) } @@ -2098,13 +2101,6 @@ func NotifyWatchers(act *Action) error { return notifyWatchers(x, act) } -func MustNotifyWatchers(act *Action) { - act.ID = 0 // Reset ID to reuse Action object - if err := NotifyWatchers(act); err != nil { - log.Error(2, "NotifyWatchers: %v", err) - } -} - // _________ __ // / _____// |______ _______ // \_____ \\ __\__ \\_ __ \ @@ -2168,24 +2164,26 @@ func (repo *Repository) GetStargazers(page int) ([]*User, error) { // \___ / \____/|__| |__|_ \ // \/ \/ -// HasForkedRepo checks if given user has already forked a repository with given ID. +// HasForkedRepo checks if given user has already forked a repository. +// When user has already forked, it returns true along with the repository. func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) { repo := new(Repository) - has, _ := x.Where("owner_id=? AND fork_id=?", ownerID, repoID).Get(repo) + has, _ := x.Where("owner_id = ? AND fork_id = ?", ownerID, repoID).Get(repo) return repo, has } -func ForkRepository(doer, owner *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) { +// ForkRepository creates a fork of target repository under another user domain. +func ForkRepository(doer, owner *User, baseRepo *Repository, name, desc string) (_ *Repository, err error) { repo := &Repository{ OwnerID: owner.ID, Owner: owner, Name: name, LowerName: strings.ToLower(name), Description: desc, - DefaultBranch: oldRepo.DefaultBranch, - IsPrivate: oldRepo.IsPrivate, + DefaultBranch: baseRepo.DefaultBranch, + IsPrivate: baseRepo.IsPrivate, IsFork: true, - ForkID: oldRepo.ID, + ForkID: baseRepo.ID, } sess := x.NewSession() @@ -2196,16 +2194,14 @@ func ForkRepository(doer, owner *User, oldRepo *Repository, name, desc string) ( if err = createRepository(sess, doer, owner, repo); err != nil { return nil, err - } - - if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", oldRepo.ID); err != nil { + } else if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", baseRepo.ID); err != nil { return nil, err } - repoPath := RepoPath(owner.Name, repo.Name) + repoPath := repo.repoPath(sess) _, stderr, err := process.ExecTimeout(10*time.Minute, fmt.Sprintf("ForkRepository 'git clone': %s/%s", owner.Name, repo.Name), - "git", "clone", "--bare", oldRepo.RepoPath(), repoPath) + "git", "clone", "--bare", baseRepo.RepoPath(), repoPath) if err != nil { return nil, fmt.Errorf("git clone: %v", stderr) } @@ -2219,6 +2215,12 @@ func ForkRepository(doer, owner *User, oldRepo *Repository, name, desc string) ( if err = createDelegateHooks(repoPath); err != nil { return nil, fmt.Errorf("createDelegateHooks: %v", err) + } else if err = prepareWebhooks(sess, baseRepo, HOOK_EVENT_FORK, &api.ForkPayload{ + Forkee: repo.APIFormat(nil), + Repo: baseRepo.APIFormat(nil), + Sender: doer.APIFormat(), + }); err != nil { + return nil, fmt.Errorf("prepareWebhooks: %v", err) } return repo, sess.Commit() diff --git a/models/webhook.go b/models/webhook.go index a55fcfa7..0f475df5 100644 --- a/models/webhook.go +++ b/models/webhook.go @@ -64,6 +64,7 @@ func IsValidHookContentType(name string) bool { type HookEvents struct { Create bool `json:"create"` Delete bool `json:"delete"` + Fork bool `json:"fork"` Push bool `json:"push"` PullRequest bool `json:"pull_request"` } @@ -163,6 +164,12 @@ func (w *Webhook) HasDeleteEvent() bool { (w.ChooseEvents && w.HookEvents.Delete) } +// HasForkEvent returns true if hook enabled fork event. +func (w *Webhook) HasForkEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.Fork) +} + // HasPushEvent returns true if hook enabled push event. func (w *Webhook) HasPushEvent() bool { return w.PushOnly || w.SendEverything || @@ -176,15 +183,21 @@ func (w *Webhook) HasPullRequestEvent() bool { } func (w *Webhook) EventsArray() []string { - events := make([]string, 0, 3) + events := make([]string, 0, 5) if w.HasCreateEvent() { - events = append(events, "create") + events = append(events, string(HOOK_EVENT_CREATE)) + } + if w.HasDeleteEvent() { + events = append(events, string(HOOK_EVENT_DELETE)) + } + if w.HasForkEvent() { + events = append(events, string(HOOK_EVENT_FORK)) } if w.HasPushEvent() { - events = append(events, "push") + events = append(events, string(HOOK_EVENT_PUSH)) } if w.HasPullRequestEvent() { - events = append(events, "pull_request") + events = append(events, string(HOOK_EVENT_PULL_REQUEST)) } return events } @@ -232,10 +245,10 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) { }) } -// GetActiveWebhooksByRepoID returns all active webhooks of repository. -func GetActiveWebhooksByRepoID(repoID int64) ([]*Webhook, error) { +// getActiveWebhooksByRepoID returns all active webhooks of repository. +func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) { webhooks := make([]*Webhook, 0, 5) - return webhooks, x.Where("repo_id = ?", repoID).And("is_active = ?", true).Find(&webhooks) + return webhooks, e.Where("repo_id = ?", repoID).And("is_active = ?", true).Find(&webhooks) } // GetWebhooksByRepoID returns all webhooks of a repository. @@ -290,10 +303,10 @@ func GetWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) { return ws, err } -// GetActiveWebhooksByOrgID returns all active webhooks for an organization. -func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) { - err = x.Where("org_id=?", orgID).And("is_active=?", true).Find(&ws) - return ws, err +// getActiveWebhooksByOrgID returns all active webhooks for an organization. +func getActiveWebhooksByOrgID(e Engine, orgID int64) ([]*Webhook, error) { + ws := make([]*Webhook, 3) + return ws, e.Where("org_id=?", orgID).And("is_active=?", true).Find(&ws) } // ___ ___ __ ___________ __ @@ -345,6 +358,7 @@ type HookEventType string const ( HOOK_EVENT_CREATE HookEventType = "create" HOOK_EVENT_DELETE HookEventType = "delete" + HOOK_EVENT_FORK HookEventType = "fork" HOOK_EVENT_PUSH HookEventType = "push" HOOK_EVENT_PULL_REQUEST HookEventType = "pull_request" ) @@ -438,16 +452,16 @@ func HookTasks(hookID int64, page int) ([]*HookTask, error) { return tasks, x.Limit(setting.Webhook.PagingNum, (page-1)*setting.Webhook.PagingNum).Where("hook_id=?", hookID).Desc("id").Find(&tasks) } -// CreateHookTask creates a new hook task, +// createHookTask creates a new hook task, // it handles conversion from Payload to PayloadContent. -func CreateHookTask(t *HookTask) error { +func createHookTask(e Engine, t *HookTask) error { data, err := t.Payloader.JSONPayload() if err != nil { return err } t.UUID = gouuid.NewV4().String() t.PayloadContent = string(data) - _, err = x.Insert(t) + _, err = e.Insert(t) return err } @@ -457,8 +471,8 @@ func UpdateHookTask(t *HookTask) error { return err } -// prepareWebhooks adds list of webhooks to task queue. -func prepareWebhooks(repo *Repository, event HookEventType, p api.Payloader, webhooks []*Webhook) (err error) { +// prepareHookTasks adds list of webhooks to task queue. +func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Payloader, webhooks []*Webhook) (err error) { if len(webhooks) == 0 { return nil } @@ -511,7 +525,7 @@ func prepareWebhooks(repo *Repository, event HookEventType, p api.Payloader, web signature = hex.EncodeToString(sig.Sum(nil)) } - if err = CreateHookTask(&HookTask{ + if err = createHookTask(e, &HookTask{ RepoID: repo.ID, HookID: w.ID, Type: w.HookTaskType, @@ -522,29 +536,37 @@ func prepareWebhooks(repo *Repository, event HookEventType, p api.Payloader, web EventType: event, IsSSL: w.IsSSL, }); err != nil { - return fmt.Errorf("CreateHookTask: %v", err) + return fmt.Errorf("createHookTask: %v", err) } } + + // It's safe to fail when the whole function is called during hook execution + // because resource released after exit. + go HookQueue.Add(repo.ID) return nil } -// PrepareWebhooks adds all active webhooks to task queue. -func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error { - webhooks, err := GetActiveWebhooksByRepoID(repo.ID) +func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payloader) error { + webhooks, err := getActiveWebhooksByRepoID(e, repo.ID) if err != nil { - return fmt.Errorf("GetActiveWebhooksByRepoID [%d]: %v", repo.ID, err) + return fmt.Errorf("getActiveWebhooksByRepoID [%d]: %v", repo.ID, err) } // check if repo belongs to org and append additional webhooks - if repo.MustOwner().IsOrganization() { + if repo.mustOwner(e).IsOrganization() { // get hooks for org - orgws, err := GetActiveWebhooksByOrgID(repo.OwnerID) + orgws, err := getActiveWebhooksByOrgID(e, repo.OwnerID) if err != nil { - return fmt.Errorf("GetActiveWebhooksByOrgID [%d]: %v", repo.OwnerID, err) + return fmt.Errorf("getActiveWebhooksByOrgID [%d]: %v", repo.OwnerID, err) } webhooks = append(webhooks, orgws...) } - return prepareWebhooks(repo, event, p, webhooks) + return prepareHookTasks(e, repo, event, p, webhooks) +} + +// PrepareWebhooks adds all active webhooks to task queue. +func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error { + return prepareWebhooks(x, repo, event, p) } // TestWebhook adds the test webhook matches the ID to task queue. @@ -553,7 +575,7 @@ func TestWebhook(repo *Repository, event HookEventType, p api.Payloader, webhook if err != nil { return fmt.Errorf("GetWebhookOfRepoByID [repo_id: %d, id: %d]: %v", repo.ID, webhookID, err) } - return prepareWebhooks(repo, event, p, []*Webhook{webhook}) + return prepareHookTasks(x, repo, event, p, []*Webhook{webhook}) } func (t *HookTask) deliver() { diff --git a/models/webhook_discord.go b/models/webhook_discord.go index 3755d3b5..d26beaaf 100644 --- a/models/webhook_discord.go +++ b/models/webhook_discord.go @@ -74,7 +74,6 @@ func getDiscordCreatePayload(p *api.CreatePayload) (*DiscordPayload, error) { 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) - return &DiscordPayload{ Embeds: []*DiscordEmbedObject{{ Description: content, @@ -92,7 +91,23 @@ 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, + }, + }}, + }, nil +} +// getDiscordForkPayload composes Discord payload for forked by a repository. +func getDiscordForkPayload(p *api.ForkPayload) (*DiscordPayload, error) { + baseLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) + forkLink := DiscordLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName) + content := fmt.Sprintf("%s is forked to %s", baseLink, forkLink) return &DiscordPayload{ Embeds: []*DiscordEmbedObject{{ Description: content, @@ -230,6 +245,8 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (paylo payload, err = getDiscordCreatePayload(p.(*api.CreatePayload)) case HOOK_EVENT_DELETE: payload, err = getDiscordDeletePayload(p.(*api.DeletePayload)) + case HOOK_EVENT_FORK: + payload, err = getDiscordForkPayload(p.(*api.ForkPayload)) case HOOK_EVENT_PUSH: payload, err = getDiscordPushPayload(p.(*api.PushPayload), slack) case HOOK_EVENT_PULL_REQUEST: diff --git a/models/webhook_slack.go b/models/webhook_slack.go index f785bb68..6f81c1c0 100644 --- a/models/webhook_slack.go +++ b/models/webhook_slack.go @@ -90,6 +90,16 @@ func getSlackDeletePayload(p *api.DeletePayload) (*SlackPayload, error) { }, nil } +// getSlackForkPayload composes Slack payload for forked by a repository. +func getSlackForkPayload(p *api.ForkPayload) (*SlackPayload, error) { + baseLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) + forkLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName) + text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink) + return &SlackPayload{ + Text: text, + }, nil +} + func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) { // n new commits var ( @@ -194,6 +204,8 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (payload payload, err = getSlackCreatePayload(p.(*api.CreatePayload)) case HOOK_EVENT_DELETE: payload, err = getSlackDeletePayload(p.(*api.DeletePayload)) + case HOOK_EVENT_FORK: + payload, err = getSlackForkPayload(p.(*api.ForkPayload)) case HOOK_EVENT_PUSH: payload, err = getSlackPushPayload(p.(*api.PushPayload), slack) case HOOK_EVENT_PULL_REQUEST: |