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