diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index bb72b7f5d..ba411dd8c 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -192,6 +192,8 @@ var migrations = []Migration{ NewMigration("fix merge base for pull requests", fixMergeBase), // v129 -> v130 NewMigration("remove dependencies from deleted repositories", purgeUnusedDependencies), + // v130 -> v131 + NewMigration("Expand webhooks for more granularity", expandWebhooks), } // Migrate database to current version diff --git a/models/migrations/v130.go b/models/migrations/v130.go new file mode 100644 index 000000000..3a7efedf8 --- /dev/null +++ b/models/migrations/v130.go @@ -0,0 +1,101 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "encoding/json" + + "xorm.io/xorm" +) + +func expandWebhooks(x *xorm.Engine) error { + + type ChooseEvents struct { + Issues bool `json:"issues"` + IssueAssign bool `json:"issue_assign"` + IssueLabel bool `json:"issue_label"` + IssueMilestone bool `json:"issue_milestone"` + IssueComment bool `json:"issue_comment"` + PullRequest bool `json:"pull_request"` + PullRequestAssign bool `json:"pull_request_assign"` + PullRequestLabel bool `json:"pull_request_label"` + PullRequestMilestone bool `json:"pull_request_milestone"` + PullRequestComment bool `json:"pull_request_comment"` + PullRequestReview bool `json:"pull_request_review"` + PullRequestSync bool `json:"pull_request_sync"` + } + + type Events struct { + PushOnly bool `json:"push_only"` + SendEverything bool `json:"send_everything"` + ChooseEvents bool `json:"choose_events"` + BranchFilter string `json:"branch_filter"` + Events ChooseEvents `json:"events"` + } + + type Webhook struct { + ID int64 + Events string + } + + var events Events + var bytes []byte + var last int + const batchSize = 50 + sess := x.NewSession() + defer sess.Close() + for { + if err := sess.Begin(); err != nil { + return err + } + var results = make([]Webhook, 0, batchSize) + err := x.OrderBy("id"). + Limit(batchSize, last). + Find(&results) + if err != nil { + return err + } + if len(results) == 0 { + break + } + last += len(results) + + for _, res := range results { + if err = json.Unmarshal([]byte(res.Events), &events); err != nil { + return err + } + + if events.Events.Issues { + events.Events.IssueAssign = true + events.Events.IssueLabel = true + events.Events.IssueMilestone = true + events.Events.IssueComment = true + } + + if events.Events.PullRequest { + events.Events.PullRequestAssign = true + events.Events.PullRequestLabel = true + events.Events.PullRequestMilestone = true + events.Events.PullRequestComment = true + events.Events.PullRequestReview = true + events.Events.PullRequestSync = true + } + + if bytes, err = json.Marshal(&events); err != nil { + return err + } + + _, err = sess.Exec("UPDATE webhook SET events = ? WHERE id = ?", string(bytes), res.ID) + if err != nil { + return err + } + } + + if err := sess.Commit(); err != nil { + return err + } + } + return nil +} diff --git a/models/webhook.go b/models/webhook.go index 626489b34..82aedf7e8 100644 --- a/models/webhook.go +++ b/models/webhook.go @@ -57,15 +57,24 @@ func IsValidHookContentType(name string) bool { // HookEvents is a set of web hook events type HookEvents struct { - Create bool `json:"create"` - Delete bool `json:"delete"` - Fork bool `json:"fork"` - Issues bool `json:"issues"` - IssueComment bool `json:"issue_comment"` - Push bool `json:"push"` - PullRequest bool `json:"pull_request"` - Repository bool `json:"repository"` - Release bool `json:"release"` + Create bool `json:"create"` + Delete bool `json:"delete"` + Fork bool `json:"fork"` + Issues bool `json:"issues"` + IssueAssign bool `json:"issue_assign"` + IssueLabel bool `json:"issue_label"` + IssueMilestone bool `json:"issue_milestone"` + IssueComment bool `json:"issue_comment"` + Push bool `json:"push"` + PullRequest bool `json:"pull_request"` + PullRequestAssign bool `json:"pull_request_assign"` + PullRequestLabel bool `json:"pull_request_label"` + PullRequestMilestone bool `json:"pull_request_milestone"` + PullRequestComment bool `json:"pull_request_comment"` + PullRequestReview bool `json:"pull_request_review"` + PullRequestSync bool `json:"pull_request_sync"` + Repository bool `json:"repository"` + Release bool `json:"release"` } // HookEvent represents events that will delivery hook. @@ -154,6 +163,24 @@ func (w *Webhook) HasIssuesEvent() bool { (w.ChooseEvents && w.HookEvents.Issues) } +// HasIssuesAssignEvent returns true if hook enabled issues assign event. +func (w *Webhook) HasIssuesAssignEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.IssueAssign) +} + +// HasIssuesLabelEvent returns true if hook enabled issues label event. +func (w *Webhook) HasIssuesLabelEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.IssueLabel) +} + +// HasIssuesMilestoneEvent returns true if hook enabled issues milestone event. +func (w *Webhook) HasIssuesMilestoneEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.IssueMilestone) +} + // HasIssueCommentEvent returns true if hook enabled issue_comment event. func (w *Webhook) HasIssueCommentEvent() bool { return w.SendEverything || @@ -172,6 +199,54 @@ func (w *Webhook) HasPullRequestEvent() bool { (w.ChooseEvents && w.HookEvents.PullRequest) } +// HasPullRequestAssignEvent returns true if hook enabled pull request assign event. +func (w *Webhook) HasPullRequestAssignEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.PullRequestAssign) +} + +// HasPullRequestLabelEvent returns true if hook enabled pull request label event. +func (w *Webhook) HasPullRequestLabelEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.PullRequestLabel) +} + +// HasPullRequestMilestoneEvent returns true if hook enabled pull request milestone event. +func (w *Webhook) HasPullRequestMilestoneEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.PullRequestMilestone) +} + +// HasPullRequestCommentEvent returns true if hook enabled pull_request_comment event. +func (w *Webhook) HasPullRequestCommentEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.PullRequestComment) +} + +// HasPullRequestApprovedEvent returns true if hook enabled pull request review event. +func (w *Webhook) HasPullRequestApprovedEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.PullRequestReview) +} + +// HasPullRequestRejectedEvent returns true if hook enabled pull request review event. +func (w *Webhook) HasPullRequestRejectedEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.PullRequestReview) +} + +// HasPullRequestReviewCommentEvent returns true if hook enabled pull request review event. +func (w *Webhook) HasPullRequestReviewCommentEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.PullRequestReview) +} + +// HasPullRequestSyncEvent returns true if hook enabled pull request sync event. +func (w *Webhook) HasPullRequestSyncEvent() bool { + return w.SendEverything || + (w.ChooseEvents && w.HookEvents.PullRequestSync) +} + // HasReleaseEvent returns if hook enabled release event. func (w *Webhook) HasReleaseEvent() bool { return w.SendEverything || @@ -198,8 +273,19 @@ func (w *Webhook) EventCheckers() []struct { {w.HasForkEvent, HookEventFork}, {w.HasPushEvent, HookEventPush}, {w.HasIssuesEvent, HookEventIssues}, + {w.HasIssuesAssignEvent, HookEventIssueAssign}, + {w.HasIssuesLabelEvent, HookEventIssueLabel}, + {w.HasIssuesMilestoneEvent, HookEventIssueMilestone}, {w.HasIssueCommentEvent, HookEventIssueComment}, {w.HasPullRequestEvent, HookEventPullRequest}, + {w.HasPullRequestAssignEvent, HookEventPullRequestAssign}, + {w.HasPullRequestLabelEvent, HookEventPullRequestLabel}, + {w.HasPullRequestMilestoneEvent, HookEventPullRequestMilestone}, + {w.HasPullRequestCommentEvent, HookEventPullRequestComment}, + {w.HasPullRequestApprovedEvent, HookEventPullRequestReviewApproved}, + {w.HasPullRequestRejectedEvent, HookEventPullRequestReviewRejected}, + {w.HasPullRequestCommentEvent, HookEventPullRequestReviewComment}, + {w.HasPullRequestSyncEvent, HookEventPullRequestSync}, {w.HasRepositoryEvent, HookEventRepository}, {w.HasReleaseEvent, HookEventRelease}, } @@ -498,20 +584,60 @@ type HookEventType string // Types of hook events const ( - HookEventCreate HookEventType = "create" - HookEventDelete HookEventType = "delete" - HookEventFork HookEventType = "fork" - HookEventPush HookEventType = "push" - HookEventIssues HookEventType = "issues" - HookEventIssueComment HookEventType = "issue_comment" - HookEventPullRequest HookEventType = "pull_request" - HookEventRepository HookEventType = "repository" - HookEventRelease HookEventType = "release" - HookEventPullRequestApproved HookEventType = "pull_request_approved" - HookEventPullRequestRejected HookEventType = "pull_request_rejected" - HookEventPullRequestComment HookEventType = "pull_request_comment" + HookEventCreate HookEventType = "create" + HookEventDelete HookEventType = "delete" + HookEventFork HookEventType = "fork" + HookEventPush HookEventType = "push" + HookEventIssues HookEventType = "issues" + HookEventIssueAssign HookEventType = "issue_assign" + HookEventIssueLabel HookEventType = "issue_label" + HookEventIssueMilestone HookEventType = "issue_milestone" + HookEventIssueComment HookEventType = "issue_comment" + HookEventPullRequest HookEventType = "pull_request" + HookEventPullRequestAssign HookEventType = "pull_request_assign" + HookEventPullRequestLabel HookEventType = "pull_request_label" + HookEventPullRequestMilestone HookEventType = "pull_request_milestone" + HookEventPullRequestComment HookEventType = "pull_request_comment" + HookEventPullRequestReviewApproved HookEventType = "pull_request_review_approved" + HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected" + HookEventPullRequestReviewComment HookEventType = "pull_request_review_comment" + HookEventPullRequestSync HookEventType = "pull_request_sync" + HookEventRepository HookEventType = "repository" + HookEventRelease HookEventType = "release" ) +// Event returns the HookEventType as an event string +func (h HookEventType) Event() string { + switch h { + case HookEventCreate: + return "create" + case HookEventDelete: + return "delete" + case HookEventFork: + return "fork" + case HookEventPush: + return "push" + case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone: + return "issues" + case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone, + HookEventPullRequestSync: + return "pull_request" + case HookEventIssueComment, HookEventPullRequestComment: + return "issue_comment" + case HookEventPullRequestReviewApproved: + return "pull_request_approved" + case HookEventPullRequestReviewRejected: + return "pull_request_rejected" + case HookEventPullRequestReviewComment: + return "pull_request_comment" + case HookEventRepository: + return "repository" + case HookEventRelease: + return "release" + } + return "" +} + // HookRequest represents hook task request information. type HookRequest struct { Headers map[string]string `json:"headers"` diff --git a/models/webhook_test.go b/models/webhook_test.go index f8e423429..5ee7f9159 100644 --- a/models/webhook_test.go +++ b/models/webhook_test.go @@ -61,7 +61,11 @@ func TestWebhook_UpdateEvent(t *testing.T) { } func TestWebhook_EventsArray(t *testing.T) { - assert.Equal(t, []string{"create", "delete", "fork", "push", "issues", "issue_comment", "pull_request", "repository", "release"}, + assert.Equal(t, []string{"create", "delete", "fork", "push", + "issues", "issue_assign", "issue_label", "issue_milestone", "issue_comment", + "pull_request", "pull_request_assign", "pull_request_label", "pull_request_milestone", + "pull_request_comment", "pull_request_review_approved", "pull_request_review_rejected", + "pull_request_review_comment", "pull_request_sync", "repository", "release"}, (&Webhook{ HookEvent: &HookEvent{SendEverything: true}, }).EventsArray(), diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 932976b6f..63a560728 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -190,18 +190,27 @@ func (f *ProtectBranchForm) Validate(ctx *macaron.Context, errs binding.Errors) // WebhookForm form for changing web hook type WebhookForm struct { - Events string - Create bool - Delete bool - Fork bool - Issues bool - IssueComment bool - Release bool - Push bool - PullRequest bool - Repository bool - Active bool - BranchFilter string `binding:"GlobPattern"` + Events string + Create bool + Delete bool + Fork bool + Issues bool + IssueAssign bool + IssueLabel bool + IssueMilestone bool + IssueComment bool + Release bool + Push bool + PullRequest bool + PullRequestAssign bool + PullRequestLabel bool + PullRequestMilestone bool + PullRequestComment bool + PullRequestReview bool + PullRequestSync bool + Repository bool + Active bool + BranchFilter string `binding:"GlobPattern"` } // PushOnly if the hook will be triggered when push diff --git a/modules/notification/webhook/webhook.go b/modules/notification/webhook/webhook.go index f598d5b1f..625cf119a 100644 --- a/modules/notification/webhook/webhook.go +++ b/modules/notification/webhook/webhook.go @@ -48,7 +48,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *models.User, issue *model return } - err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{ + err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequestLabel, &api.PullRequestPayload{ Action: api.HookIssueLabelCleared, Index: issue.Index, PullRequest: convert.ToAPIPullRequest(issue.PullRequest), @@ -56,7 +56,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *models.User, issue *model Sender: doer.APIFormat(), }) } else { - err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventIssues, &api.IssuePayload{ + err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventIssueLabel, &api.IssuePayload{ Action: api.HookIssueLabelCleared, Index: issue.Index, Issue: convert.ToAPIIssue(issue), @@ -147,7 +147,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *mo apiPullRequest.Action = api.HookIssueAssigned } // Assignee comment triggers a webhook - if err := webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, apiPullRequest); err != nil { + if err := webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequestAssign, apiPullRequest); err != nil { log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err) return } @@ -165,7 +165,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *mo apiIssue.Action = api.HookIssueAssigned } // Assignee comment triggers a webhook - if err := webhook_module.PrepareWebhooks(issue.Repo, models.HookEventIssues, apiIssue); err != nil { + if err := webhook_module.PrepareWebhooks(issue.Repo, models.HookEventIssueAssign, apiIssue); err != nil { log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err) return } @@ -338,34 +338,54 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod } func (m *webhookNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) { - if err := c.LoadPoster(); err != nil { + var err error + + if err = c.LoadPoster(); err != nil { log.Error("LoadPoster: %v", err) return } - if err := c.LoadIssue(); err != nil { + if err = c.LoadIssue(); err != nil { log.Error("LoadIssue: %v", err) return } - if err := c.Issue.LoadAttributes(); err != nil { + if err = c.Issue.LoadAttributes(); err != nil { log.Error("LoadAttributes: %v", err) return } mode, _ := models.AccessLevel(doer, c.Issue.Repo) - if err := webhook_module.PrepareWebhooks(c.Issue.Repo, models.HookEventIssueComment, &api.IssueCommentPayload{ - Action: api.HookIssueCommentEdited, - Issue: convert.ToAPIIssue(c.Issue), - Comment: c.APIFormat(), - Changes: &api.ChangesPayload{ - Body: &api.ChangesFromPayload{ - From: oldContent, + if c.Issue.IsPull { + err = webhook_module.PrepareWebhooks(c.Issue.Repo, models.HookEventPullRequestComment, &api.IssueCommentPayload{ + Action: api.HookIssueCommentEdited, + Issue: convert.ToAPIIssue(c.Issue), + Comment: c.APIFormat(), + Changes: &api.ChangesPayload{ + Body: &api.ChangesFromPayload{ + From: oldContent, + }, }, - }, - Repository: c.Issue.Repo.APIFormat(mode), - Sender: doer.APIFormat(), - IsPull: c.Issue.IsPull, - }); err != nil { + Repository: c.Issue.Repo.APIFormat(mode), + Sender: doer.APIFormat(), + IsPull: true, + }) + } else { + err = webhook_module.PrepareWebhooks(c.Issue.Repo, models.HookEventIssueComment, &api.IssueCommentPayload{ + Action: api.HookIssueCommentEdited, + Issue: convert.ToAPIIssue(c.Issue), + Comment: c.APIFormat(), + Changes: &api.ChangesPayload{ + Body: &api.ChangesFromPayload{ + From: oldContent, + }, + }, + Repository: c.Issue.Repo.APIFormat(mode), + Sender: doer.APIFormat(), + IsPull: false, + }) + } + + if err != nil { log.Error("PrepareWebhooks [comment_id: %d]: %v", c.ID, err) } } @@ -373,45 +393,76 @@ func (m *webhookNotifier) NotifyUpdateComment(doer *models.User, c *models.Comme func (m *webhookNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository, issue *models.Issue, comment *models.Comment) { mode, _ := models.AccessLevel(doer, repo) - if err := webhook_module.PrepareWebhooks(repo, models.HookEventIssueComment, &api.IssueCommentPayload{ - Action: api.HookIssueCommentCreated, - Issue: convert.ToAPIIssue(issue), - Comment: comment.APIFormat(), - Repository: repo.APIFormat(mode), - Sender: doer.APIFormat(), - IsPull: issue.IsPull, - }); err != nil { + + var err error + if issue.IsPull { + err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequestComment, &api.IssueCommentPayload{ + Action: api.HookIssueCommentCreated, + Issue: convert.ToAPIIssue(issue), + Comment: comment.APIFormat(), + Repository: repo.APIFormat(mode), + Sender: doer.APIFormat(), + IsPull: true, + }) + } else { + err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventIssueComment, &api.IssueCommentPayload{ + Action: api.HookIssueCommentCreated, + Issue: convert.ToAPIIssue(issue), + Comment: comment.APIFormat(), + Repository: repo.APIFormat(mode), + Sender: doer.APIFormat(), + IsPull: false, + }) + } + + if err != nil { log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err) } } func (m *webhookNotifier) NotifyDeleteComment(doer *models.User, comment *models.Comment) { - if err := comment.LoadPoster(); err != nil { + var err error + + if err = comment.LoadPoster(); err != nil { log.Error("LoadPoster: %v", err) return } - if err := comment.LoadIssue(); err != nil { + if err = comment.LoadIssue(); err != nil { log.Error("LoadIssue: %v", err) return } - if err := comment.Issue.LoadAttributes(); err != nil { + if err = comment.Issue.LoadAttributes(); err != nil { log.Error("LoadAttributes: %v", err) return } mode, _ := models.AccessLevel(doer, comment.Issue.Repo) - if err := webhook_module.PrepareWebhooks(comment.Issue.Repo, models.HookEventIssueComment, &api.IssueCommentPayload{ - Action: api.HookIssueCommentDeleted, - Issue: convert.ToAPIIssue(comment.Issue), - Comment: comment.APIFormat(), - Repository: comment.Issue.Repo.APIFormat(mode), - Sender: doer.APIFormat(), - IsPull: comment.Issue.IsPull, - }); err != nil { + if comment.Issue.IsPull { + err = webhook_module.PrepareWebhooks(comment.Issue.Repo, models.HookEventPullRequestComment, &api.IssueCommentPayload{ + Action: api.HookIssueCommentDeleted, + Issue: convert.ToAPIIssue(comment.Issue), + Comment: comment.APIFormat(), + Repository: comment.Issue.Repo.APIFormat(mode), + Sender: doer.APIFormat(), + IsPull: true, + }) + } else { + err = webhook_module.PrepareWebhooks(comment.Issue.Repo, models.HookEventIssueComment, &api.IssueCommentPayload{ + Action: api.HookIssueCommentDeleted, + Issue: convert.ToAPIIssue(comment.Issue), + Comment: comment.APIFormat(), + Repository: comment.Issue.Repo.APIFormat(mode), + Sender: doer.APIFormat(), + IsPull: false, + }) + } + + if err != nil { log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err) } + } func (m *webhookNotifier) NotifyIssueChangeLabels(doer *models.User, issue *models.Issue, @@ -438,7 +489,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *models.User, issue *mode log.Error("LoadIssue: %v", err) return } - err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{ + err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequestLabel, &api.PullRequestPayload{ Action: api.HookIssueLabelUpdated, Index: issue.Index, PullRequest: convert.ToAPIPullRequest(issue.PullRequest), @@ -446,7 +497,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *models.User, issue *mode Sender: doer.APIFormat(), }) } else { - err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventIssues, &api.IssuePayload{ + err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventIssueLabel, &api.IssuePayload{ Action: api.HookIssueLabelUpdated, Index: issue.Index, Issue: convert.ToAPIIssue(issue), @@ -480,7 +531,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m log.Error("LoadIssue: %v", err) return } - err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{ + err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventPullRequestMilestone, &api.PullRequestPayload{ Action: hookAction, Index: issue.Index, PullRequest: convert.ToAPIPullRequest(issue.PullRequest), @@ -488,7 +539,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m Sender: doer.APIFormat(), }) } else { - err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventIssues, &api.IssuePayload{ + err = webhook_module.PrepareWebhooks(issue.Repo, models.HookEventIssueMilestone, &api.IssuePayload{ Action: hookAction, Index: issue.Index, Issue: convert.ToAPIIssue(issue), @@ -597,11 +648,11 @@ func (m *webhookNotifier) NotifyPullRequestReview(pr *models.PullRequest, review switch review.Type { case models.ReviewTypeApprove: - reviewHookType = models.HookEventPullRequestApproved + reviewHookType = models.HookEventPullRequestReviewApproved case models.ReviewTypeComment: reviewHookType = models.HookEventPullRequestComment case models.ReviewTypeReject: - reviewHookType = models.HookEventPullRequestRejected + reviewHookType = models.HookEventPullRequestReviewRejected default: // unsupported review webhook type here log.Error("Unsupported review webhook type") @@ -619,7 +670,7 @@ func (m *webhookNotifier) NotifyPullRequestReview(pr *models.PullRequest, review return } if err := webhook_module.PrepareWebhooks(review.Issue.Repo, reviewHookType, &api.PullRequestPayload{ - Action: api.HookIssueSynchronized, + Action: api.HookIssueReviewed, Index: review.Issue.Index, PullRequest: convert.ToAPIPullRequest(pr), Repository: review.Issue.Repo.APIFormat(mode), @@ -673,7 +724,7 @@ func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *m return } - if err := webhook_module.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{ + if err := webhook_module.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequestSync, &api.PullRequestPayload{ Action: api.HookIssueSynchronized, Index: pr.Issue.Index, PullRequest: convert.ToAPIPullRequest(pr), diff --git a/modules/structs/hook.go b/modules/structs/hook.go index f2bc4f16e..a10dd4281 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -374,6 +374,8 @@ const ( HookIssueMilestoned HookIssueAction = "milestoned" // HookIssueDemilestoned is an issue action for when a milestone is cleared on an issue. HookIssueDemilestoned HookIssueAction = "demilestoned" + // HookIssueReviewed is an issue action for when a pull request is reviewed + HookIssueReviewed HookIssueAction = "reviewed" ) // IssuePayload represents the payload information that is sent along with an issue event. diff --git a/modules/webhook/deliver.go b/modules/webhook/deliver.go index 9f5c938f8..505d27a2d 100644 --- a/modules/webhook/deliver.go +++ b/modules/webhook/deliver.go @@ -74,13 +74,13 @@ func Deliver(t *models.HookTask) error { } req.Header.Add("X-Gitea-Delivery", t.UUID) - req.Header.Add("X-Gitea-Event", string(t.EventType)) + req.Header.Add("X-Gitea-Event", t.EventType.Event()) req.Header.Add("X-Gitea-Signature", t.Signature) req.Header.Add("X-Gogs-Delivery", t.UUID) - req.Header.Add("X-Gogs-Event", string(t.EventType)) + req.Header.Add("X-Gogs-Event", t.EventType.Event()) req.Header.Add("X-Gogs-Signature", t.Signature) req.Header["X-GitHub-Delivery"] = []string{t.UUID} - req.Header["X-GitHub-Event"] = []string{string(t.EventType)} + req.Header["X-GitHub-Event"] = []string{t.EventType.Event()} // Record delivery information. t.RequestInfo = &models.HookRequest{ diff --git a/modules/webhook/dingtalk.go b/modules/webhook/dingtalk.go index fc99202a7..f2dd5a79e 100644 --- a/modules/webhook/dingtalk.go +++ b/modules/webhook/dingtalk.go @@ -181,7 +181,7 @@ func getDingtalkPullRequestPayload(p *api.PullRequestPayload) (*DingtalkPayload, func getDingtalkPullRequestApprovalPayload(p *api.PullRequestPayload, event models.HookEventType) (*DingtalkPayload, error) { var text, title string switch p.Action { - case api.HookIssueSynchronized: + case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { return nil, err @@ -261,15 +261,16 @@ func GetDingtalkPayload(p api.Payloader, event models.HookEventType, meta string return getDingtalkDeletePayload(p.(*api.DeletePayload)) case models.HookEventFork: return getDingtalkForkPayload(p.(*api.ForkPayload)) - case models.HookEventIssues: + case models.HookEventIssues, models.HookEventIssueAssign, models.HookEventIssueLabel, models.HookEventIssueMilestone: return getDingtalkIssuesPayload(p.(*api.IssuePayload)) - case models.HookEventIssueComment: + case models.HookEventIssueComment, models.HookEventPullRequestComment: return getDingtalkIssueCommentPayload(p.(*api.IssueCommentPayload)) case models.HookEventPush: return getDingtalkPushPayload(p.(*api.PushPayload)) - case models.HookEventPullRequest: + case models.HookEventPullRequest, models.HookEventPullRequestAssign, models.HookEventPullRequestLabel, + models.HookEventPullRequestMilestone, models.HookEventPullRequestSync: return getDingtalkPullRequestPayload(p.(*api.PullRequestPayload)) - case models.HookEventPullRequestApproved, models.HookEventPullRequestRejected, models.HookEventPullRequestComment: + case models.HookEventPullRequestReviewApproved, models.HookEventPullRequestReviewRejected, models.HookEventPullRequestReviewComment: return getDingtalkPullRequestApprovalPayload(p.(*api.PullRequestPayload), event) case models.HookEventRepository: return getDingtalkRepositoryPayload(p.(*api.RepositoryPayload)) diff --git a/modules/webhook/discord.go b/modules/webhook/discord.go index 732821c18..e455a102b 100644 --- a/modules/webhook/discord.go +++ b/modules/webhook/discord.go @@ -296,7 +296,7 @@ func getDiscordPullRequestApprovalPayload(p *api.PullRequestPayload, meta *Disco var text, title string var color int switch p.Action { - case api.HookIssueSynchronized: + case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { return nil, err @@ -306,9 +306,9 @@ func getDiscordPullRequestApprovalPayload(p *api.PullRequestPayload, meta *Disco text = p.Review.Content switch event { - case models.HookEventPullRequestApproved: + case models.HookEventPullRequestReviewApproved: color = greenColor - case models.HookEventPullRequestRejected: + case models.HookEventPullRequestReviewRejected: color = redColor case models.HookEventPullRequestComment: color = greyColor @@ -405,15 +405,16 @@ func GetDiscordPayload(p api.Payloader, event models.HookEventType, meta string) return getDiscordDeletePayload(p.(*api.DeletePayload), discord) case models.HookEventFork: return getDiscordForkPayload(p.(*api.ForkPayload), discord) - case models.HookEventIssues: + case models.HookEventIssues, models.HookEventIssueAssign, models.HookEventIssueLabel, models.HookEventIssueMilestone: return getDiscordIssuesPayload(p.(*api.IssuePayload), discord) - case models.HookEventIssueComment: + case models.HookEventIssueComment, models.HookEventPullRequestComment: return getDiscordIssueCommentPayload(p.(*api.IssueCommentPayload), discord) case models.HookEventPush: return getDiscordPushPayload(p.(*api.PushPayload), discord) - case models.HookEventPullRequest: + case models.HookEventPullRequest, models.HookEventPullRequestAssign, models.HookEventPullRequestLabel, + models.HookEventPullRequestMilestone, models.HookEventPullRequestSync: return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord) - case models.HookEventPullRequestRejected, models.HookEventPullRequestApproved, models.HookEventPullRequestComment: + case models.HookEventPullRequestReviewRejected, models.HookEventPullRequestReviewApproved, models.HookEventPullRequestReviewComment: return getDiscordPullRequestApprovalPayload(p.(*api.PullRequestPayload), discord, event) case models.HookEventRepository: return getDiscordRepositoryPayload(p.(*api.RepositoryPayload), discord) @@ -428,9 +429,9 @@ func parseHookPullRequestEventType(event models.HookEventType) (string, error) { switch event { - case models.HookEventPullRequestApproved: + case models.HookEventPullRequestReviewApproved: return "approved", nil - case models.HookEventPullRequestRejected: + case models.HookEventPullRequestReviewRejected: return "rejected", nil case models.HookEventPullRequestComment: return "comment", nil diff --git a/modules/webhook/feishu.go b/modules/webhook/feishu.go index 6af78494c..57eb909c4 100644 --- a/modules/webhook/feishu.go +++ b/modules/webhook/feishu.go @@ -189,7 +189,7 @@ func GetFeishuPayload(p api.Payloader, event models.HookEventType, meta string) return getFeishuPushPayload(p.(*api.PushPayload)) case models.HookEventPullRequest: return getFeishuPullRequestPayload(p.(*api.PullRequestPayload)) - case models.HookEventPullRequestApproved, models.HookEventPullRequestRejected, models.HookEventPullRequestComment: + case models.HookEventPullRequestReviewApproved, models.HookEventPullRequestReviewRejected, models.HookEventPullRequestComment: return getFeishuPullRequestApprovalPayload(p.(*api.PullRequestPayload), event) case models.HookEventRepository: return getFeishuRepositoryPayload(p.(*api.RepositoryPayload)) diff --git a/modules/webhook/msteams.go b/modules/webhook/msteams.go index b9ceb5ee0..a0925010d 100644 --- a/modules/webhook/msteams.go +++ b/modules/webhook/msteams.go @@ -395,7 +395,7 @@ func getMSTeamsPullRequestApprovalPayload(p *api.PullRequestPayload, event model var text, title string var color int switch p.Action { - case api.HookIssueSynchronized: + case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { return nil, err @@ -405,9 +405,9 @@ func getMSTeamsPullRequestApprovalPayload(p *api.PullRequestPayload, event model text = p.Review.Content switch event { - case models.HookEventPullRequestApproved: + case models.HookEventPullRequestReviewApproved: color = greenColor - case models.HookEventPullRequestRejected: + case models.HookEventPullRequestReviewRejected: color = redColor case models.HookEventPullRequestComment: color = greyColor @@ -555,15 +555,16 @@ func GetMSTeamsPayload(p api.Payloader, event models.HookEventType, meta string) return getMSTeamsDeletePayload(p.(*api.DeletePayload)) case models.HookEventFork: return getMSTeamsForkPayload(p.(*api.ForkPayload)) - case models.HookEventIssues: + case models.HookEventIssues, models.HookEventIssueAssign, models.HookEventIssueLabel, models.HookEventIssueMilestone: return getMSTeamsIssuesPayload(p.(*api.IssuePayload)) - case models.HookEventIssueComment: + case models.HookEventIssueComment, models.HookEventPullRequestComment: return getMSTeamsIssueCommentPayload(p.(*api.IssueCommentPayload)) case models.HookEventPush: return getMSTeamsPushPayload(p.(*api.PushPayload)) - case models.HookEventPullRequest: + case models.HookEventPullRequest, models.HookEventPullRequestAssign, models.HookEventPullRequestLabel, + models.HookEventPullRequestMilestone, models.HookEventPullRequestSync: return getMSTeamsPullRequestPayload(p.(*api.PullRequestPayload)) - case models.HookEventPullRequestRejected, models.HookEventPullRequestApproved, models.HookEventPullRequestComment: + case models.HookEventPullRequestReviewRejected, models.HookEventPullRequestReviewApproved, models.HookEventPullRequestReviewComment: return getMSTeamsPullRequestApprovalPayload(p.(*api.PullRequestPayload), event) case models.HookEventRepository: return getMSTeamsRepositoryPayload(p.(*api.RepositoryPayload)) diff --git a/modules/webhook/slack.go b/modules/webhook/slack.go index 361e15ece..e3715ab00 100644 --- a/modules/webhook/slack.go +++ b/modules/webhook/slack.go @@ -271,7 +271,7 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM var text string switch p.Action { - case api.HookIssueSynchronized: + case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { return nil, err @@ -324,15 +324,16 @@ func GetSlackPayload(p api.Payloader, event models.HookEventType, meta string) ( return getSlackDeletePayload(p.(*api.DeletePayload), slack) case models.HookEventFork: return getSlackForkPayload(p.(*api.ForkPayload), slack) - case models.HookEventIssues: + case models.HookEventIssues, models.HookEventIssueAssign, models.HookEventIssueLabel, models.HookEventIssueMilestone: return getSlackIssuesPayload(p.(*api.IssuePayload), slack) - case models.HookEventIssueComment: + case models.HookEventIssueComment, models.HookEventPullRequestComment: return getSlackIssueCommentPayload(p.(*api.IssueCommentPayload), slack) case models.HookEventPush: return getSlackPushPayload(p.(*api.PushPayload), slack) - case models.HookEventPullRequest: + case models.HookEventPullRequest, models.HookEventPullRequestAssign, models.HookEventPullRequestLabel, + models.HookEventPullRequestMilestone, models.HookEventPullRequestSync: return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack) - case models.HookEventPullRequestRejected, models.HookEventPullRequestApproved, models.HookEventPullRequestComment: + case models.HookEventPullRequestReviewRejected, models.HookEventPullRequestReviewApproved, models.HookEventPullRequestReviewComment: return getSlackPullRequestApprovalPayload(p.(*api.PullRequestPayload), slack, event) case models.HookEventRepository: return getSlackRepositoryPayload(p.(*api.RepositoryPayload), slack) diff --git a/modules/webhook/telegram.go b/modules/webhook/telegram.go index 47d54f7cb..cf096e2c6 100644 --- a/modules/webhook/telegram.go +++ b/modules/webhook/telegram.go @@ -151,7 +151,7 @@ func getTelegramPullRequestPayload(p *api.PullRequestPayload) (*TelegramPayload, func getTelegramPullRequestApprovalPayload(p *api.PullRequestPayload, event models.HookEventType) (*TelegramPayload, error) { var text, attachmentText string switch p.Action { - case api.HookIssueSynchronized: + case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { return nil, err @@ -203,15 +203,16 @@ func GetTelegramPayload(p api.Payloader, event models.HookEventType, meta string return getTelegramDeletePayload(p.(*api.DeletePayload)) case models.HookEventFork: return getTelegramForkPayload(p.(*api.ForkPayload)) - case models.HookEventIssues: + case models.HookEventIssues, models.HookEventIssueAssign, models.HookEventIssueLabel, models.HookEventIssueMilestone: return getTelegramIssuesPayload(p.(*api.IssuePayload)) - case models.HookEventIssueComment: + case models.HookEventIssueComment, models.HookEventPullRequestComment: return getTelegramIssueCommentPayload(p.(*api.IssueCommentPayload)) case models.HookEventPush: return getTelegramPushPayload(p.(*api.PushPayload)) - case models.HookEventPullRequest: + case models.HookEventPullRequest, models.HookEventPullRequestAssign, models.HookEventPullRequestLabel, + models.HookEventPullRequestMilestone, models.HookEventPullRequestSync: return getTelegramPullRequestPayload(p.(*api.PullRequestPayload)) - case models.HookEventPullRequestRejected, models.HookEventPullRequestApproved, models.HookEventPullRequestComment: + case models.HookEventPullRequestReviewRejected, models.HookEventPullRequestReviewApproved, models.HookEventPullRequestReviewComment: return getTelegramPullRequestApprovalPayload(p.(*api.PullRequestPayload), event) case models.HookEventRepository: return getTelegramRepositoryPayload(p.(*api.RepositoryPayload)) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index ea4eccac8..bf5b03d47 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1380,26 +1380,47 @@ settings.event_desc = Trigger On: settings.event_push_only = Push Events settings.event_send_everything = All Events settings.event_choose = Custom Events… +settings.event_header_repository = Repository Events settings.event_create = Create settings.event_create_desc = Branch or tag created. 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_issues = Issues -settings.event_issues_desc = Issue opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, milestoned, or demilestoned. -settings.event_issue_comment = Issue Comment -settings.event_issue_comment_desc = Issue comment created, edited, or deleted. +settings.event_fork_desc = Repository forked. settings.event_release = Release settings.event_release_desc = Release published, updated or deleted in a repository. -settings.event_pull_request = Pull Request -settings.event_pull_request_desc = Pull request opened, closed, reopened, edited, approved, rejected, review comment, assigned, unassigned, label updated, label cleared or synchronized. settings.event_push = Push settings.event_push_desc = Git push to a repository. -settings.branch_filter = Branch filter -settings.branch_filter_desc = Branch whitelist for push, branch creation and branch deletion events, specified as glob pattern. If empty or *, events for all branches are reported. See github.com/gobwas/glob documentation for syntax. Examples: master, {master,release*}. settings.event_repository = Repository settings.event_repository_desc = Repository created or deleted. +settings.event_header_issue = Issue Events +settings.event_issues = Issues +settings.event_issues_desc = Issue opened, closed, reopened, or edited. +settings.event_issue_assign = Issue Assigned +settings.event_issue_assign_desc = Issue assigned or unassigned. +settings.event_issue_label = Issue Labeled +settings.event_issue_label_desc = Issue labels updated or cleared. +settings.event_issue_milestone = Issue Milestoned +settings.event_issue_milestone_desc = Issue milestoned or demilestoned. +settings.event_issue_comment = Issue Comment +settings.event_issue_comment_desc = Issue comment created, edited, or deleted. +settings.event_header_pull_request = Pull Request Events +settings.event_pull_request = Pull Request +settings.event_pull_request_desc = Pull request opened, closed, reopened, or edited. +settings.event_pull_request_assign = Pull Request Assigned +settings.event_pull_request_assign_desc = Pull request assigned or unassigned. +settings.event_pull_request_label = Pull Request Labeled +settings.event_pull_request_label_desc = Pull request labels updated or cleared. +settings.event_pull_request_milestone = Pull Request Milestoned +settings.event_pull_request_milestone_desc = Pull request milestoned or demilestoned. +settings.event_pull_request_comment = Pull Request Comment +settings.event_pull_request_comment_desc = Pull request comment created, edited, or deleted. +settings.event_pull_request_review = Pull Request Reviewed +settings.event_pull_request_review_desc = Pull request approved, rejected, or review comment. +settings.event_pull_request_sync = Pull Request Synchronized +settings.event_pull_request_sync_desc = Pull request synchronized. +settings.branch_filter = Branch filter +settings.branch_filter_desc = Branch whitelist for push, branch creation and branch deletion events, specified as glob pattern. If empty or *, events for all branches are reported. See github.com/gobwas/glob documentation for syntax. Examples: master, {master,release*}. settings.active = Active settings.active_helper = Information about triggered events will be sent to this webhook URL. settings.add_hook_success = The webhook has been added. diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go index 9a6733f9d..eb2371c50 100644 --- a/routers/api/v1/utils/hook.go +++ b/routers/api/v1/utils/hook.go @@ -87,6 +87,14 @@ func AddRepoHook(ctx *context.APIContext, form *api.CreateHookOption) { } } +func issuesHook(events []string, event string) bool { + return com.IsSliceContainsStr(events, event) || com.IsSliceContainsStr(events, string(models.HookEventIssues)) +} + +func pullHook(events []string, event string) bool { + return com.IsSliceContainsStr(events, event) || com.IsSliceContainsStr(events, string(models.HookEventPullRequest)) +} + // addHook add the hook specified by `form`, `orgID` and `repoID`. If there is // an error, write to `ctx` accordingly. Return (webhook, ok) func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID int64) (*models.Webhook, bool) { @@ -103,15 +111,24 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID HookEvent: &models.HookEvent{ ChooseEvents: true, HookEvents: models.HookEvents{ - Create: com.IsSliceContainsStr(form.Events, string(models.HookEventCreate)), - Delete: com.IsSliceContainsStr(form.Events, string(models.HookEventDelete)), - Fork: com.IsSliceContainsStr(form.Events, string(models.HookEventFork)), - Issues: com.IsSliceContainsStr(form.Events, string(models.HookEventIssues)), - IssueComment: com.IsSliceContainsStr(form.Events, string(models.HookEventIssueComment)), - Push: com.IsSliceContainsStr(form.Events, string(models.HookEventPush)), - PullRequest: com.IsSliceContainsStr(form.Events, string(models.HookEventPullRequest)), - Repository: com.IsSliceContainsStr(form.Events, string(models.HookEventRepository)), - Release: com.IsSliceContainsStr(form.Events, string(models.HookEventRelease)), + Create: com.IsSliceContainsStr(form.Events, string(models.HookEventCreate)), + Delete: com.IsSliceContainsStr(form.Events, string(models.HookEventDelete)), + Fork: com.IsSliceContainsStr(form.Events, string(models.HookEventFork)), + Issues: issuesHook(form.Events, "issues_only"), + IssueAssign: issuesHook(form.Events, string(models.HookEventIssueAssign)), + IssueLabel: issuesHook(form.Events, string(models.HookEventIssueLabel)), + IssueMilestone: issuesHook(form.Events, string(models.HookEventIssueMilestone)), + IssueComment: issuesHook(form.Events, string(models.HookEventIssueComment)), + Push: com.IsSliceContainsStr(form.Events, string(models.HookEventPush)), + PullRequest: pullHook(form.Events, "pull_request_only"), + PullRequestAssign: pullHook(form.Events, string(models.HookEventPullRequestAssign)), + PullRequestLabel: pullHook(form.Events, string(models.HookEventPullRequestLabel)), + PullRequestMilestone: pullHook(form.Events, string(models.HookEventPullRequestMilestone)), + PullRequestComment: pullHook(form.Events, string(models.HookEventPullRequestComment)), + PullRequestReview: pullHook(form.Events, "pull_request_review"), + PullRequestSync: pullHook(form.Events, string(models.HookEventPullRequestSync)), + Repository: com.IsSliceContainsStr(form.Events, string(models.HookEventRepository)), + Release: com.IsSliceContainsStr(form.Events, string(models.HookEventRelease)), }, BranchFilter: form.BranchFilter, }, diff --git a/routers/repo/webhook.go b/routers/repo/webhook.go index 845498967..cf6ff2754 100644 --- a/routers/repo/webhook.go +++ b/routers/repo/webhook.go @@ -136,15 +136,24 @@ func ParseHookEvent(form auth.WebhookForm) *models.HookEvent { SendEverything: form.SendEverything(), ChooseEvents: form.ChooseEvents(), HookEvents: models.HookEvents{ - Create: form.Create, - Delete: form.Delete, - Fork: form.Fork, - Issues: form.Issues, - IssueComment: form.IssueComment, - Release: form.Release, - Push: form.Push, - PullRequest: form.PullRequest, - Repository: form.Repository, + Create: form.Create, + Delete: form.Delete, + Fork: form.Fork, + Issues: form.Issues, + IssueAssign: form.IssueAssign, + IssueLabel: form.IssueLabel, + IssueMilestone: form.IssueMilestone, + IssueComment: form.IssueComment, + Release: form.Release, + Push: form.Push, + PullRequest: form.PullRequest, + PullRequestAssign: form.PullRequestAssign, + PullRequestLabel: form.PullRequestLabel, + PullRequestMilestone: form.PullRequestMilestone, + PullRequestComment: form.PullRequestComment, + PullRequestReview: form.PullRequestReview, + PullRequestSync: form.PullRequestSync, + Repository: form.Repository, }, BranchFilter: form.BranchFilter, } diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/repo/settings/webhook/settings.tmpl index a033ac14b..de74dab05 100644 --- a/templates/repo/settings/webhook/settings.tmpl +++ b/templates/repo/settings/webhook/settings.tmpl @@ -23,6 +23,10 @@
+ +
+ +
@@ -63,6 +67,31 @@
+ +
+
+
+ + + {{.i18n.Tr "repo.settings.event_repository_desc"}} +
+
+
+ +
+
+
+ + + {{.i18n.Tr "repo.settings.event_release_desc"}} +
+
+
+ + +
+ +
@@ -73,6 +102,36 @@
+ +
+
+
+ + + {{.i18n.Tr "repo.settings.event_issue_assign_desc"}} +
+
+
+ +
+
+
+ + + {{.i18n.Tr "repo.settings.event_issue_label_desc"}} +
+
+
+ +
+
+
+ + + {{.i18n.Tr "repo.settings.event_issue_milestone_desc"}} +
+
+
@@ -83,6 +142,11 @@
+ + +
+ +
@@ -93,23 +157,63 @@
- +
- - - {{.i18n.Tr "repo.settings.event_repository_desc"}} + + + {{.i18n.Tr "repo.settings.event_pull_request_assign_desc"}}
- +
- - - {{.i18n.Tr "repo.settings.event_release_desc"}} + + + {{.i18n.Tr "repo.settings.event_pull_request_label_desc"}} +
+
+
+ +
+
+
+ + + {{.i18n.Tr "repo.settings.event_pull_request_milestone_desc"}} +
+
+
+ +
+
+
+ + + {{.i18n.Tr "repo.settings.event_pull_request_comment_desc"}} +
+
+
+ +
+
+
+ + + {{.i18n.Tr "repo.settings.event_pull_request_review_desc"}} +
+
+
+ +
+
+
+ + + {{.i18n.Tr "repo.settings.event_pull_request_sync_desc"}}