Browse Source

webhook: add fork event

pull/4240/head
Unknwon 8 years ago
parent
commit
b06f299748
No known key found for this signature in database
GPG Key ID: 25B575AE3213B2B3
  1. 3
      conf/locale/locale_en-US.ini
  2. 61
      models/action.go
  3. 8
      models/org.go
  4. 50
      models/repo.go
  5. 76
      models/webhook.go
  6. 19
      models/webhook_discord.go
  7. 12
      models/webhook_slack.go
  8. 4
      modules/bindata/bindata.go
  9. 3
      modules/form/repo.go
  10. 2
      modules/template/template.go
  11. 52
      routers/repo/pull.go
  12. 2
      routers/repo/repo.go
  13. 1
      routers/repo/webhook.go
  14. 2
      templates/repo/create.tmpl
  15. 2
      templates/repo/pulls/fork.tmpl
  16. 10
      templates/repo/settings/webhook_settings.tmpl
  17. 2
      templates/user/dashboard/feeds.tmpl
  18. 17
      vendor/github.com/gogits/go-gogs-client/repo_hook.go
  19. 6
      vendor/vendor.json

3
conf/locale/locale_en-US.ini

@ -758,6 +758,8 @@ 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 = Delete
settings.event_delete_desc = Branch or tag deleted settings.event_delete_desc = Branch or tag deleted
settings.event_fork = Fork
settings.event_fork_desc = Repository forked
settings.event_pull_request = Pull Request 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_pull_request_desc = Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
settings.event_push = Push settings.event_push = Push
@ -1208,6 +1210,7 @@ notices.delete_success = System notices have been deleted successfully.
[action] [action]
create_repo = created repository <a href="%s">%s</a> create_repo = created repository <a href="%s">%s</a>
fork_repo = forked a repository to <a href="%s">%s</a>
rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a> rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
commit_repo = pushed to <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a> commit_repo = pushed to <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a>
compare_commits = View comparison for these %d commits compare_commits = View comparison for these %d commits

61
models/action.go

@ -46,6 +46,7 @@ const (
ACTION_CREATE_BRANCH // 16 ACTION_CREATE_BRANCH // 16
ACTION_DELETE_BRANCH // 17 ACTION_DELETE_BRANCH // 17
ACTION_DELETE_TAG // 18 ACTION_DELETE_TAG // 18
ACTION_FORK_REPO // 19
) )
var ( var (
@ -177,20 +178,20 @@ func (a *Action) GetIssueContent() string {
} }
func newRepoAction(e Engine, doer, owner *User, repo *Repository) (err error) { 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, ActUserID: doer.ID,
ActUserName: doer.Name, ActUserName: doer.Name,
OpType: ACTION_CREATE_REPO, OpType: opType,
RepoID: repo.ID, RepoID: repo.ID,
RepoUserName: repo.Owner.Name, RepoUserName: repo.Owner.Name,
RepoName: repo.Name, RepoName: repo.Name,
IsPrivate: repo.IsPrivate, 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. // NewRepoAction adds new action for creating repository.
@ -489,12 +490,6 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
return fmt.Errorf("Marshal: %v", err) 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) refName := git.RefEndName(opts.RefFullName)
action := &Action{ action := &Action{
ActUserID: pusher.ID, ActUserID: pusher.ID,
@ -512,9 +507,6 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
switch opType { switch opType {
case ACTION_COMMIT_REPO: // Push case ACTION_COMMIT_REPO: // Push
if isDelRef { if isDelRef {
action.OpType = ACTION_DELETE_BRANCH
MustNotifyWatchers(action)
if err = PrepareWebhooks(repo, HOOK_EVENT_DELETE, &api.DeletePayload{ if err = PrepareWebhooks(repo, HOOK_EVENT_DELETE, &api.DeletePayload{
Ref: refName, Ref: refName,
RefType: "branch", RefType: "branch",
@ -525,15 +517,17 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
return fmt.Errorf("PrepareWebhooks.(delete branch): %v", err) 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 // Delete branch doesn't have anything to push or compare
return nil return nil
} }
compareURL := setting.AppUrl + opts.Commits.CompareURL compareURL := setting.AppUrl + opts.Commits.CompareURL
if isNewRef { if isNewRef {
action.OpType = ACTION_CREATE_BRANCH
MustNotifyWatchers(action)
compareURL = "" compareURL = ""
if err = PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ if err = PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
Ref: refName, Ref: refName,
@ -544,10 +538,13 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
}); err != nil { }); err != nil {
return fmt.Errorf("PrepareWebhooks.(new branch): %v", err) 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{ if err = PrepareWebhooks(repo, HOOK_EVENT_PUSH, &api.PushPayload{
Ref: opts.RefFullName, Ref: opts.RefFullName,
Before: opts.OldCommitID, Before: opts.OldCommitID,
@ -561,11 +558,13 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
return fmt.Errorf("PrepareWebhooks.(new commit): %v", err) 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 case ACTION_PUSH_TAG: // Tag
if isDelRef { if isDelRef {
action.OpType = ACTION_DELETE_TAG
MustNotifyWatchers(action)
if err = PrepareWebhooks(repo, HOOK_EVENT_DELETE, &api.DeletePayload{ if err = PrepareWebhooks(repo, HOOK_EVENT_DELETE, &api.DeletePayload{
Ref: refName, Ref: refName,
RefType: "tag", RefType: "tag",
@ -575,11 +574,14 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
}); err != nil { }); err != nil {
return fmt.Errorf("PrepareWebhooks.(delete tag): %v", err) 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 return nil
} }
action.OpType = ACTION_PUSH_TAG
MustNotifyWatchers(action)
if err = PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ if err = PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
Ref: refName, Ref: refName,
RefType: "tag", RefType: "tag",
@ -589,6 +591,11 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
}); err != nil { }); err != nil {
return fmt.Errorf("PrepareWebhooks.(new tag): %v", err) 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 return nil

8
models/org.go

@ -20,8 +20,8 @@ var (
) )
// IsOwnedBy returns true if given user is in the owner team. // IsOwnedBy returns true if given user is in the owner team.
func (org *User) IsOwnedBy(uid int64) bool { func (org *User) IsOwnedBy(userID int64) bool {
return IsOrganizationOwner(org.ID, uid) return IsOrganizationOwner(org.ID, userID)
} }
// IsOrgMember returns true if given user is member of organization. // 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. // IsOrganizationOwner returns true if given user is in the owner team.
func IsOrganizationOwner(orgId, uid int64) bool { func IsOrganizationOwner(orgID, userID int64) bool {
has, _ := x.Where("is_owner=?", true).And("uid=?", uid).And("org_id=?", orgId).Get(new(OrgUser)) has, _ := x.Where("is_owner = ?", true).And("uid = ?", userID).And("org_id = ?", orgID).Get(new(OrgUser))
return has return has
} }

50
models/repo.go

@ -2068,24 +2068,27 @@ func (repo *Repository) GetWatchers(page int) ([]*User, error) {
func notifyWatchers(e Engine, act *Action) error { func notifyWatchers(e Engine, act *Action) error {
// Add feeds for user self and all watchers. // Add feeds for user self and all watchers.
watches, err := getWatchers(e, act.RepoID) watchers, err := getWatchers(e, act.RepoID)
if err != nil { 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. // Add feed for actioner.
act.UserID = act.ActUserID act.UserID = act.ActUserID
if _, err = e.Insert(act); err != nil { 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 { for i := range watchers {
if act.ActUserID == watches[i].UserID { if act.ActUserID == watchers[i].UserID {
continue continue
} }
act.ID = 0 act.ID = 0
act.UserID = watches[i].UserID act.UserID = watchers[i].UserID
if _, err = e.Insert(act); err != nil { if _, err = e.Insert(act); err != nil {
return fmt.Errorf("insert new action: %v", err) return fmt.Errorf("insert new action: %v", err)
} }
@ -2098,13 +2101,6 @@ func NotifyWatchers(act *Action) error {
return notifyWatchers(x, act) 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) { func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) {
repo := new(Repository) 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 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{ repo := &Repository{
OwnerID: owner.ID, OwnerID: owner.ID,
Owner: owner, Owner: owner,
Name: name, Name: name,
LowerName: strings.ToLower(name), LowerName: strings.ToLower(name),
Description: desc, Description: desc,
DefaultBranch: oldRepo.DefaultBranch, DefaultBranch: baseRepo.DefaultBranch,
IsPrivate: oldRepo.IsPrivate, IsPrivate: baseRepo.IsPrivate,
IsFork: true, IsFork: true,
ForkID: oldRepo.ID, ForkID: baseRepo.ID,
} }
sess := x.NewSession() 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 { if err = createRepository(sess, doer, owner, repo); err != nil {
return nil, err return nil, err
} } else if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", baseRepo.ID); err != nil {
if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", oldRepo.ID); err != nil {
return nil, err return nil, err
} }
repoPath := RepoPath(owner.Name, repo.Name) repoPath := repo.repoPath(sess)
_, stderr, err := process.ExecTimeout(10*time.Minute, _, stderr, err := process.ExecTimeout(10*time.Minute,
fmt.Sprintf("ForkRepository 'git clone': %s/%s", owner.Name, repo.Name), 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 { if err != nil {
return nil, fmt.Errorf("git clone: %v", stderr) 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 { if err = createDelegateHooks(repoPath); err != nil {
return nil, fmt.Errorf("createDelegateHooks: %v", err) 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() return repo, sess.Commit()

76
models/webhook.go

@ -64,6 +64,7 @@ func IsValidHookContentType(name string) bool {
type HookEvents struct { type HookEvents struct {
Create bool `json:"create"` Create bool `json:"create"`
Delete bool `json:"delete"` Delete bool `json:"delete"`
Fork bool `json:"fork"`
Push bool `json:"push"` Push bool `json:"push"`
PullRequest bool `json:"pull_request"` PullRequest bool `json:"pull_request"`
} }
@ -163,6 +164,12 @@ func (w *Webhook) HasDeleteEvent() bool {
(w.ChooseEvents && w.HookEvents.Delete) (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. // HasPushEvent returns true if hook enabled push event.
func (w *Webhook) HasPushEvent() bool { func (w *Webhook) HasPushEvent() bool {
return w.PushOnly || w.SendEverything || return w.PushOnly || w.SendEverything ||
@ -176,15 +183,21 @@ func (w *Webhook) HasPullRequestEvent() bool {
} }
func (w *Webhook) EventsArray() []string { func (w *Webhook) EventsArray() []string {
events := make([]string, 0, 3) events := make([]string, 0, 5)
if w.HasCreateEvent() { 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() { if w.HasPushEvent() {
events = append(events, "push") events = append(events, string(HOOK_EVENT_PUSH))
} }
if w.HasPullRequestEvent() { if w.HasPullRequestEvent() {
events = append(events, "pull_request") events = append(events, string(HOOK_EVENT_PULL_REQUEST))
} }
return events return events
} }
@ -232,10 +245,10 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) {
}) })
} }
// GetActiveWebhooksByRepoID returns all active webhooks of repository. // getActiveWebhooksByRepoID returns all active webhooks of repository.
func GetActiveWebhooksByRepoID(repoID int64) ([]*Webhook, error) { func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5) 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. // GetWebhooksByRepoID returns all webhooks of a repository.
@ -290,10 +303,10 @@ func GetWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) {
return ws, err return ws, err
} }
// GetActiveWebhooksByOrgID returns all active webhooks for an organization. // getActiveWebhooksByOrgID returns all active webhooks for an organization.
func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) { func getActiveWebhooksByOrgID(e Engine, orgID int64) ([]*Webhook, error) {
err = x.Where("org_id=?", orgID).And("is_active=?", true).Find(&ws) ws := make([]*Webhook, 3)
return ws, err return ws, e.Where("org_id=?", orgID).And("is_active=?", true).Find(&ws)
} }
// ___ ___ __ ___________ __ // ___ ___ __ ___________ __
@ -345,6 +358,7 @@ type HookEventType string
const ( const (
HOOK_EVENT_CREATE HookEventType = "create" HOOK_EVENT_CREATE HookEventType = "create"
HOOK_EVENT_DELETE HookEventType = "delete" HOOK_EVENT_DELETE HookEventType = "delete"
HOOK_EVENT_FORK HookEventType = "fork"
HOOK_EVENT_PUSH HookEventType = "push" HOOK_EVENT_PUSH HookEventType = "push"
HOOK_EVENT_PULL_REQUEST HookEventType = "pull_request" 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) 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. // it handles conversion from Payload to PayloadContent.
func CreateHookTask(t *HookTask) error { func createHookTask(e Engine, t *HookTask) error {
data, err := t.Payloader.JSONPayload() data, err := t.Payloader.JSONPayload()
if err != nil { if err != nil {
return err return err
} }
t.UUID = gouuid.NewV4().String() t.UUID = gouuid.NewV4().String()
t.PayloadContent = string(data) t.PayloadContent = string(data)
_, err = x.Insert(t) _, err = e.Insert(t)
return err return err
} }
@ -457,8 +471,8 @@ func UpdateHookTask(t *HookTask) error {
return err return err
} }
// prepareWebhooks adds list of webhooks to task queue. // prepareHookTasks adds list of webhooks to task queue.
func prepareWebhooks(repo *Repository, event HookEventType, p api.Payloader, webhooks []*Webhook) (err error) { func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Payloader, webhooks []*Webhook) (err error) {
if len(webhooks) == 0 { if len(webhooks) == 0 {
return nil return nil
} }
@ -511,7 +525,7 @@ func prepareWebhooks(repo *Repository, event HookEventType, p api.Payloader, web
signature = hex.EncodeToString(sig.Sum(nil)) signature = hex.EncodeToString(sig.Sum(nil))
} }
if err = CreateHookTask(&HookTask{ if err = createHookTask(e, &HookTask{
RepoID: repo.ID, RepoID: repo.ID,
HookID: w.ID, HookID: w.ID,
Type: w.HookTaskType, Type: w.HookTaskType,
@ -522,29 +536,37 @@ func prepareWebhooks(repo *Repository, event HookEventType, p api.Payloader, web
EventType: event, EventType: event,
IsSSL: w.IsSSL, IsSSL: w.IsSSL,
}); err != nil { }); 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 return nil
} }
// PrepareWebhooks adds all active webhooks to task queue. func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payloader) error {
func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error { webhooks, err := getActiveWebhooksByRepoID(e, repo.ID)
webhooks, err := GetActiveWebhooksByRepoID(repo.ID)
if err != nil { 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 // check if repo belongs to org and append additional webhooks
if repo.MustOwner().IsOrganization() { if repo.mustOwner(e).IsOrganization() {
// get hooks for org // get hooks for org
orgws, err := GetActiveWebhooksByOrgID(repo.OwnerID) orgws, err := getActiveWebhooksByOrgID(e, repo.OwnerID)
if err != nil { 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...) 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. // 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 { if err != nil {
return fmt.Errorf("GetWebhookOfRepoByID [repo_id: %d, id: %d]: %v", repo.ID, webhookID, err) 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() { func (t *HookTask) deliver() {

19
models/webhook_discord.go

@ -74,7 +74,6 @@ func getDiscordCreatePayload(p *api.CreatePayload) (*DiscordPayload, error) {
repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
refLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName) refLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
content := fmt.Sprintf("Created new %s: %s/%s", p.RefType, repoLink, refLink) content := fmt.Sprintf("Created new %s: %s/%s", p.RefType, repoLink, refLink)
return &DiscordPayload{ return &DiscordPayload{
Embeds: []*DiscordEmbedObject{{ Embeds: []*DiscordEmbedObject{{
Description: content, Description: content,
@ -92,7 +91,23 @@ func getDiscordDeletePayload(p *api.DeletePayload) (*DiscordPayload, error) {
refName := git.RefEndName(p.Ref) refName := git.RefEndName(p.Ref)
repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
content := fmt.Sprintf("Deleted %s: %s/%s", p.RefType, repoLink, refName) 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{ return &DiscordPayload{
Embeds: []*DiscordEmbedObject{{ Embeds: []*DiscordEmbedObject{{
Description: content, Description: content,
@ -230,6 +245,8 @@ func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (paylo
payload, err = getDiscordCreatePayload(p.(*api.CreatePayload)) payload, err = getDiscordCreatePayload(p.(*api.CreatePayload))
case HOOK_EVENT_DELETE: case HOOK_EVENT_DELETE:
payload, err = getDiscordDeletePayload(p.(*api.DeletePayload)) payload, err = getDiscordDeletePayload(p.(*api.DeletePayload))
case HOOK_EVENT_FORK:
payload, err = getDiscordForkPayload(p.(*api.ForkPayload))
case HOOK_EVENT_PUSH: case HOOK_EVENT_PUSH:
payload, err = getDiscordPushPayload(p.(*api.PushPayload), slack) payload, err = getDiscordPushPayload(p.(*api.PushPayload), slack)
case HOOK_EVENT_PULL_REQUEST: case HOOK_EVENT_PULL_REQUEST:

12
models/webhook_slack.go

@ -90,6 +90,16 @@ func getSlackDeletePayload(p *api.DeletePayload) (*SlackPayload, error) {
}, nil }, 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) { func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) {
// n new commits // n new commits
var ( var (
@ -194,6 +204,8 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (payload
payload, err = getSlackCreatePayload(p.(*api.CreatePayload)) payload, err = getSlackCreatePayload(p.(*api.CreatePayload))
case HOOK_EVENT_DELETE: case HOOK_EVENT_DELETE:
payload, err = getSlackDeletePayload(p.(*api.DeletePayload)) payload, err = getSlackDeletePayload(p.(*api.DeletePayload))
case HOOK_EVENT_FORK:
payload, err = getSlackForkPayload(p.(*api.ForkPayload))
case HOOK_EVENT_PUSH: case HOOK_EVENT_PUSH:
payload, err = getSlackPushPayload(p.(*api.PushPayload), slack) payload, err = getSlackPushPayload(p.(*api.PushPayload), slack)
case HOOK_EVENT_PULL_REQUEST: case HOOK_EVENT_PULL_REQUEST:

4
modules/bindata/bindata.go

File diff suppressed because one or more lines are too long

3
modules/form/repo.go

@ -23,7 +23,7 @@ import (
// \/ \/ \/ \/ \/ \/ \/ // \/ \/ \/ \/ \/ \/ \/
type CreateRepo struct { type CreateRepo struct {
Uid int64 `binding:"Required"` UserID int64 `binding:"Required"`
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Private bool Private bool
Description string `binding:"MaxSize(255)"` Description string `binding:"MaxSize(255)"`
@ -136,6 +136,7 @@ type Webhook struct {
Events string Events string
Create bool Create bool
Delete bool Delete bool
Fork bool
Push bool Push bool
PullRequest bool PullRequest bool
Active bool Active bool

2
modules/template/template.go

@ -262,6 +262,8 @@ func ActionIcon(opType int) string {
return "git-branch" return "git-branch"
case 17, 18: // Delete branch or tag case 17, 18: // Delete branch or tag
return "alert" return "alert"
case 19: // Fork a repository
return "repo-forked"
default: default:
return "invalid type" return "invalid type"
} }

52
routers/repo/pull.go

@ -38,31 +38,27 @@ var (
} }
) )
func getForkRepository(ctx *context.Context) *models.Repository { func parseBaseRepository(ctx *context.Context) *models.Repository {
forkRepo, err := models.GetRepositoryByID(ctx.ParamsInt64(":repoid")) baseRepo, err := models.GetRepositoryByID(ctx.ParamsInt64(":repoid"))
if err != nil { if err != nil {
if models.IsErrRepoNotExist(err) { ctx.NotFoundOrServerError("GetRepositoryByID", models.IsErrRepoNotExist, err)
ctx.Handle(404, "GetRepositoryByID", nil)
} else {
ctx.Handle(500, "GetRepositoryByID", err)
}
return nil return nil
} }
if !forkRepo.CanBeForked() || !forkRepo.HasAccess(ctx.User.ID) { if !baseRepo.CanBeForked() || !baseRepo.HasAccess(ctx.User.ID) {
ctx.Handle(404, "getForkRepository", nil) ctx.NotFound()
return nil return nil
} }
ctx.Data["repo_name"] = forkRepo.Name ctx.Data["repo_name"] = baseRepo.Name
ctx.Data["description"] = forkRepo.Description ctx.Data["description"] = baseRepo.Description
ctx.Data["IsPrivate"] = forkRepo.IsPrivate ctx.Data["IsPrivate"] = baseRepo.IsPrivate
if err = forkRepo.GetOwner(); err != nil { if err = baseRepo.GetOwner(); err != nil {
ctx.Handle(500, "GetOwner", err) ctx.Handle(500, "GetOwner", err)
return nil return nil
} }
ctx.Data["ForkFrom"] = forkRepo.Owner.Name + "/" + forkRepo.Name ctx.Data["ForkFrom"] = baseRepo.Owner.Name + "/" + baseRepo.Name
if err := ctx.User.GetOrganizations(true); err != nil { if err := ctx.User.GetOrganizations(true); err != nil {
ctx.Handle(500, "GetOrganizations", err) ctx.Handle(500, "GetOrganizations", err)
@ -70,13 +66,13 @@ func getForkRepository(ctx *context.Context) *models.Repository {
} }
ctx.Data["Orgs"] = ctx.User.Orgs ctx.Data["Orgs"] = ctx.User.Orgs
return forkRepo return baseRepo
} }
func Fork(ctx *context.Context) { func Fork(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_fork") ctx.Data["Title"] = ctx.Tr("new_fork")
getForkRepository(ctx) parseBaseRepository(ctx)
if ctx.Written() { if ctx.Written() {
return return
} }
@ -88,12 +84,12 @@ func Fork(ctx *context.Context) {
func ForkPost(ctx *context.Context, f form.CreateRepo) { func ForkPost(ctx *context.Context, f form.CreateRepo) {
ctx.Data["Title"] = ctx.Tr("new_fork") ctx.Data["Title"] = ctx.Tr("new_fork")
forkRepo := getForkRepository(ctx) baseRepo := parseBaseRepository(ctx)
if ctx.Written() { if ctx.Written() {
return return
} }
ctxUser := checkContextUser(ctx, f.Uid) ctxUser := checkContextUser(ctx, f.UserID)
if ctx.Written() { if ctx.Written() {
return return
} }
@ -104,27 +100,25 @@ func ForkPost(ctx *context.Context, f form.CreateRepo) {
return return
} }
repo, has := models.HasForkedRepo(ctxUser.ID, forkRepo.ID) repo, has := models.HasForkedRepo(ctxUser.ID, baseRepo.ID)
if has { if has {
ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name) ctx.Redirect(repo.Link())
return return
} }
// Check ownership of organization. // Check ownership of organization.
if ctxUser.IsOrganization() { if ctxUser.IsOrganization() && !ctxUser.IsOwnedBy(ctx.User.ID) {
if !ctxUser.IsOwnedBy(ctx.User.ID) { ctx.Error(403)
ctx.Error(403) return
return
}
} }
// Cannot fork to same owner // Cannot fork to same owner
if ctxUser.ID == forkRepo.OwnerID { if ctxUser.ID == baseRepo.OwnerID {
ctx.RenderWithErr(ctx.Tr("repo.settings.cannot_fork_to_same_owner"), FORK, &f) ctx.RenderWithErr(ctx.Tr("repo.settings.cannot_fork_to_same_owner"), FORK, &f)
return return
} }
repo, err := models.ForkRepository(ctx.User, ctxUser, forkRepo, f.RepoName, f.Description) repo, err := models.ForkRepository(ctx.User, ctxUser, baseRepo, f.RepoName, f.Description)
if err != nil { if err != nil {
ctx.Data["Err_RepoName"] = true ctx.Data["Err_RepoName"] = true
switch { switch {
@ -140,8 +134,8 @@ func ForkPost(ctx *context.Context, f form.CreateRepo) {
return return
} }
log.Trace("Repository forked[%d]: %s/%s", forkRepo.ID, ctxUser.Name, repo.Name) log.Trace("Repository forked from '%s' -> '%s'", baseRepo.FullName(), repo.FullName())
ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name) ctx.Redirect(repo.Link())
} }
func checkPullInfo(ctx *context.Context) *models.Issue { func checkPullInfo(ctx *context.Context) *models.Issue {

2
routers/repo/repo.go

@ -109,7 +109,7 @@ func CreatePost(ctx *context.Context, f form.CreateRepo) {
ctx.Data["Licenses"] = models.Licenses ctx.Data["Licenses"] = models.Licenses
ctx.Data["Readmes"] = models.Readmes ctx.Data["Readmes"] = models.Readmes
ctxUser := checkContextUser(ctx, f.Uid) ctxUser := checkContextUser(ctx, f.UserID)
if ctx.Written() { if ctx.Written() {
return return
} }

1
routers/repo/webhook.go

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

2
templates/repo/create.tmpl

@ -12,7 +12,7 @@
<div class="inline required field {{if .Err_Owner}}error{{end}}"> <div class="inline required field {{if .Err_Owner}}error{{end}}">
<label>{{.i18n.Tr "repo.owner"}}</label> <label>{{.i18n.Tr "repo.owner"}}</label>
<div class="ui selection owner dropdown"> <div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <input type="hidden" id="user_id" name="user_id" value="{{.ContextUser.ID}}" required>
<span class="text"> <span class="text">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
{{.ContextUser.ShortName 20}} {{.ContextUser.ShortName 20}}

2
templates/repo/pulls/fork.tmpl

@ -12,7 +12,7 @@
<div class="inline required field {{if .Err_Owner}}error{{end}}"> <div class="inline required field {{if .Err_Owner}}error{{end}}">
<label>{{.i18n.Tr "repo.owner"}}</label> <label>{{.i18n.Tr "repo.owner"}}</label>
<div class="ui selection owner dropdown"> <div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <input type="hidden" id="user_id" name="user_id" value="{{.ContextUser.ID}}" required>
<span class="text"> <span class="text">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
{{.ContextUser.ShortName 20}} {{.ContextUser.ShortName 20}}

10
templates/repo/settings/webhook_settings.tmpl

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

2
templates/user/dashboard/feeds.tmpl

@ -51,6 +51,8 @@
{{$.i18n.Tr "action.delete_branch" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} {{$.i18n.Tr "action.delete_branch" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 18}} {{else if eq .GetOpType 18}}
{{$.i18n.Tr "action.delete_tag" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} {{$.i18n.Tr "action.delete_tag" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}}
{{else if eq .GetOpType 19}}
{{$.i18n.Tr "action.fork_repo" .GetRepoLink .ShortRepoPath | Str2html}}
{{end}} {{end}}
</p> </p>
{{if eq .GetOpType 5}} {{if eq .GetOpType 5}}

17
vendor/github.com/gogits/go-gogs-client/repo_hook.go generated vendored

@ -160,6 +160,23 @@ func (p *DeletePayload) JSONPayload() ([]byte, error) {
return json.MarshalIndent(p, "", " ") return json.MarshalIndent(p, "", " ")
} }
// ___________ __
// \_ _____/__________| | __
// | __)/ _ \_ __ \ |/ /
// | \( <_> ) | \/ <
// \___ / \____/|__| |__|_ \
// \/ \/
type ForkPayload struct {
Forkee *Repository `json:"forkee"`
Repo *Repository `json:"repository"`
Sender *User `json:"sender"`
}
func (p *ForkPayload) JSONPayload() ([]byte, error) {
return json.MarshalIndent(p, "", " ")
}
// __________ .__ // __________ .__
// \______ \__ __ _____| |__ // \______ \__ __ _____| |__
// | ___/ | \/ ___/ | \ // | ___/ | \/ ___/ | \

6
vendor/vendor.json vendored

@ -165,10 +165,10 @@
"revisionTime": "2017-02-19T18:16:29Z" "revisionTime": "2017-02-19T18:16:29Z"
}, },
{ {
"checksumSHA1": "rtJ+nZ9VHh2X2Zon7wLczPAAc/s=", "checksumSHA1": "sAGNvN2IXzD+rra6Y9sxJBpR4L8=",
"path": "github.com/gogits/go-gogs-client", "path": "github.com/gogits/go-gogs-client",
"revision": "ba630f557c8349952183305373fa89b155202bac", "revision": "264a3d5bc98e108f17cc055338dbf4b94faf0d21",
"revisionTime": "2017-02-24T20:25:47Z" "revisionTime": "2017-02-25T08:33:02Z"
}, },
{ {
"checksumSHA1": "p4yoFWgDiTfpu1JYgh26t6+VDTk=", "checksumSHA1": "p4yoFWgDiTfpu1JYgh26t6+VDTk=",

Loading…
Cancel
Save