summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorEthan Koenig <ethantkoenig@gmail.com>2017-09-03 01:20:24 -0700
committerLauris BH <lauris@nix.lv>2017-09-03 11:20:24 +0300
commitb689bb61804fddfd2df8df7250522b0fc227484e (patch)
tree6cd96dfc3c0c7b2a0b265217f2893aa606307b0a /models
parent79f7839633167bbb146a7b0d17ce0ec1c990d085 (diff)
downloadgitea-b689bb61804fddfd2df8df7250522b0fc227484e.tar.gz
gitea-b689bb61804fddfd2df8df7250522b0fc227484e.zip
Webhooks for repo creation/deletion (#1663)
* Webhooks for repo creation/deletion * add createHookTask * Add handles for GetSlackPayload and GetDiscordPayload
Diffstat (limited to 'models')
-rw-r--r--models/repo.go42
-rw-r--r--models/repo_test.go11
-rw-r--r--models/webhook.go48
-rw-r--r--models/webhook_discord.go33
-rw-r--r--models/webhook_slack.go26
5 files changed, 135 insertions, 25 deletions
diff --git a/models/repo.go b/models/repo.go
index 81c9d4a11f..a629b7311f 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -835,8 +835,8 @@ func wikiRemoteURL(remote string) string {
}
// MigrateRepository migrates a existing repository from other project hosting.
-func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
- repo, err := CreateRepository(u, CreateRepoOptions{
+func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, error) {
+ repo, err := CreateRepository(doer, u, CreateRepoOptions{
Name: opts.Name,
Description: opts.Description,
IsPrivate: opts.IsPrivate,
@@ -1202,7 +1202,7 @@ func IsUsableRepoName(name string) error {
return isUsableName(reservedRepoNames, reservedRepoPatterns, name)
}
-func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) {
+func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err error) {
if err = IsUsableRepoName(repo.Name); err != nil {
return err
}
@@ -1249,7 +1249,15 @@ func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) {
return fmt.Errorf("getOwnerTeam: %v", err)
} else if err = t.addRepository(e, repo); err != nil {
return fmt.Errorf("addRepository: %v", err)
+ } else if err = prepareWebhooks(e, repo, HookEventRepository, &api.RepositoryPayload{
+ Action: api.HookRepoCreated,
+ Repository: repo.APIFormat(AccessModeOwner),
+ Organization: u.APIFormat(),
+ Sender: doer.APIFormat(),
+ }); err != nil {
+ return fmt.Errorf("prepareWebhooks: %v", err)
}
+ go HookQueue.Add(repo.ID)
} else {
// Organization automatically called this in addRepository method.
if err = repo.recalculateAccesses(e); err != nil {
@@ -1266,8 +1274,8 @@ func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) {
return nil
}
-// CreateRepository creates a repository for given user or organization.
-func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error) {
+// CreateRepository creates a repository for the user/organization u.
+func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err error) {
if !u.CanCreateRepo() {
return nil, ErrReachLimitOfRepo{u.MaxRepoCreation}
}
@@ -1287,7 +1295,7 @@ func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error
return nil, err
}
- if err = createRepository(sess, u, repo); err != nil {
+ if err = createRepository(sess, doer, u, repo); err != nil {
return nil, err
}
@@ -1623,7 +1631,7 @@ func UpdateRepositoryUnits(repo *Repository, units []RepoUnit) (err error) {
}
// DeleteRepository deletes a repository for a user or organization.
-func DeleteRepository(uid, repoID int64) error {
+func DeleteRepository(doer *User, uid, repoID int64) error {
// In case is a organization.
org, err := GetUserByID(uid)
if err != nil {
@@ -1781,6 +1789,18 @@ func DeleteRepository(uid, repoID int64) error {
return fmt.Errorf("Commit: %v", err)
}
+ if org.IsOrganization() {
+ if err = PrepareWebhooks(repo, HookEventRepository, &api.RepositoryPayload{
+ Action: api.HookRepoDeleted,
+ Repository: repo.APIFormat(AccessModeOwner),
+ Organization: org.APIFormat(),
+ Sender: doer.APIFormat(),
+ }); err != nil {
+ return err
+ }
+ go HookQueue.Add(repo.ID)
+ }
+
return nil
}
@@ -1974,7 +1994,7 @@ func gatherMissingRepoRecords() ([]*Repository, error) {
}
// DeleteMissingRepositories deletes all repository records that lost Git files.
-func DeleteMissingRepositories() error {
+func DeleteMissingRepositories(doer *User) error {
repos, err := gatherMissingRepoRecords()
if err != nil {
return fmt.Errorf("gatherMissingRepoRecords: %v", err)
@@ -1986,7 +2006,7 @@ func DeleteMissingRepositories() error {
for _, repo := range repos {
log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID)
- if err := DeleteRepository(repo.OwnerID, repo.ID); err != nil {
+ if err := DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil {
return fmt.Errorf("CreateRepositoryNotice: %v", err)
}
@@ -2226,7 +2246,7 @@ func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) {
}
// ForkRepository forks a repository
-func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) {
+func ForkRepository(doer, u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) {
forkedRepo, err := oldRepo.GetUserFork(u.ID)
if err != nil {
return nil, err
@@ -2256,7 +2276,7 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
return nil, err
}
- if err = createRepository(sess, u, repo); err != nil {
+ if err = createRepository(sess, doer, u, repo); err != nil {
return nil, err
}
diff --git a/models/repo_test.go b/models/repo_test.go
index 4b6fd440aa..c1eb4e2a79 100644
--- a/models/repo_test.go
+++ b/models/repo_test.go
@@ -118,13 +118,12 @@ func TestGetUserFork(t *testing.T) {
func TestForkRepository(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
- // User13 has repo 11 forked from repo10
- repo, err := GetRepositoryByID(10)
- assert.NoError(t, err)
- assert.NotNil(t, repo)
+ // user 13 has already forked repo10
+ user := AssertExistsAndLoadBean(t, &User{ID: 13}).(*User)
+ repo := AssertExistsAndLoadBean(t, &Repository{ID: 10}).(*Repository)
- repo, err = ForkRepository(&User{ID: 13}, repo, "test", "test")
- assert.Nil(t, repo)
+ fork, err := ForkRepository(user, user, repo, "test", "test")
+ assert.Nil(t, fork)
assert.Error(t, err)
assert.True(t, IsErrRepoAlreadyExist(err))
}
diff --git a/models/webhook.go b/models/webhook.go
index 9e5742cb52..fa0c498f33 100644
--- a/models/webhook.go
+++ b/models/webhook.go
@@ -68,6 +68,7 @@ type HookEvents struct {
Create bool `json:"create"`
Push bool `json:"push"`
PullRequest bool `json:"pull_request"`
+ Repository bool `json:"repository"`
}
// HookEvent represents events that will delivery hook.
@@ -188,6 +189,12 @@ func (w *Webhook) HasPullRequestEvent() bool {
(w.ChooseEvents && w.HookEvents.PullRequest)
}
+// HasRepositoryEvent returns if hook enabled repository event.
+func (w *Webhook) HasRepositoryEvent() bool {
+ return w.SendEverything ||
+ (w.ChooseEvents && w.HookEvents.Repository)
+}
+
// EventsArray returns an array of hook events
func (w *Webhook) EventsArray() []string {
events := make([]string, 0, 3)
@@ -246,8 +253,12 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) {
// GetActiveWebhooksByRepoID returns all active webhooks of repository.
func GetActiveWebhooksByRepoID(repoID int64) ([]*Webhook, error) {
+ return getActiveWebhooksByRepoID(x, repoID)
+}
+
+func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5)
- return webhooks, x.Where("is_active=?", true).
+ return webhooks, e.Where("is_active=?", true).
Find(&webhooks, &Webhook{RepoID: repoID})
}
@@ -259,7 +270,11 @@ func GetWebhooksByRepoID(repoID int64) ([]*Webhook, error) {
// GetActiveWebhooksByOrgID returns all active webhooks for an organization.
func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) {
- err = x.
+ return getActiveWebhooksByOrgID(x, orgID)
+}
+
+func getActiveWebhooksByOrgID(e Engine, orgID int64) (ws []*Webhook, err error) {
+ err = e.
Where("org_id=?", orgID).
And("is_active=?", true).
Find(&ws)
@@ -379,6 +394,7 @@ const (
HookEventCreate HookEventType = "create"
HookEventPush HookEventType = "push"
HookEventPullRequest HookEventType = "pull_request"
+ HookEventRepository HookEventType = "repository"
)
// HookRequest represents hook task request information.
@@ -479,13 +495,17 @@ func HookTasks(hookID int64, page int) ([]*HookTask, error) {
// CreateHookTask creates a new hook task,
// it handles conversion from Payload to PayloadContent.
func CreateHookTask(t *HookTask) error {
+ return createHookTask(x, t)
+}
+
+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
}
@@ -497,6 +517,10 @@ func UpdateHookTask(t *HookTask) error {
// PrepareWebhook adds special webhook to task queue for given payload.
func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Payloader) error {
+ return prepareWebhook(x, w, repo, event, p)
+}
+
+func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType, p api.Payloader) error {
switch event {
case HookEventCreate:
if !w.HasCreateEvent() {
@@ -510,6 +534,10 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay
if !w.HasPullRequestEvent() {
return nil
}
+ case HookEventRepository:
+ if !w.HasRepositoryEvent() {
+ return nil
+ }
}
var payloader api.Payloader
@@ -531,7 +559,7 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay
payloader = p
}
- if err = CreateHookTask(&HookTask{
+ if err = createHookTask(e, &HookTask{
RepoID: repo.ID,
HookID: w.ID,
Type: w.HookTaskType,
@@ -548,15 +576,19 @@ func PrepareWebhook(w *Webhook, repo *Repository, event HookEventType, p api.Pay
// PrepareWebhooks adds new webhooks to task queue for given payload.
func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error {
- ws, err := GetActiveWebhooksByRepoID(repo.ID)
+ return prepareWebhooks(x, repo, event, p)
+}
+
+func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payloader) error {
+ ws, err := getActiveWebhooksByRepoID(e, repo.ID)
if err != nil {
return fmt.Errorf("GetActiveWebhooksByRepoID: %v", err)
}
// check if repo belongs to org and append additional webhooks
- if repo.MustOwner().IsOrganization() {
+ if repo.mustOwner(e).IsOrganization() {
// get hooks for org
- orgHooks, err := GetActiveWebhooksByOrgID(repo.OwnerID)
+ orgHooks, err := getActiveWebhooksByOrgID(e, repo.OwnerID)
if err != nil {
return fmt.Errorf("GetActiveWebhooksByOrgID: %v", err)
}
@@ -568,7 +600,7 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
}
for _, w := range ws {
- if err = PrepareWebhook(w, repo, event, p); err != nil {
+ if err = prepareWebhook(e, w, repo, event, p); err != nil {
return err
}
}
diff --git a/models/webhook_discord.go b/models/webhook_discord.go
index 4426a945b4..631c8174e9 100644
--- a/models/webhook_discord.go
+++ b/models/webhook_discord.go
@@ -228,6 +228,37 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta)
}, nil
}
+func getDiscordRepositoryPayload(p *api.RepositoryPayload, meta *DiscordMeta) (*DiscordPayload, error) {
+ var title, url string
+ var color int
+ switch p.Action {
+ case api.HookRepoCreated:
+ title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
+ url = p.Repository.HTMLURL
+ color = successColor
+ case api.HookRepoDeleted:
+ title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
+ color = warnColor
+ }
+
+ return &DiscordPayload{
+ Username: meta.Username,
+ AvatarURL: meta.IconURL,
+ Embeds: []DiscordEmbed{
+ {
+ Title: title,
+ URL: url,
+ Color: color,
+ Author: DiscordEmbedAuthor{
+ Name: p.Sender.UserName,
+ URL: setting.AppURL + p.Sender.UserName,
+ IconURL: p.Sender.AvatarURL,
+ },
+ },
+ },
+ }, nil
+}
+
// GetDiscordPayload converts a discord webhook into a DiscordPayload
func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*DiscordPayload, error) {
s := new(DiscordPayload)
@@ -244,6 +275,8 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*Disc
return getDiscordPushPayload(p.(*api.PushPayload), discord)
case HookEventPullRequest:
return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord)
+ case HookEventRepository:
+ return getDiscordRepositoryPayload(p.(*api.RepositoryPayload), discord)
}
return s, nil
diff --git a/models/webhook_slack.go b/models/webhook_slack.go
index b297fefd66..ca43cfd427 100644
--- a/models/webhook_slack.go
+++ b/models/webhook_slack.go
@@ -189,6 +189,30 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S
}, nil
}
+func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) {
+ senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
+ var text, title, attachmentText string
+ switch p.Action {
+ case api.HookRepoCreated:
+ text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink)
+ title = p.Repository.HTMLURL
+ case api.HookRepoDeleted:
+ text = fmt.Sprintf("[%s] Repository deleted by %s", p.Repository.FullName, senderLink)
+ }
+
+ return &SlackPayload{
+ Channel: slack.Channel,
+ Text: text,
+ Username: slack.Username,
+ IconURL: slack.IconURL,
+ Attachments: []SlackAttachment{{
+ Color: slack.Color,
+ Title: title,
+ Text: attachmentText,
+ }},
+ }, nil
+}
+
// GetSlackPayload converts a slack webhook into a SlackPayload
func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) {
s := new(SlackPayload)
@@ -205,6 +229,8 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackP
return getSlackPushPayload(p.(*api.PushPayload), slack)
case HookEventPullRequest:
return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
+ case HookEventRepository:
+ return getSlackRepositoryPayload(p.(*api.RepositoryPayload), slack)
}
return s, nil