Improve notifications for WIP draft PR's (#14663)
* #14559 Reduce amount of email notifications for WIP draft PR's don't notify repo watchers of WIP draft PR's * #13190 Notification when WIP Pull Request is ready for review * Send email notification to repo watchers when WIP PR is created * Send ui notification to repo watchers when WIP PR is created * send specific email notification when PR is marked ready for review instead of reusing the CreatePullRequest action * Fix lint error Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
parent
66f8da538a
commit
17030ced75
|
@ -26,31 +26,32 @@ type ActionType int
|
||||||
|
|
||||||
// Possible action types.
|
// Possible action types.
|
||||||
const (
|
const (
|
||||||
ActionCreateRepo ActionType = iota + 1 // 1
|
ActionCreateRepo ActionType = iota + 1 // 1
|
||||||
ActionRenameRepo // 2
|
ActionRenameRepo // 2
|
||||||
ActionStarRepo // 3
|
ActionStarRepo // 3
|
||||||
ActionWatchRepo // 4
|
ActionWatchRepo // 4
|
||||||
ActionCommitRepo // 5
|
ActionCommitRepo // 5
|
||||||
ActionCreateIssue // 6
|
ActionCreateIssue // 6
|
||||||
ActionCreatePullRequest // 7
|
ActionCreatePullRequest // 7
|
||||||
ActionTransferRepo // 8
|
ActionTransferRepo // 8
|
||||||
ActionPushTag // 9
|
ActionPushTag // 9
|
||||||
ActionCommentIssue // 10
|
ActionCommentIssue // 10
|
||||||
ActionMergePullRequest // 11
|
ActionMergePullRequest // 11
|
||||||
ActionCloseIssue // 12
|
ActionCloseIssue // 12
|
||||||
ActionReopenIssue // 13
|
ActionReopenIssue // 13
|
||||||
ActionClosePullRequest // 14
|
ActionClosePullRequest // 14
|
||||||
ActionReopenPullRequest // 15
|
ActionReopenPullRequest // 15
|
||||||
ActionDeleteTag // 16
|
ActionDeleteTag // 16
|
||||||
ActionDeleteBranch // 17
|
ActionDeleteBranch // 17
|
||||||
ActionMirrorSyncPush // 18
|
ActionMirrorSyncPush // 18
|
||||||
ActionMirrorSyncCreate // 19
|
ActionMirrorSyncCreate // 19
|
||||||
ActionMirrorSyncDelete // 20
|
ActionMirrorSyncDelete // 20
|
||||||
ActionApprovePullRequest // 21
|
ActionApprovePullRequest // 21
|
||||||
ActionRejectPullRequest // 22
|
ActionRejectPullRequest // 22
|
||||||
ActionCommentPull // 23
|
ActionCommentPull // 23
|
||||||
ActionPublishRelease // 24
|
ActionPublishRelease // 24
|
||||||
ActionPullReviewDismissed // 25
|
ActionPullReviewDismissed // 25
|
||||||
|
ActionPullRequestReadyForReview // 26
|
||||||
)
|
)
|
||||||
|
|
||||||
// Action represents user operation type and other information to
|
// Action represents user operation type and other information to
|
||||||
|
|
|
@ -207,13 +207,14 @@ func createOrUpdateIssueNotifications(e Engine, issueID, commentID, notification
|
||||||
for _, id := range issueWatches {
|
for _, id := range issueWatches {
|
||||||
toNotify[id] = struct{}{}
|
toNotify[id] = struct{}{}
|
||||||
}
|
}
|
||||||
|
if !(issue.IsPull && HasWorkInProgressPrefix(issue.Title)) {
|
||||||
repoWatches, err := getRepoWatchersIDs(e, issue.RepoID)
|
repoWatches, err := getRepoWatchersIDs(e, issue.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, id := range repoWatches {
|
for _, id := range repoWatches {
|
||||||
toNotify[id] = struct{}{}
|
toNotify[id] = struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
issueParticipants, err := issue.getParticipantIDsByIssue(e)
|
issueParticipants, err := issue.getParticipantIDsByIssue(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -595,9 +595,13 @@ func (pr *PullRequest) IsWorkInProgress() bool {
|
||||||
log.Error("LoadIssue: %v", err)
|
log.Error("LoadIssue: %v", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
return HasWorkInProgressPrefix(pr.Issue.Title)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasWorkInProgressPrefix determines if the given PR title has a Work In Progress prefix
|
||||||
|
func HasWorkInProgressPrefix(title string) bool {
|
||||||
for _, prefix := range setting.Repository.PullRequest.WorkInProgressPrefixes {
|
for _, prefix := range setting.Repository.PullRequest.WorkInProgressPrefixes {
|
||||||
if strings.HasPrefix(strings.ToUpper(pr.Issue.Title), prefix) {
|
if strings.HasPrefix(strings.ToUpper(title), prefix) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,18 @@ func (m *mailNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mailNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
|
||||||
|
if err := issue.LoadPullRequest(); err != nil {
|
||||||
|
log.Error("issue.LoadPullRequest: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if issue.IsPull && models.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress() {
|
||||||
|
if err := mailer.MailParticipants(issue, doer, models.ActionPullRequestReadyForReview, nil); err != nil {
|
||||||
|
log.Error("MailParticipants: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *mailNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*models.User) {
|
func (m *mailNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*models.User) {
|
||||||
if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, models.ActionCreatePullRequest, mentions); err != nil {
|
if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, models.ActionCreatePullRequest, mentions); err != nil {
|
||||||
log.Error("MailParticipants: %v", err)
|
log.Error("MailParticipants: %v", err)
|
||||||
|
|
|
@ -94,6 +94,19 @@ func (ns *notificationService) NotifyIssueChangeStatus(doer *models.User, issue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ns *notificationService) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
|
||||||
|
if err := issue.LoadPullRequest(); err != nil {
|
||||||
|
log.Error("issue.LoadPullRequest: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if issue.IsPull && models.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress() {
|
||||||
|
_ = ns.issueQueue.Push(issueNotificationOpts{
|
||||||
|
IssueID: issue.ID,
|
||||||
|
NotificationAuthorID: doer.ID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (ns *notificationService) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
|
func (ns *notificationService) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
|
||||||
_ = ns.issueQueue.Push(issueNotificationOpts{
|
_ = ns.issueQueue.Push(issueNotificationOpts{
|
||||||
IssueID: pr.Issue.ID,
|
IssueID: pr.Issue.ID,
|
||||||
|
@ -106,15 +119,32 @@ func (ns *notificationService) NotifyNewPullRequest(pr *models.PullRequest, ment
|
||||||
log.Error("Unable to load issue: %d for pr: %d: Error: %v", pr.IssueID, pr.ID, err)
|
log.Error("Unable to load issue: %d for pr: %d: Error: %v", pr.IssueID, pr.ID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = ns.issueQueue.Push(issueNotificationOpts{
|
toNotify := make(map[int64]struct{}, 32)
|
||||||
IssueID: pr.Issue.ID,
|
repoWatchers, err := models.GetRepoWatchersIDs(pr.Issue.RepoID)
|
||||||
NotificationAuthorID: pr.Issue.PosterID,
|
if err != nil {
|
||||||
})
|
log.Error("GetRepoWatchersIDs: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, id := range repoWatchers {
|
||||||
|
toNotify[id] = struct{}{}
|
||||||
|
}
|
||||||
|
issueParticipants, err := models.GetParticipantsIDsByIssueID(pr.IssueID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetParticipantsIDsByIssueID: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, id := range issueParticipants {
|
||||||
|
toNotify[id] = struct{}{}
|
||||||
|
}
|
||||||
|
delete(toNotify, pr.Issue.PosterID)
|
||||||
for _, mention := range mentions {
|
for _, mention := range mentions {
|
||||||
|
toNotify[mention.ID] = struct{}{}
|
||||||
|
}
|
||||||
|
for receiverID := range toNotify {
|
||||||
_ = ns.issueQueue.Push(issueNotificationOpts{
|
_ = ns.issueQueue.Push(issueNotificationOpts{
|
||||||
IssueID: pr.Issue.ID,
|
IssueID: pr.Issue.ID,
|
||||||
NotificationAuthorID: pr.Issue.PosterID,
|
NotificationAuthorID: pr.Issue.PosterID,
|
||||||
ReceiverID: mention.ID,
|
ReceiverID: receiverID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -377,6 +377,8 @@ func actionToTemplate(issue *models.Issue, actionType models.ActionType,
|
||||||
name = "merge"
|
name = "merge"
|
||||||
case models.ActionPullReviewDismissed:
|
case models.ActionPullReviewDismissed:
|
||||||
name = "review_dismissed"
|
name = "review_dismissed"
|
||||||
|
case models.ActionPullRequestReadyForReview:
|
||||||
|
name = "ready_for_review"
|
||||||
default:
|
default:
|
||||||
switch commentType {
|
switch commentType {
|
||||||
case models.CommentTypeReview:
|
case models.CommentTypeReview:
|
||||||
|
|
|
@ -30,7 +30,7 @@ const (
|
||||||
|
|
||||||
// mailIssueCommentToParticipants can be used for both new issue creation and comment.
|
// mailIssueCommentToParticipants can be used for both new issue creation and comment.
|
||||||
// This function sends two list of emails:
|
// This function sends two list of emails:
|
||||||
// 1. Repository watchers and users who are participated in comments.
|
// 1. Repository watchers (except for WIP pull requests) and users who are participated in comments.
|
||||||
// 2. Users who are not in 1. but get mentioned in current issue/comment.
|
// 2. Users who are not in 1. but get mentioned in current issue/comment.
|
||||||
func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*models.User) error {
|
func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*models.User) error {
|
||||||
|
|
||||||
|
@ -74,11 +74,13 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*models.
|
||||||
|
|
||||||
// =========== Repo watchers ===========
|
// =========== Repo watchers ===========
|
||||||
// Make repo watchers last, since it's likely the list with the most users
|
// Make repo watchers last, since it's likely the list with the most users
|
||||||
ids, err = models.GetRepoWatchersIDs(ctx.Issue.RepoID)
|
if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress() && ctx.ActionType != models.ActionCreatePullRequest) {
|
||||||
if err != nil {
|
ids, err = models.GetRepoWatchersIDs(ctx.Issue.RepoID)
|
||||||
return fmt.Errorf("GetRepoWatchersIDs(%d): %v", ctx.Issue.RepoID, err)
|
if err != nil {
|
||||||
|
return fmt.Errorf("GetRepoWatchersIDs(%d): %v", ctx.Issue.RepoID, err)
|
||||||
|
}
|
||||||
|
unfiltered = append(ids, unfiltered...)
|
||||||
}
|
}
|
||||||
unfiltered = append(ids, unfiltered...)
|
|
||||||
|
|
||||||
visited := make(map[int64]bool, len(unfiltered)+len(mentions)+1)
|
visited := make(map[int64]bool, len(unfiltered)+len(mentions)+1)
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,8 @@
|
||||||
<b>@{{.Doer.Name}}</b> commented on this pull request.
|
<b>@{{.Doer.Name}}</b> commented on this pull request.
|
||||||
{{else if eq .ActionName "review_dismissed"}}
|
{{else if eq .ActionName "review_dismissed"}}
|
||||||
<b>@{{.Doer.Name}}</b> dismissed last review from {{.Comment.Review.Reviewer.Name}} for this pull request.
|
<b>@{{.Doer.Name}}</b> dismissed last review from {{.Comment.Review.Reviewer.Name}} for this pull request.
|
||||||
|
{{else if eq .ActionName "ready_for_review"}}
|
||||||
|
<b>@{{.Doer.Name}}</b> marked this pull request ready for review.
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{- if eq .Body ""}}
|
{{- if eq .Body ""}}
|
||||||
|
|
Loading…
Reference in a new issue