aboutsummaryrefslogtreecommitdiff
path: root/models
diff options
context:
space:
mode:
authorUnknwon <u@gogs.io>2017-02-25 03:35:26 -0500
committerUnknwon <u@gogs.io>2017-02-27 22:48:18 -0500
commitb06f2997489d58cc5a4375044e378c0565ea09d4 (patch)
treefd02710adc4ed574fe28aa173b2f02ac68bd6568 /models
parentbeea014343251fc9f379f996553c0b8030723464 (diff)
webhook: add fork event
Diffstat (limited to 'models')
-rw-r--r--models/action.go61
-rw-r--r--models/org.go8
-rw-r--r--models/repo.go50
-rw-r--r--models/webhook.go76
-rw-r--r--models/webhook_discord.go19
-rw-r--r--models/webhook_slack.go12
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: