code.gitea.io/gitea@v1.22.3/services/actions/notifier.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package actions
     5  
     6  import (
     7  	"context"
     8  
     9  	issues_model "code.gitea.io/gitea/models/issues"
    10  	packages_model "code.gitea.io/gitea/models/packages"
    11  	perm_model "code.gitea.io/gitea/models/perm"
    12  	access_model "code.gitea.io/gitea/models/perm/access"
    13  	repo_model "code.gitea.io/gitea/models/repo"
    14  	user_model "code.gitea.io/gitea/models/user"
    15  	"code.gitea.io/gitea/modules/git"
    16  	"code.gitea.io/gitea/modules/log"
    17  	"code.gitea.io/gitea/modules/repository"
    18  	"code.gitea.io/gitea/modules/setting"
    19  	api "code.gitea.io/gitea/modules/structs"
    20  	webhook_module "code.gitea.io/gitea/modules/webhook"
    21  	"code.gitea.io/gitea/services/convert"
    22  	notify_service "code.gitea.io/gitea/services/notify"
    23  )
    24  
    25  type actionsNotifier struct {
    26  	notify_service.NullNotifier
    27  }
    28  
    29  var _ notify_service.Notifier = &actionsNotifier{}
    30  
    31  // NewNotifier create a new actionsNotifier notifier
    32  func NewNotifier() notify_service.Notifier {
    33  	return &actionsNotifier{}
    34  }
    35  
    36  // NewIssue notifies issue created event
    37  func (n *actionsNotifier) NewIssue(ctx context.Context, issue *issues_model.Issue, _ []*user_model.User) {
    38  	ctx = withMethod(ctx, "NewIssue")
    39  	if err := issue.LoadRepo(ctx); err != nil {
    40  		log.Error("issue.LoadRepo: %v", err)
    41  		return
    42  	}
    43  	if err := issue.LoadPoster(ctx); err != nil {
    44  		log.Error("issue.LoadPoster: %v", err)
    45  		return
    46  	}
    47  	permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, issue.Poster)
    48  
    49  	newNotifyInputFromIssue(issue, webhook_module.HookEventIssues).WithPayload(&api.IssuePayload{
    50  		Action:     api.HookIssueOpened,
    51  		Index:      issue.Index,
    52  		Issue:      convert.ToAPIIssue(ctx, issue.Poster, issue),
    53  		Repository: convert.ToRepo(ctx, issue.Repo, permission),
    54  		Sender:     convert.ToUser(ctx, issue.Poster, nil),
    55  	}).Notify(withMethod(ctx, "NewIssue"))
    56  }
    57  
    58  // IssueChangeContent notifies change content of issue
    59  func (n *actionsNotifier) IssueChangeContent(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldContent string) {
    60  	ctx = withMethod(ctx, "IssueChangeContent")
    61  
    62  	var err error
    63  	if err = issue.LoadRepo(ctx); err != nil {
    64  		log.Error("LoadRepo: %v", err)
    65  		return
    66  	}
    67  
    68  	permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, issue.Poster)
    69  	if issue.IsPull {
    70  		if err = issue.LoadPullRequest(ctx); err != nil {
    71  			log.Error("loadPullRequest: %v", err)
    72  			return
    73  		}
    74  		newNotifyInputFromIssue(issue, webhook_module.HookEventPullRequest).
    75  			WithDoer(doer).
    76  			WithPayload(&api.PullRequestPayload{
    77  				Action:      api.HookIssueEdited,
    78  				Index:       issue.Index,
    79  				PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
    80  				Repository:  convert.ToRepo(ctx, issue.Repo, access_model.Permission{AccessMode: perm_model.AccessModeNone}),
    81  				Sender:      convert.ToUser(ctx, doer, nil),
    82  			}).
    83  			WithPullRequest(issue.PullRequest).
    84  			Notify(ctx)
    85  		return
    86  	}
    87  	newNotifyInputFromIssue(issue, webhook_module.HookEventIssues).
    88  		WithDoer(doer).
    89  		WithPayload(&api.IssuePayload{
    90  			Action:     api.HookIssueEdited,
    91  			Index:      issue.Index,
    92  			Issue:      convert.ToAPIIssue(ctx, doer, issue),
    93  			Repository: convert.ToRepo(ctx, issue.Repo, permission),
    94  			Sender:     convert.ToUser(ctx, doer, nil),
    95  		}).
    96  		Notify(ctx)
    97  }
    98  
    99  // IssueChangeStatus notifies close or reopen issue to notifiers
   100  func (n *actionsNotifier) IssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, _ *issues_model.Comment, isClosed bool) {
   101  	ctx = withMethod(ctx, "IssueChangeStatus")
   102  	permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, issue.Poster)
   103  	if issue.IsPull {
   104  		if err := issue.LoadPullRequest(ctx); err != nil {
   105  			log.Error("LoadPullRequest: %v", err)
   106  			return
   107  		}
   108  		// Merge pull request calls issue.changeStatus so we need to handle separately.
   109  		apiPullRequest := &api.PullRequestPayload{
   110  			Index:       issue.Index,
   111  			PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
   112  			Repository:  convert.ToRepo(ctx, issue.Repo, permission),
   113  			Sender:      convert.ToUser(ctx, doer, nil),
   114  			CommitID:    commitID,
   115  		}
   116  		if isClosed {
   117  			apiPullRequest.Action = api.HookIssueClosed
   118  		} else {
   119  			apiPullRequest.Action = api.HookIssueReOpened
   120  		}
   121  		newNotifyInputFromIssue(issue, webhook_module.HookEventPullRequest).
   122  			WithDoer(doer).
   123  			WithPayload(apiPullRequest).
   124  			WithPullRequest(issue.PullRequest).
   125  			Notify(ctx)
   126  		return
   127  	}
   128  	apiIssue := &api.IssuePayload{
   129  		Index:      issue.Index,
   130  		Issue:      convert.ToAPIIssue(ctx, doer, issue),
   131  		Repository: convert.ToRepo(ctx, issue.Repo, permission),
   132  		Sender:     convert.ToUser(ctx, doer, nil),
   133  	}
   134  	if isClosed {
   135  		apiIssue.Action = api.HookIssueClosed
   136  	} else {
   137  		apiIssue.Action = api.HookIssueReOpened
   138  	}
   139  	newNotifyInputFromIssue(issue, webhook_module.HookEventIssues).
   140  		WithDoer(doer).
   141  		WithPayload(apiIssue).
   142  		Notify(ctx)
   143  }
   144  
   145  // IssueChangeAssignee notifies assigned or unassigned to notifiers
   146  func (n *actionsNotifier) IssueChangeAssignee(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) {
   147  	ctx = withMethod(ctx, "IssueChangeAssignee")
   148  
   149  	var action api.HookIssueAction
   150  	if removed {
   151  		action = api.HookIssueUnassigned
   152  	} else {
   153  		action = api.HookIssueAssigned
   154  	}
   155  
   156  	hookEvent := webhook_module.HookEventIssueAssign
   157  	if issue.IsPull {
   158  		hookEvent = webhook_module.HookEventPullRequestAssign
   159  	}
   160  
   161  	notifyIssueChange(ctx, doer, issue, hookEvent, action)
   162  }
   163  
   164  // IssueChangeMilestone notifies assignee to notifiers
   165  func (n *actionsNotifier) IssueChangeMilestone(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) {
   166  	ctx = withMethod(ctx, "IssueChangeMilestone")
   167  
   168  	var action api.HookIssueAction
   169  	if issue.MilestoneID > 0 {
   170  		action = api.HookIssueMilestoned
   171  	} else {
   172  		action = api.HookIssueDemilestoned
   173  	}
   174  
   175  	hookEvent := webhook_module.HookEventIssueMilestone
   176  	if issue.IsPull {
   177  		hookEvent = webhook_module.HookEventPullRequestMilestone
   178  	}
   179  
   180  	notifyIssueChange(ctx, doer, issue, hookEvent, action)
   181  }
   182  
   183  func (n *actionsNotifier) IssueChangeLabels(ctx context.Context, doer *user_model.User, issue *issues_model.Issue,
   184  	_, _ []*issues_model.Label,
   185  ) {
   186  	ctx = withMethod(ctx, "IssueChangeLabels")
   187  
   188  	hookEvent := webhook_module.HookEventIssueLabel
   189  	if issue.IsPull {
   190  		hookEvent = webhook_module.HookEventPullRequestLabel
   191  	}
   192  
   193  	notifyIssueChange(ctx, doer, issue, hookEvent, api.HookIssueLabelUpdated)
   194  }
   195  
   196  func notifyIssueChange(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, event webhook_module.HookEventType, action api.HookIssueAction) {
   197  	var err error
   198  	if err = issue.LoadRepo(ctx); err != nil {
   199  		log.Error("LoadRepo: %v", err)
   200  		return
   201  	}
   202  
   203  	if err = issue.LoadPoster(ctx); err != nil {
   204  		log.Error("LoadPoster: %v", err)
   205  		return
   206  	}
   207  
   208  	if issue.IsPull {
   209  		if err = issue.LoadPullRequest(ctx); err != nil {
   210  			log.Error("loadPullRequest: %v", err)
   211  			return
   212  		}
   213  		newNotifyInputFromIssue(issue, event).
   214  			WithDoer(doer).
   215  			WithPayload(&api.PullRequestPayload{
   216  				Action:      action,
   217  				Index:       issue.Index,
   218  				PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
   219  				Repository:  convert.ToRepo(ctx, issue.Repo, access_model.Permission{AccessMode: perm_model.AccessModeNone}),
   220  				Sender:      convert.ToUser(ctx, doer, nil),
   221  			}).
   222  			WithPullRequest(issue.PullRequest).
   223  			Notify(ctx)
   224  		return
   225  	}
   226  	permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, issue.Poster)
   227  	newNotifyInputFromIssue(issue, event).
   228  		WithDoer(doer).
   229  		WithPayload(&api.IssuePayload{
   230  			Action:     action,
   231  			Index:      issue.Index,
   232  			Issue:      convert.ToAPIIssue(ctx, doer, issue),
   233  			Repository: convert.ToRepo(ctx, issue.Repo, permission),
   234  			Sender:     convert.ToUser(ctx, doer, nil),
   235  		}).
   236  		Notify(ctx)
   237  }
   238  
   239  // CreateIssueComment notifies comment on an issue to notifiers
   240  func (n *actionsNotifier) CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository,
   241  	issue *issues_model.Issue, comment *issues_model.Comment, _ []*user_model.User,
   242  ) {
   243  	ctx = withMethod(ctx, "CreateIssueComment")
   244  
   245  	if issue.IsPull {
   246  		notifyIssueCommentChange(ctx, doer, comment, "", webhook_module.HookEventPullRequestComment, api.HookIssueCommentCreated)
   247  		return
   248  	}
   249  	notifyIssueCommentChange(ctx, doer, comment, "", webhook_module.HookEventIssueComment, api.HookIssueCommentCreated)
   250  }
   251  
   252  func (n *actionsNotifier) UpdateComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment, oldContent string) {
   253  	ctx = withMethod(ctx, "UpdateComment")
   254  
   255  	if err := c.LoadIssue(ctx); err != nil {
   256  		log.Error("LoadIssue: %v", err)
   257  		return
   258  	}
   259  
   260  	if c.Issue.IsPull {
   261  		notifyIssueCommentChange(ctx, doer, c, oldContent, webhook_module.HookEventPullRequestComment, api.HookIssueCommentEdited)
   262  		return
   263  	}
   264  	notifyIssueCommentChange(ctx, doer, c, oldContent, webhook_module.HookEventIssueComment, api.HookIssueCommentEdited)
   265  }
   266  
   267  func (n *actionsNotifier) DeleteComment(ctx context.Context, doer *user_model.User, comment *issues_model.Comment) {
   268  	ctx = withMethod(ctx, "DeleteComment")
   269  
   270  	if err := comment.LoadIssue(ctx); err != nil {
   271  		log.Error("LoadIssue: %v", err)
   272  		return
   273  	}
   274  
   275  	if comment.Issue.IsPull {
   276  		notifyIssueCommentChange(ctx, doer, comment, "", webhook_module.HookEventPullRequestComment, api.HookIssueCommentDeleted)
   277  		return
   278  	}
   279  	notifyIssueCommentChange(ctx, doer, comment, "", webhook_module.HookEventIssueComment, api.HookIssueCommentDeleted)
   280  }
   281  
   282  func notifyIssueCommentChange(ctx context.Context, doer *user_model.User, comment *issues_model.Comment, oldContent string, event webhook_module.HookEventType, action api.HookIssueCommentAction) {
   283  	if err := comment.LoadIssue(ctx); err != nil {
   284  		log.Error("LoadIssue: %v", err)
   285  		return
   286  	}
   287  	if err := comment.Issue.LoadAttributes(ctx); err != nil {
   288  		log.Error("LoadAttributes: %v", err)
   289  		return
   290  	}
   291  
   292  	permission, _ := access_model.GetUserRepoPermission(ctx, comment.Issue.Repo, doer)
   293  
   294  	payload := &api.IssueCommentPayload{
   295  		Action:     action,
   296  		Issue:      convert.ToAPIIssue(ctx, doer, comment.Issue),
   297  		Comment:    convert.ToAPIComment(ctx, comment.Issue.Repo, comment),
   298  		Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission),
   299  		Sender:     convert.ToUser(ctx, doer, nil),
   300  		IsPull:     comment.Issue.IsPull,
   301  	}
   302  
   303  	if action == api.HookIssueCommentEdited {
   304  		payload.Changes = &api.ChangesPayload{
   305  			Body: &api.ChangesFromPayload{
   306  				From: oldContent,
   307  			},
   308  		}
   309  	}
   310  
   311  	if comment.Issue.IsPull {
   312  		if err := comment.Issue.LoadPullRequest(ctx); err != nil {
   313  			log.Error("LoadPullRequest: %v", err)
   314  			return
   315  		}
   316  		newNotifyInputFromIssue(comment.Issue, event).
   317  			WithDoer(doer).
   318  			WithPayload(payload).
   319  			WithPullRequest(comment.Issue.PullRequest).
   320  			Notify(ctx)
   321  		return
   322  	}
   323  
   324  	newNotifyInputFromIssue(comment.Issue, event).
   325  		WithDoer(doer).
   326  		WithPayload(payload).
   327  		Notify(ctx)
   328  }
   329  
   330  func (n *actionsNotifier) NewPullRequest(ctx context.Context, pull *issues_model.PullRequest, _ []*user_model.User) {
   331  	ctx = withMethod(ctx, "NewPullRequest")
   332  
   333  	if err := pull.LoadIssue(ctx); err != nil {
   334  		log.Error("pull.LoadIssue: %v", err)
   335  		return
   336  	}
   337  	if err := pull.Issue.LoadRepo(ctx); err != nil {
   338  		log.Error("pull.Issue.LoadRepo: %v", err)
   339  		return
   340  	}
   341  	if err := pull.Issue.LoadPoster(ctx); err != nil {
   342  		log.Error("pull.Issue.LoadPoster: %v", err)
   343  		return
   344  	}
   345  
   346  	permission, _ := access_model.GetUserRepoPermission(ctx, pull.Issue.Repo, pull.Issue.Poster)
   347  
   348  	newNotifyInputFromIssue(pull.Issue, webhook_module.HookEventPullRequest).
   349  		WithPayload(&api.PullRequestPayload{
   350  			Action:      api.HookIssueOpened,
   351  			Index:       pull.Issue.Index,
   352  			PullRequest: convert.ToAPIPullRequest(ctx, pull, nil),
   353  			Repository:  convert.ToRepo(ctx, pull.Issue.Repo, permission),
   354  			Sender:      convert.ToUser(ctx, pull.Issue.Poster, nil),
   355  		}).
   356  		WithPullRequest(pull).
   357  		Notify(ctx)
   358  }
   359  
   360  func (n *actionsNotifier) CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) {
   361  	ctx = withMethod(ctx, "CreateRepository")
   362  
   363  	newNotifyInput(repo, doer, webhook_module.HookEventRepository).WithPayload(&api.RepositoryPayload{
   364  		Action:       api.HookRepoCreated,
   365  		Repository:   convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeOwner}),
   366  		Organization: convert.ToUser(ctx, u, nil),
   367  		Sender:       convert.ToUser(ctx, doer, nil),
   368  	}).Notify(ctx)
   369  }
   370  
   371  func (n *actionsNotifier) ForkRepository(ctx context.Context, doer *user_model.User, oldRepo, repo *repo_model.Repository) {
   372  	ctx = withMethod(ctx, "ForkRepository")
   373  
   374  	oldPermission, _ := access_model.GetUserRepoPermission(ctx, oldRepo, doer)
   375  	permission, _ := access_model.GetUserRepoPermission(ctx, repo, doer)
   376  
   377  	// forked webhook
   378  	newNotifyInput(oldRepo, doer, webhook_module.HookEventFork).WithPayload(&api.ForkPayload{
   379  		Forkee: convert.ToRepo(ctx, oldRepo, oldPermission),
   380  		Repo:   convert.ToRepo(ctx, repo, permission),
   381  		Sender: convert.ToUser(ctx, doer, nil),
   382  	}).Notify(ctx)
   383  
   384  	u := repo.MustOwner(ctx)
   385  
   386  	// Add to hook queue for created repo after session commit.
   387  	if u.IsOrganization() {
   388  		newNotifyInput(repo, doer, webhook_module.HookEventRepository).
   389  			WithRef(git.RefNameFromBranch(oldRepo.DefaultBranch).String()).
   390  			WithPayload(&api.RepositoryPayload{
   391  				Action:       api.HookRepoCreated,
   392  				Repository:   convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeOwner}),
   393  				Organization: convert.ToUser(ctx, u, nil),
   394  				Sender:       convert.ToUser(ctx, doer, nil),
   395  			}).Notify(ctx)
   396  	}
   397  }
   398  
   399  func (n *actionsNotifier) PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, review *issues_model.Review, _ *issues_model.Comment, _ []*user_model.User) {
   400  	ctx = withMethod(ctx, "PullRequestReview")
   401  
   402  	var reviewHookType webhook_module.HookEventType
   403  
   404  	switch review.Type {
   405  	case issues_model.ReviewTypeApprove:
   406  		reviewHookType = webhook_module.HookEventPullRequestReviewApproved
   407  	case issues_model.ReviewTypeComment:
   408  		reviewHookType = webhook_module.HookEventPullRequestReviewComment
   409  	case issues_model.ReviewTypeReject:
   410  		reviewHookType = webhook_module.HookEventPullRequestReviewRejected
   411  	default:
   412  		// unsupported review webhook type here
   413  		log.Error("Unsupported review webhook type")
   414  		return
   415  	}
   416  
   417  	if err := pr.LoadIssue(ctx); err != nil {
   418  		log.Error("pr.LoadIssue: %v", err)
   419  		return
   420  	}
   421  
   422  	permission, err := access_model.GetUserRepoPermission(ctx, review.Issue.Repo, review.Issue.Poster)
   423  	if err != nil {
   424  		log.Error("models.GetUserRepoPermission: %v", err)
   425  		return
   426  	}
   427  
   428  	newNotifyInput(review.Issue.Repo, review.Reviewer, reviewHookType).
   429  		WithRef(review.CommitID).
   430  		WithPayload(&api.PullRequestPayload{
   431  			Action:      api.HookIssueReviewed,
   432  			Index:       review.Issue.Index,
   433  			PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
   434  			Repository:  convert.ToRepo(ctx, review.Issue.Repo, permission),
   435  			Sender:      convert.ToUser(ctx, review.Reviewer, nil),
   436  			Review: &api.ReviewPayload{
   437  				Type:    string(reviewHookType),
   438  				Content: review.Content,
   439  			},
   440  		}).Notify(ctx)
   441  }
   442  
   443  func (n *actionsNotifier) PullRequestReviewRequest(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) {
   444  	if !issue.IsPull {
   445  		log.Warn("PullRequestReviewRequest: issue is not a pull request: %v", issue.ID)
   446  		return
   447  	}
   448  
   449  	ctx = withMethod(ctx, "PullRequestReviewRequest")
   450  
   451  	permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
   452  	if err := issue.LoadPullRequest(ctx); err != nil {
   453  		log.Error("LoadPullRequest failed: %v", err)
   454  		return
   455  	}
   456  	var action api.HookIssueAction
   457  	if isRequest {
   458  		action = api.HookIssueReviewRequested
   459  	} else {
   460  		action = api.HookIssueReviewRequestRemoved
   461  	}
   462  	newNotifyInputFromIssue(issue, webhook_module.HookEventPullRequestReviewRequest).
   463  		WithDoer(doer).
   464  		WithPayload(&api.PullRequestPayload{
   465  			Action:            action,
   466  			Index:             issue.Index,
   467  			PullRequest:       convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
   468  			RequestedReviewer: convert.ToUser(ctx, reviewer, nil),
   469  			Repository:        convert.ToRepo(ctx, issue.Repo, permission),
   470  			Sender:            convert.ToUser(ctx, doer, nil),
   471  		}).
   472  		WithPullRequest(issue.PullRequest).
   473  		Notify(ctx)
   474  }
   475  
   476  func (*actionsNotifier) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
   477  	ctx = withMethod(ctx, "MergePullRequest")
   478  
   479  	// Reload pull request information.
   480  	if err := pr.LoadAttributes(ctx); err != nil {
   481  		log.Error("LoadAttributes: %v", err)
   482  		return
   483  	}
   484  
   485  	if err := pr.LoadIssue(ctx); err != nil {
   486  		log.Error("LoadAttributes: %v", err)
   487  		return
   488  	}
   489  
   490  	if err := pr.Issue.LoadRepo(ctx); err != nil {
   491  		log.Error("pr.Issue.LoadRepo: %v", err)
   492  		return
   493  	}
   494  
   495  	permission, err := access_model.GetUserRepoPermission(ctx, pr.Issue.Repo, doer)
   496  	if err != nil {
   497  		log.Error("models.GetUserRepoPermission: %v", err)
   498  		return
   499  	}
   500  
   501  	// Merge pull request calls issue.changeStatus so we need to handle separately.
   502  	apiPullRequest := &api.PullRequestPayload{
   503  		Index:       pr.Issue.Index,
   504  		PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
   505  		Repository:  convert.ToRepo(ctx, pr.Issue.Repo, permission),
   506  		Sender:      convert.ToUser(ctx, doer, nil),
   507  		Action:      api.HookIssueClosed,
   508  	}
   509  
   510  	newNotifyInput(pr.Issue.Repo, doer, webhook_module.HookEventPullRequest).
   511  		WithRef(pr.MergedCommitID).
   512  		WithPayload(apiPullRequest).
   513  		WithPullRequest(pr).
   514  		Notify(ctx)
   515  }
   516  
   517  func (n *actionsNotifier) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
   518  	commitID, _ := git.NewIDFromString(opts.NewCommitID)
   519  	if commitID.IsZero() {
   520  		log.Trace("new commitID is empty")
   521  		return
   522  	}
   523  
   524  	ctx = withMethod(ctx, "PushCommits")
   525  
   526  	apiPusher := convert.ToUser(ctx, pusher, nil)
   527  	apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
   528  	if err != nil {
   529  		log.Error("commits.ToAPIPayloadCommits failed: %v", err)
   530  		return
   531  	}
   532  
   533  	newNotifyInput(repo, pusher, webhook_module.HookEventPush).
   534  		WithRef(opts.RefFullName.String()).
   535  		WithPayload(&api.PushPayload{
   536  			Ref:        opts.RefFullName.String(),
   537  			Before:     opts.OldCommitID,
   538  			After:      opts.NewCommitID,
   539  			CompareURL: setting.AppURL + commits.CompareURL,
   540  			Commits:    apiCommits,
   541  			HeadCommit: apiHeadCommit,
   542  			Repo:       convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeOwner}),
   543  			Pusher:     apiPusher,
   544  			Sender:     apiPusher,
   545  		}).
   546  		Notify(ctx)
   547  }
   548  
   549  func (n *actionsNotifier) CreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
   550  	ctx = withMethod(ctx, "CreateRef")
   551  
   552  	apiPusher := convert.ToUser(ctx, pusher, nil)
   553  	apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeNone})
   554  
   555  	newNotifyInput(repo, pusher, webhook_module.HookEventCreate).
   556  		WithRef(refFullName.String()).
   557  		WithPayload(&api.CreatePayload{
   558  			Ref:     refFullName.String(),
   559  			Sha:     refID,
   560  			RefType: refFullName.RefType(),
   561  			Repo:    apiRepo,
   562  			Sender:  apiPusher,
   563  		}).
   564  		Notify(ctx)
   565  }
   566  
   567  func (n *actionsNotifier) DeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
   568  	ctx = withMethod(ctx, "DeleteRef")
   569  
   570  	apiPusher := convert.ToUser(ctx, pusher, nil)
   571  	apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeNone})
   572  
   573  	newNotifyInput(repo, pusher, webhook_module.HookEventDelete).
   574  		WithPayload(&api.DeletePayload{
   575  			Ref:        refFullName.String(),
   576  			RefType:    refFullName.RefType(),
   577  			PusherType: api.PusherTypeUser,
   578  			Repo:       apiRepo,
   579  			Sender:     apiPusher,
   580  		}).
   581  		Notify(ctx)
   582  }
   583  
   584  func (n *actionsNotifier) SyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
   585  	ctx = withMethod(ctx, "SyncPushCommits")
   586  
   587  	apiPusher := convert.ToUser(ctx, pusher, nil)
   588  	apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
   589  	if err != nil {
   590  		log.Error("commits.ToAPIPayloadCommits failed: %v", err)
   591  		return
   592  	}
   593  
   594  	newNotifyInput(repo, pusher, webhook_module.HookEventPush).
   595  		WithRef(opts.RefFullName.String()).
   596  		WithPayload(&api.PushPayload{
   597  			Ref:          opts.RefFullName.String(),
   598  			Before:       opts.OldCommitID,
   599  			After:        opts.NewCommitID,
   600  			CompareURL:   setting.AppURL + commits.CompareURL,
   601  			Commits:      apiCommits,
   602  			TotalCommits: commits.Len,
   603  			HeadCommit:   apiHeadCommit,
   604  			Repo:         convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeOwner}),
   605  			Pusher:       apiPusher,
   606  			Sender:       apiPusher,
   607  		}).
   608  		Notify(ctx)
   609  }
   610  
   611  func (n *actionsNotifier) SyncCreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
   612  	ctx = withMethod(ctx, "SyncCreateRef")
   613  	n.CreateRef(ctx, pusher, repo, refFullName, refID)
   614  }
   615  
   616  func (n *actionsNotifier) SyncDeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
   617  	ctx = withMethod(ctx, "SyncDeleteRef")
   618  	n.DeleteRef(ctx, pusher, repo, refFullName)
   619  }
   620  
   621  func (n *actionsNotifier) NewRelease(ctx context.Context, rel *repo_model.Release) {
   622  	ctx = withMethod(ctx, "NewRelease")
   623  	notifyRelease(ctx, rel.Publisher, rel, api.HookReleasePublished)
   624  }
   625  
   626  func (n *actionsNotifier) UpdateRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release) {
   627  	ctx = withMethod(ctx, "UpdateRelease")
   628  	notifyRelease(ctx, doer, rel, api.HookReleaseUpdated)
   629  }
   630  
   631  func (n *actionsNotifier) DeleteRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release) {
   632  	if rel.IsTag {
   633  		// has sent same action in `PushCommits`, so skip it.
   634  		return
   635  	}
   636  	ctx = withMethod(ctx, "DeleteRelease")
   637  	notifyRelease(ctx, doer, rel, api.HookReleaseDeleted)
   638  }
   639  
   640  func (n *actionsNotifier) PackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
   641  	ctx = withMethod(ctx, "PackageCreate")
   642  	notifyPackage(ctx, doer, pd, api.HookPackageCreated)
   643  }
   644  
   645  func (n *actionsNotifier) PackageDelete(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor) {
   646  	ctx = withMethod(ctx, "PackageDelete")
   647  	notifyPackage(ctx, doer, pd, api.HookPackageDeleted)
   648  }
   649  
   650  func (n *actionsNotifier) AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
   651  	ctx = withMethod(ctx, "AutoMergePullRequest")
   652  	n.MergePullRequest(ctx, doer, pr)
   653  }
   654  
   655  func (n *actionsNotifier) PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
   656  	ctx = withMethod(ctx, "PullRequestSynchronized")
   657  
   658  	if err := pr.LoadIssue(ctx); err != nil {
   659  		log.Error("LoadAttributes: %v", err)
   660  		return
   661  	}
   662  
   663  	if err := pr.Issue.LoadRepo(ctx); err != nil {
   664  		log.Error("pr.Issue.LoadRepo: %v", err)
   665  		return
   666  	}
   667  
   668  	newNotifyInput(pr.Issue.Repo, doer, webhook_module.HookEventPullRequestSync).
   669  		WithPayload(&api.PullRequestPayload{
   670  			Action:      api.HookIssueSynchronized,
   671  			Index:       pr.Issue.Index,
   672  			PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
   673  			Repository:  convert.ToRepo(ctx, pr.Issue.Repo, access_model.Permission{AccessMode: perm_model.AccessModeNone}),
   674  			Sender:      convert.ToUser(ctx, doer, nil),
   675  		}).
   676  		WithPullRequest(pr).
   677  		Notify(ctx)
   678  }
   679  
   680  func (n *actionsNotifier) PullRequestChangeTargetBranch(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, oldBranch string) {
   681  	ctx = withMethod(ctx, "PullRequestChangeTargetBranch")
   682  
   683  	if err := pr.LoadIssue(ctx); err != nil {
   684  		log.Error("LoadAttributes: %v", err)
   685  		return
   686  	}
   687  
   688  	if err := pr.Issue.LoadRepo(ctx); err != nil {
   689  		log.Error("pr.Issue.LoadRepo: %v", err)
   690  		return
   691  	}
   692  
   693  	permission, _ := access_model.GetUserRepoPermission(ctx, pr.Issue.Repo, pr.Issue.Poster)
   694  	newNotifyInput(pr.Issue.Repo, doer, webhook_module.HookEventPullRequest).
   695  		WithPayload(&api.PullRequestPayload{
   696  			Action: api.HookIssueEdited,
   697  			Index:  pr.Issue.Index,
   698  			Changes: &api.ChangesPayload{
   699  				Ref: &api.ChangesFromPayload{
   700  					From: oldBranch,
   701  				},
   702  			},
   703  			PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
   704  			Repository:  convert.ToRepo(ctx, pr.Issue.Repo, permission),
   705  			Sender:      convert.ToUser(ctx, doer, nil),
   706  		}).
   707  		WithPullRequest(pr).
   708  		Notify(ctx)
   709  }
   710  
   711  func (n *actionsNotifier) NewWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page, comment string) {
   712  	ctx = withMethod(ctx, "NewWikiPage")
   713  
   714  	newNotifyInput(repo, doer, webhook_module.HookEventWiki).WithPayload(&api.WikiPayload{
   715  		Action:     api.HookWikiCreated,
   716  		Repository: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeOwner}),
   717  		Sender:     convert.ToUser(ctx, doer, nil),
   718  		Page:       page,
   719  		Comment:    comment,
   720  	}).Notify(ctx)
   721  }
   722  
   723  func (n *actionsNotifier) EditWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page, comment string) {
   724  	ctx = withMethod(ctx, "EditWikiPage")
   725  
   726  	newNotifyInput(repo, doer, webhook_module.HookEventWiki).WithPayload(&api.WikiPayload{
   727  		Action:     api.HookWikiEdited,
   728  		Repository: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeOwner}),
   729  		Sender:     convert.ToUser(ctx, doer, nil),
   730  		Page:       page,
   731  		Comment:    comment,
   732  	}).Notify(ctx)
   733  }
   734  
   735  func (n *actionsNotifier) DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, page string) {
   736  	ctx = withMethod(ctx, "DeleteWikiPage")
   737  
   738  	newNotifyInput(repo, doer, webhook_module.HookEventWiki).WithPayload(&api.WikiPayload{
   739  		Action:     api.HookWikiDeleted,
   740  		Repository: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeOwner}),
   741  		Sender:     convert.ToUser(ctx, doer, nil),
   742  		Page:       page,
   743  	}).Notify(ctx)
   744  }
   745  
   746  // MigrateRepository is used to detect workflows after a repository has been migrated
   747  func (n *actionsNotifier) MigrateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) {
   748  	ctx = withMethod(ctx, "MigrateRepository")
   749  
   750  	newNotifyInput(repo, doer, webhook_module.HookEventRepository).WithPayload(&api.RepositoryPayload{
   751  		Action:       api.HookRepoCreated,
   752  		Repository:   convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeOwner}),
   753  		Organization: convert.ToUser(ctx, u, nil),
   754  		Sender:       convert.ToUser(ctx, doer, nil),
   755  	}).Notify(ctx)
   756  }