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.

пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 7 година
пре 10 година
пре 7 година
пре 10 година
пре 7 година
пре 7 година
пре 7 година
пре 10 година
пре 7 година
пре 7 година
пре 7 година
пре 7 година
пре 10 година
пре 8 година
пре 7 година
пре 10 година
пре 7 година
пре 10 година
пре 7 година
пре 7 година
пре 10 година
пре 7 година
пре 7 година
пре 10 година
пре 7 година
пре 7 година
пре 8 година
пре 7 година
пре 7 година
пре 8 година
пре 7 година
пре 10 година
пре 7 година
пре 10 година
пре 10 година
пре 7 година
пре 10 година
пре 10 година
пре 7 година
пре 10 година
пре 7 година
пре 10 година
пре 7 година
пре 8 година
пре 8 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 8 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 8 година
пре 8 година
пре 7 година
пре 8 година
пре 7 година
пре 7 година
пре 7 година
пре 7 година
пре 7 година
пре 8 година
пре 7 година
пре 8 година
пре 8 година
пре 8 година
пре 8 година
пре 10 година
пре 8 година
пре 8 година
пре 10 година
пре 8 година
пре 8 година
пре 8 година
пре 10 година
пре 8 година
пре 9 година
пре 10 година
пре 10 година
пре 10 година
пре 8 година
пре 8 година
пре 10 година
пре 8 година
пре 8 година
пре 8 година
пре 8 година
пре 8 година
пре 10 година
пре 10 година
пре 10 година
пре 8 година
пре 8 година
пре 10 година
пре 9 година
пре 9 година
пре 10 година
пре 6 година
пре 10 година
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "path"
  9. "regexp"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "unicode"
  14. "code.gitea.io/git"
  15. "code.gitea.io/gitea/modules/base"
  16. "code.gitea.io/gitea/modules/log"
  17. "code.gitea.io/gitea/modules/setting"
  18. "code.gitea.io/gitea/modules/util"
  19. api "code.gitea.io/sdk/gitea"
  20. "github.com/Unknwon/com"
  21. "github.com/go-xorm/builder"
  22. )
  23. // ActionType represents the type of an action.
  24. type ActionType int
  25. // Possible action types.
  26. const (
  27. ActionCreateRepo ActionType = iota + 1 // 1
  28. ActionRenameRepo // 2
  29. ActionStarRepo // 3
  30. ActionWatchRepo // 4
  31. ActionCommitRepo // 5
  32. ActionCreateIssue // 6
  33. ActionCreatePullRequest // 7
  34. ActionTransferRepo // 8
  35. ActionPushTag // 9
  36. ActionCommentIssue // 10
  37. ActionMergePullRequest // 11
  38. ActionCloseIssue // 12
  39. ActionReopenIssue // 13
  40. ActionClosePullRequest // 14
  41. ActionReopenPullRequest // 15
  42. ActionDeleteTag // 16
  43. ActionDeleteBranch // 17
  44. ActionMirrorSyncPush // 18
  45. ActionMirrorSyncCreate // 19
  46. ActionMirrorSyncDelete // 20
  47. )
  48. var (
  49. // Same as Github. See
  50. // https://help.github.com/articles/closing-issues-via-commit-messages
  51. issueCloseKeywords = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
  52. issueReopenKeywords = []string{"reopen", "reopens", "reopened"}
  53. issueCloseKeywordsPat, issueReopenKeywordsPat *regexp.Regexp
  54. issueReferenceKeywordsPat *regexp.Regexp
  55. )
  56. const issueRefRegexpStr = `(?:\S+/\S=)?#\d+`
  57. func assembleKeywordsPattern(words []string) string {
  58. return fmt.Sprintf(`(?i)(?:%s) %s`, strings.Join(words, "|"), issueRefRegexpStr)
  59. }
  60. func init() {
  61. issueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueCloseKeywords))
  62. issueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueReopenKeywords))
  63. issueReferenceKeywordsPat = regexp.MustCompile(issueRefRegexpStr)
  64. }
  65. // Action represents user operation type and other information to
  66. // repository. It implemented interface base.Actioner so that can be
  67. // used in template render.
  68. type Action struct {
  69. ID int64 `xorm:"pk autoincr"`
  70. UserID int64 `xorm:"INDEX"` // Receiver user id.
  71. OpType ActionType
  72. ActUserID int64 `xorm:"INDEX"` // Action user id.
  73. ActUser *User `xorm:"-"`
  74. RepoID int64 `xorm:"INDEX"`
  75. Repo *Repository `xorm:"-"`
  76. CommentID int64 `xorm:"INDEX"`
  77. Comment *Comment `xorm:"-"`
  78. IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"`
  79. RefName string
  80. IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"`
  81. Content string `xorm:"TEXT"`
  82. CreatedUnix util.TimeStamp `xorm:"INDEX created"`
  83. }
  84. // GetOpType gets the ActionType of this action.
  85. func (a *Action) GetOpType() ActionType {
  86. return a.OpType
  87. }
  88. func (a *Action) loadActUser() {
  89. if a.ActUser != nil {
  90. return
  91. }
  92. var err error
  93. a.ActUser, err = GetUserByID(a.ActUserID)
  94. if err == nil {
  95. return
  96. } else if IsErrUserNotExist(err) {
  97. a.ActUser = NewGhostUser()
  98. } else {
  99. log.Error(4, "GetUserByID(%d): %v", a.ActUserID, err)
  100. }
  101. }
  102. func (a *Action) loadRepo() {
  103. if a.Repo != nil {
  104. return
  105. }
  106. var err error
  107. a.Repo, err = GetRepositoryByID(a.RepoID)
  108. if err != nil {
  109. log.Error(4, "GetRepositoryByID(%d): %v", a.RepoID, err)
  110. }
  111. }
  112. // GetActFullName gets the action's user full name.
  113. func (a *Action) GetActFullName() string {
  114. a.loadActUser()
  115. return a.ActUser.FullName
  116. }
  117. // GetActUserName gets the action's user name.
  118. func (a *Action) GetActUserName() string {
  119. a.loadActUser()
  120. return a.ActUser.Name
  121. }
  122. // ShortActUserName gets the action's user name trimmed to max 20
  123. // chars.
  124. func (a *Action) ShortActUserName() string {
  125. return base.EllipsisString(a.GetActUserName(), 20)
  126. }
  127. // GetActAvatar the action's user's avatar link
  128. func (a *Action) GetActAvatar() string {
  129. a.loadActUser()
  130. return a.ActUser.RelAvatarLink()
  131. }
  132. // GetRepoUserName returns the name of the action repository owner.
  133. func (a *Action) GetRepoUserName() string {
  134. a.loadRepo()
  135. return a.Repo.MustOwner().Name
  136. }
  137. // ShortRepoUserName returns the name of the action repository owner
  138. // trimmed to max 20 chars.
  139. func (a *Action) ShortRepoUserName() string {
  140. return base.EllipsisString(a.GetRepoUserName(), 20)
  141. }
  142. // GetRepoName returns the name of the action repository.
  143. func (a *Action) GetRepoName() string {
  144. a.loadRepo()
  145. return a.Repo.Name
  146. }
  147. // ShortRepoName returns the name of the action repository
  148. // trimmed to max 33 chars.
  149. func (a *Action) ShortRepoName() string {
  150. return base.EllipsisString(a.GetRepoName(), 33)
  151. }
  152. // GetRepoPath returns the virtual path to the action repository.
  153. func (a *Action) GetRepoPath() string {
  154. return path.Join(a.GetRepoUserName(), a.GetRepoName())
  155. }
  156. // ShortRepoPath returns the virtual path to the action repository
  157. // trimmed to max 20 + 1 + 33 chars.
  158. func (a *Action) ShortRepoPath() string {
  159. return path.Join(a.ShortRepoUserName(), a.ShortRepoName())
  160. }
  161. // GetRepoLink returns relative link to action repository.
  162. func (a *Action) GetRepoLink() string {
  163. if len(setting.AppSubURL) > 0 {
  164. return path.Join(setting.AppSubURL, a.GetRepoPath())
  165. }
  166. return "/" + a.GetRepoPath()
  167. }
  168. // GetCommentLink returns link to action comment.
  169. func (a *Action) GetCommentLink() string {
  170. return a.getCommentLink(x)
  171. }
  172. func (a *Action) getCommentLink(e Engine) string {
  173. if a == nil {
  174. return "#"
  175. }
  176. if a.Comment == nil && a.CommentID != 0 {
  177. a.Comment, _ = GetCommentByID(a.CommentID)
  178. }
  179. if a.Comment != nil {
  180. return a.Comment.HTMLURL()
  181. }
  182. if len(a.GetIssueInfos()) == 0 {
  183. return "#"
  184. }
  185. //Return link to issue
  186. issueIDString := a.GetIssueInfos()[0]
  187. issueID, err := strconv.ParseInt(issueIDString, 10, 64)
  188. if err != nil {
  189. return "#"
  190. }
  191. issue, err := getIssueByID(e, issueID)
  192. if err != nil {
  193. return "#"
  194. }
  195. if err = issue.loadRepo(e); err != nil {
  196. return "#"
  197. }
  198. return issue.HTMLURL()
  199. }
  200. // GetBranch returns the action's repository branch.
  201. func (a *Action) GetBranch() string {
  202. return a.RefName
  203. }
  204. // GetContent returns the action's content.
  205. func (a *Action) GetContent() string {
  206. return a.Content
  207. }
  208. // GetCreate returns the action creation time.
  209. func (a *Action) GetCreate() time.Time {
  210. return a.CreatedUnix.AsTime()
  211. }
  212. // GetIssueInfos returns a list of issues associated with
  213. // the action.
  214. func (a *Action) GetIssueInfos() []string {
  215. return strings.SplitN(a.Content, "|", 2)
  216. }
  217. // GetIssueTitle returns the title of first issue associated
  218. // with the action.
  219. func (a *Action) GetIssueTitle() string {
  220. index := com.StrTo(a.GetIssueInfos()[0]).MustInt64()
  221. issue, err := GetIssueByIndex(a.RepoID, index)
  222. if err != nil {
  223. log.Error(4, "GetIssueByIndex: %v", err)
  224. return "500 when get issue"
  225. }
  226. return issue.Title
  227. }
  228. // GetIssueContent returns the content of first issue associated with
  229. // this action.
  230. func (a *Action) GetIssueContent() string {
  231. index := com.StrTo(a.GetIssueInfos()[0]).MustInt64()
  232. issue, err := GetIssueByIndex(a.RepoID, index)
  233. if err != nil {
  234. log.Error(4, "GetIssueByIndex: %v", err)
  235. return "500 when get issue"
  236. }
  237. return issue.Content
  238. }
  239. func newRepoAction(e Engine, u *User, repo *Repository) (err error) {
  240. if err = notifyWatchers(e, &Action{
  241. ActUserID: u.ID,
  242. ActUser: u,
  243. OpType: ActionCreateRepo,
  244. RepoID: repo.ID,
  245. Repo: repo,
  246. IsPrivate: repo.IsPrivate,
  247. }); err != nil {
  248. return fmt.Errorf("notify watchers '%d/%d': %v", u.ID, repo.ID, err)
  249. }
  250. log.Trace("action.newRepoAction: %s/%s", u.Name, repo.Name)
  251. return err
  252. }
  253. // NewRepoAction adds new action for creating repository.
  254. func NewRepoAction(u *User, repo *Repository) (err error) {
  255. return newRepoAction(x, u, repo)
  256. }
  257. func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Repository) (err error) {
  258. if err = notifyWatchers(e, &Action{
  259. ActUserID: actUser.ID,
  260. ActUser: actUser,
  261. OpType: ActionRenameRepo,
  262. RepoID: repo.ID,
  263. Repo: repo,
  264. IsPrivate: repo.IsPrivate,
  265. Content: oldRepoName,
  266. }); err != nil {
  267. return fmt.Errorf("notify watchers: %v", err)
  268. }
  269. log.Trace("action.renameRepoAction: %s/%s", actUser.Name, repo.Name)
  270. return nil
  271. }
  272. // RenameRepoAction adds new action for renaming a repository.
  273. func RenameRepoAction(actUser *User, oldRepoName string, repo *Repository) error {
  274. return renameRepoAction(x, actUser, oldRepoName, repo)
  275. }
  276. func issueIndexTrimRight(c rune) bool {
  277. return !unicode.IsDigit(c)
  278. }
  279. // PushCommit represents a commit in a push operation.
  280. type PushCommit struct {
  281. Sha1 string
  282. Message string
  283. AuthorEmail string
  284. AuthorName string
  285. CommitterEmail string
  286. CommitterName string
  287. Timestamp time.Time
  288. }
  289. // PushCommits represents list of commits in a push operation.
  290. type PushCommits struct {
  291. Len int
  292. Commits []*PushCommit
  293. CompareURL string
  294. avatars map[string]string
  295. emailUsers map[string]*User
  296. }
  297. // NewPushCommits creates a new PushCommits object.
  298. func NewPushCommits() *PushCommits {
  299. return &PushCommits{
  300. avatars: make(map[string]string),
  301. emailUsers: make(map[string]*User),
  302. }
  303. }
  304. // ToAPIPayloadCommits converts a PushCommits object to
  305. // api.PayloadCommit format.
  306. func (pc *PushCommits) ToAPIPayloadCommits(repoLink string) []*api.PayloadCommit {
  307. commits := make([]*api.PayloadCommit, len(pc.Commits))
  308. if pc.emailUsers == nil {
  309. pc.emailUsers = make(map[string]*User)
  310. }
  311. var err error
  312. for i, commit := range pc.Commits {
  313. authorUsername := ""
  314. author, ok := pc.emailUsers[commit.AuthorEmail]
  315. if !ok {
  316. author, err = GetUserByEmail(commit.AuthorEmail)
  317. if err == nil {
  318. authorUsername = author.Name
  319. pc.emailUsers[commit.AuthorEmail] = author
  320. }
  321. } else {
  322. authorUsername = author.Name
  323. }
  324. committerUsername := ""
  325. committer, ok := pc.emailUsers[commit.CommitterEmail]
  326. if !ok {
  327. committer, err = GetUserByEmail(commit.CommitterEmail)
  328. if err == nil {
  329. // TODO: check errors other than email not found.
  330. committerUsername = committer.Name
  331. pc.emailUsers[commit.CommitterEmail] = committer
  332. }
  333. } else {
  334. committerUsername = committer.Name
  335. }
  336. commits[i] = &api.PayloadCommit{
  337. ID: commit.Sha1,
  338. Message: commit.Message,
  339. URL: fmt.Sprintf("%s/commit/%s", repoLink, commit.Sha1),
  340. Author: &api.PayloadUser{
  341. Name: commit.AuthorName,
  342. Email: commit.AuthorEmail,
  343. UserName: authorUsername,
  344. },
  345. Committer: &api.PayloadUser{
  346. Name: commit.CommitterName,
  347. Email: commit.CommitterEmail,
  348. UserName: committerUsername,
  349. },
  350. Timestamp: commit.Timestamp,
  351. }
  352. }
  353. return commits
  354. }
  355. // AvatarLink tries to match user in database with e-mail
  356. // in order to show custom avatar, and falls back to general avatar link.
  357. func (pc *PushCommits) AvatarLink(email string) string {
  358. avatar, ok := pc.avatars[email]
  359. if ok {
  360. return avatar
  361. }
  362. u, ok := pc.emailUsers[email]
  363. if !ok {
  364. var err error
  365. u, err = GetUserByEmail(email)
  366. if err != nil {
  367. pc.avatars[email] = base.AvatarLink(email)
  368. if !IsErrUserNotExist(err) {
  369. log.Error(4, "GetUserByEmail: %v", err)
  370. return ""
  371. }
  372. } else {
  373. pc.emailUsers[email] = u
  374. }
  375. }
  376. if u != nil {
  377. pc.avatars[email] = u.RelAvatarLink()
  378. }
  379. return pc.avatars[email]
  380. }
  381. // getIssueFromRef returns the issue referenced by a ref. Returns a nil *Issue
  382. // if the provided ref is misformatted or references a non-existent issue.
  383. func getIssueFromRef(repo *Repository, ref string) (*Issue, error) {
  384. ref = ref[strings.IndexByte(ref, ' ')+1:]
  385. ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
  386. var refRepo *Repository
  387. poundIndex := strings.IndexByte(ref, '#')
  388. if poundIndex < 0 {
  389. return nil, nil
  390. } else if poundIndex == 0 {
  391. refRepo = repo
  392. } else {
  393. slashIndex := strings.IndexByte(ref, '/')
  394. if slashIndex < 0 || slashIndex >= poundIndex {
  395. return nil, nil
  396. }
  397. ownerName := ref[:slashIndex]
  398. repoName := ref[slashIndex+1 : poundIndex]
  399. var err error
  400. refRepo, err = GetRepositoryByOwnerAndName(ownerName, repoName)
  401. if err != nil {
  402. if IsErrRepoNotExist(err) {
  403. return nil, nil
  404. }
  405. return nil, err
  406. }
  407. }
  408. issueIndex, err := strconv.ParseInt(ref[poundIndex+1:], 10, 64)
  409. if err != nil {
  410. return nil, nil
  411. }
  412. issue, err := GetIssueByIndex(refRepo.ID, int64(issueIndex))
  413. if err != nil {
  414. if IsErrIssueNotExist(err) {
  415. return nil, nil
  416. }
  417. return nil, err
  418. }
  419. return issue, nil
  420. }
  421. func changeIssueStatus(repo *Repository, doer *User, ref string, refMarked map[int64]bool, status bool) error {
  422. issue, err := getIssueFromRef(repo, ref)
  423. if err != nil {
  424. return err
  425. }
  426. if issue == nil || refMarked[issue.ID] {
  427. return nil
  428. }
  429. refMarked[issue.ID] = true
  430. if issue.RepoID != repo.ID || issue.IsClosed == status {
  431. return nil
  432. }
  433. issue.Repo = repo
  434. if err = issue.ChangeStatus(doer, status); err != nil {
  435. // Don't return an error when dependencies are open as this would let the push fail
  436. if IsErrDependenciesLeft(err) {
  437. return nil
  438. }
  439. return err
  440. }
  441. return nil
  442. }
  443. // UpdateIssuesCommit checks if issues are manipulated by commit message.
  444. func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, branchName string) error {
  445. // Commits are appended in the reverse order.
  446. for i := len(commits) - 1; i >= 0; i-- {
  447. c := commits[i]
  448. refMarked := make(map[int64]bool)
  449. for _, ref := range issueReferenceKeywordsPat.FindAllString(c.Message, -1) {
  450. issue, err := getIssueFromRef(repo, ref)
  451. if err != nil {
  452. return err
  453. }
  454. if issue == nil || refMarked[issue.ID] {
  455. continue
  456. }
  457. refMarked[issue.ID] = true
  458. message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, c.Message)
  459. if err = CreateRefComment(doer, repo, issue, message, c.Sha1); err != nil {
  460. return err
  461. }
  462. }
  463. // Change issue status only if the commit has been pushed to the default branch.
  464. if repo.DefaultBranch != branchName {
  465. continue
  466. }
  467. refMarked = make(map[int64]bool)
  468. for _, ref := range issueCloseKeywordsPat.FindAllString(c.Message, -1) {
  469. if err := changeIssueStatus(repo, doer, ref, refMarked, true); err != nil {
  470. return err
  471. }
  472. }
  473. // It is conflict to have close and reopen at same time, so refsMarked doesn't need to reinit here.
  474. for _, ref := range issueReopenKeywordsPat.FindAllString(c.Message, -1) {
  475. if err := changeIssueStatus(repo, doer, ref, refMarked, false); err != nil {
  476. return err
  477. }
  478. }
  479. }
  480. return nil
  481. }
  482. // CommitRepoActionOptions represent options of a new commit action.
  483. type CommitRepoActionOptions struct {
  484. PusherName string
  485. RepoOwnerID int64
  486. RepoName string
  487. RefFullName string
  488. OldCommitID string
  489. NewCommitID string
  490. Commits *PushCommits
  491. }
  492. // CommitRepoAction adds new commit action to the repository, and prepare
  493. // corresponding webhooks.
  494. func CommitRepoAction(opts CommitRepoActionOptions) error {
  495. pusher, err := GetUserByName(opts.PusherName)
  496. if err != nil {
  497. return fmt.Errorf("GetUserByName [%s]: %v", opts.PusherName, err)
  498. }
  499. repo, err := GetRepositoryByName(opts.RepoOwnerID, opts.RepoName)
  500. if err != nil {
  501. return fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err)
  502. }
  503. refName := git.RefEndName(opts.RefFullName)
  504. // Change default branch and empty status only if pushed ref is non-empty branch.
  505. if repo.IsEmpty && opts.NewCommitID != git.EmptySHA && strings.HasPrefix(opts.RefFullName, git.BranchPrefix) {
  506. repo.DefaultBranch = refName
  507. repo.IsEmpty = false
  508. }
  509. // Change repository empty status and update last updated time.
  510. if err = UpdateRepository(repo, false); err != nil {
  511. return fmt.Errorf("UpdateRepository: %v", err)
  512. }
  513. isNewBranch := false
  514. opType := ActionCommitRepo
  515. // Check it's tag push or branch.
  516. if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
  517. opType = ActionPushTag
  518. if opts.NewCommitID == git.EmptySHA {
  519. opType = ActionDeleteTag
  520. }
  521. opts.Commits = &PushCommits{}
  522. } else if opts.NewCommitID == git.EmptySHA {
  523. opType = ActionDeleteBranch
  524. opts.Commits = &PushCommits{}
  525. } else {
  526. // if not the first commit, set the compare URL.
  527. if opts.OldCommitID == git.EmptySHA {
  528. isNewBranch = true
  529. } else {
  530. opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
  531. }
  532. if err = UpdateIssuesCommit(pusher, repo, opts.Commits.Commits, refName); err != nil {
  533. log.Error(4, "updateIssuesCommit: %v", err)
  534. }
  535. }
  536. if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum {
  537. opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
  538. }
  539. data, err := json.Marshal(opts.Commits)
  540. if err != nil {
  541. return fmt.Errorf("Marshal: %v", err)
  542. }
  543. if err = NotifyWatchers(&Action{
  544. ActUserID: pusher.ID,
  545. ActUser: pusher,
  546. OpType: opType,
  547. Content: string(data),
  548. RepoID: repo.ID,
  549. Repo: repo,
  550. RefName: refName,
  551. IsPrivate: repo.IsPrivate,
  552. }); err != nil {
  553. return fmt.Errorf("NotifyWatchers: %v", err)
  554. }
  555. defer func() {
  556. go HookQueue.Add(repo.ID)
  557. }()
  558. apiPusher := pusher.APIFormat()
  559. apiRepo := repo.APIFormat(AccessModeNone)
  560. var shaSum string
  561. var isHookEventPush = false
  562. switch opType {
  563. case ActionCommitRepo: // Push
  564. isHookEventPush = true
  565. if isNewBranch {
  566. gitRepo, err := git.OpenRepository(repo.RepoPath())
  567. if err != nil {
  568. log.Error(4, "OpenRepository[%s]: %v", repo.RepoPath(), err)
  569. }
  570. shaSum, err = gitRepo.GetBranchCommitID(refName)
  571. if err != nil {
  572. log.Error(4, "GetBranchCommitID[%s]: %v", opts.RefFullName, err)
  573. }
  574. if err = PrepareWebhooks(repo, HookEventCreate, &api.CreatePayload{
  575. Ref: refName,
  576. Sha: shaSum,
  577. RefType: "branch",
  578. Repo: apiRepo,
  579. Sender: apiPusher,
  580. }); err != nil {
  581. return fmt.Errorf("PrepareWebhooks: %v", err)
  582. }
  583. }
  584. case ActionDeleteBranch: // Delete Branch
  585. isHookEventPush = true
  586. if err = PrepareWebhooks(repo, HookEventDelete, &api.DeletePayload{
  587. Ref: refName,
  588. RefType: "branch",
  589. PusherType: api.PusherTypeUser,
  590. Repo: apiRepo,
  591. Sender: apiPusher,
  592. }); err != nil {
  593. return fmt.Errorf("PrepareWebhooks.(delete branch): %v", err)
  594. }
  595. case ActionPushTag: // Create
  596. isHookEventPush = true
  597. gitRepo, err := git.OpenRepository(repo.RepoPath())
  598. if err != nil {
  599. log.Error(4, "OpenRepository[%s]: %v", repo.RepoPath(), err)
  600. }
  601. shaSum, err = gitRepo.GetTagCommitID(refName)
  602. if err != nil {
  603. log.Error(4, "GetTagCommitID[%s]: %v", opts.RefFullName, err)
  604. }
  605. if err = PrepareWebhooks(repo, HookEventCreate, &api.CreatePayload{
  606. Ref: refName,
  607. Sha: shaSum,
  608. RefType: "tag",
  609. Repo: apiRepo,
  610. Sender: apiPusher,
  611. }); err != nil {
  612. return fmt.Errorf("PrepareWebhooks: %v", err)
  613. }
  614. case ActionDeleteTag: // Delete Tag
  615. isHookEventPush = true
  616. if err = PrepareWebhooks(repo, HookEventDelete, &api.DeletePayload{
  617. Ref: refName,
  618. RefType: "tag",
  619. PusherType: api.PusherTypeUser,
  620. Repo: apiRepo,
  621. Sender: apiPusher,
  622. }); err != nil {
  623. return fmt.Errorf("PrepareWebhooks.(delete tag): %v", err)
  624. }
  625. }
  626. if isHookEventPush {
  627. if err = PrepareWebhooks(repo, HookEventPush, &api.PushPayload{
  628. Ref: opts.RefFullName,
  629. Before: opts.OldCommitID,
  630. After: opts.NewCommitID,
  631. CompareURL: setting.AppURL + opts.Commits.CompareURL,
  632. Commits: opts.Commits.ToAPIPayloadCommits(repo.HTMLURL()),
  633. Repo: apiRepo,
  634. Pusher: apiPusher,
  635. Sender: apiPusher,
  636. }); err != nil {
  637. return fmt.Errorf("PrepareWebhooks: %v", err)
  638. }
  639. }
  640. return nil
  641. }
  642. func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err error) {
  643. if err = notifyWatchers(e, &Action{
  644. ActUserID: doer.ID,
  645. ActUser: doer,
  646. OpType: ActionTransferRepo,
  647. RepoID: repo.ID,
  648. Repo: repo,
  649. IsPrivate: repo.IsPrivate,
  650. Content: path.Join(oldOwner.Name, repo.Name),
  651. }); err != nil {
  652. return fmt.Errorf("notifyWatchers: %v", err)
  653. }
  654. // Remove watch for organization.
  655. if oldOwner.IsOrganization() {
  656. if err = watchRepo(e, oldOwner.ID, repo.ID, false); err != nil {
  657. return fmt.Errorf("watchRepo [false]: %v", err)
  658. }
  659. }
  660. return nil
  661. }
  662. // TransferRepoAction adds new action for transferring repository,
  663. // the Owner field of repository is assumed to be new owner.
  664. func TransferRepoAction(doer, oldOwner *User, repo *Repository) error {
  665. return transferRepoAction(x, doer, oldOwner, repo)
  666. }
  667. func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue) error {
  668. return notifyWatchers(e, &Action{
  669. ActUserID: doer.ID,
  670. ActUser: doer,
  671. OpType: ActionMergePullRequest,
  672. Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
  673. RepoID: repo.ID,
  674. Repo: repo,
  675. IsPrivate: repo.IsPrivate,
  676. })
  677. }
  678. // MergePullRequestAction adds new action for merging pull request.
  679. func MergePullRequestAction(actUser *User, repo *Repository, pull *Issue) error {
  680. return mergePullRequestAction(x, actUser, repo, pull)
  681. }
  682. func mirrorSyncAction(e Engine, opType ActionType, repo *Repository, refName string, data []byte) error {
  683. if err := notifyWatchers(e, &Action{
  684. ActUserID: repo.OwnerID,
  685. ActUser: repo.MustOwner(),
  686. OpType: opType,
  687. RepoID: repo.ID,
  688. Repo: repo,
  689. IsPrivate: repo.IsPrivate,
  690. RefName: refName,
  691. Content: string(data),
  692. }); err != nil {
  693. return fmt.Errorf("notifyWatchers: %v", err)
  694. }
  695. return nil
  696. }
  697. // MirrorSyncPushActionOptions mirror synchronization action options.
  698. type MirrorSyncPushActionOptions struct {
  699. RefName string
  700. OldCommitID string
  701. NewCommitID string
  702. Commits *PushCommits
  703. }
  704. // MirrorSyncPushAction adds new action for mirror synchronization of pushed commits.
  705. func MirrorSyncPushAction(repo *Repository, opts MirrorSyncPushActionOptions) error {
  706. if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum {
  707. opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
  708. }
  709. apiCommits := opts.Commits.ToAPIPayloadCommits(repo.HTMLURL())
  710. opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
  711. apiPusher := repo.MustOwner().APIFormat()
  712. if err := PrepareWebhooks(repo, HookEventPush, &api.PushPayload{
  713. Ref: opts.RefName,
  714. Before: opts.OldCommitID,
  715. After: opts.NewCommitID,
  716. CompareURL: setting.AppURL + opts.Commits.CompareURL,
  717. Commits: apiCommits,
  718. Repo: repo.APIFormat(AccessModeOwner),
  719. Pusher: apiPusher,
  720. Sender: apiPusher,
  721. }); err != nil {
  722. return fmt.Errorf("PrepareWebhooks: %v", err)
  723. }
  724. data, err := json.Marshal(opts.Commits)
  725. if err != nil {
  726. return err
  727. }
  728. return mirrorSyncAction(x, ActionMirrorSyncPush, repo, opts.RefName, data)
  729. }
  730. // MirrorSyncCreateAction adds new action for mirror synchronization of new reference.
  731. func MirrorSyncCreateAction(repo *Repository, refName string) error {
  732. return mirrorSyncAction(x, ActionMirrorSyncCreate, repo, refName, nil)
  733. }
  734. // MirrorSyncDeleteAction adds new action for mirror synchronization of delete reference.
  735. func MirrorSyncDeleteAction(repo *Repository, refName string) error {
  736. return mirrorSyncAction(x, ActionMirrorSyncDelete, repo, refName, nil)
  737. }
  738. // GetFeedsOptions options for retrieving feeds
  739. type GetFeedsOptions struct {
  740. RequestedUser *User
  741. RequestingUserID int64
  742. IncludePrivate bool // include private actions
  743. OnlyPerformedBy bool // only actions performed by requested user
  744. IncludeDeleted bool // include deleted actions
  745. }
  746. // GetFeeds returns actions according to the provided options
  747. func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
  748. cond := builder.NewCond()
  749. var repoIDs []int64
  750. if opts.RequestedUser.IsOrganization() {
  751. env, err := opts.RequestedUser.AccessibleReposEnv(opts.RequestingUserID)
  752. if err != nil {
  753. return nil, fmt.Errorf("AccessibleReposEnv: %v", err)
  754. }
  755. if repoIDs, err = env.RepoIDs(1, opts.RequestedUser.NumRepos); err != nil {
  756. return nil, fmt.Errorf("GetUserRepositories: %v", err)
  757. }
  758. cond = cond.And(builder.In("repo_id", repoIDs))
  759. }
  760. cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
  761. if opts.OnlyPerformedBy {
  762. cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
  763. }
  764. if !opts.IncludePrivate {
  765. cond = cond.And(builder.Eq{"is_private": false})
  766. }
  767. if !opts.IncludeDeleted {
  768. cond = cond.And(builder.Eq{"is_deleted": false})
  769. }
  770. actions := make([]*Action, 0, 20)
  771. if err := x.Limit(20).Desc("id").Where(cond).Find(&actions); err != nil {
  772. return nil, fmt.Errorf("Find: %v", err)
  773. }
  774. if err := ActionList(actions).LoadAttributes(); err != nil {
  775. return nil, fmt.Errorf("LoadAttributes: %v", err)
  776. }
  777. return actions, nil
  778. }