You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

notifier.go 33KB

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