branch.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "sort"
  6. "go-common/app/admin/ep/saga/model"
  7. "go-common/app/admin/ep/saga/service/utils"
  8. "go-common/library/log"
  9. "github.com/xanzy/go-gitlab"
  10. )
  11. // QueryProjectBranchList query project commit info according to project id.
  12. func (s *Service) QueryProjectBranchList(c context.Context, req *model.ProjectDataReq) (resp []*gitlab.Branch, err error) {
  13. var (
  14. branch []*gitlab.Branch
  15. response *gitlab.Response
  16. branchItem []*gitlab.Branch
  17. )
  18. if branch, response, err = s.gitlab.ListProjectBranch(req.ProjectID, 1); err != nil {
  19. return
  20. }
  21. page := 2
  22. for page <= response.TotalPages {
  23. if branchItem, _, err = s.gitlab.ListProjectBranch(req.ProjectID, page); err != nil {
  24. return
  25. }
  26. branch = append(branch, branchItem...)
  27. page++
  28. }
  29. resp = branch
  30. return
  31. }
  32. // QueryBranchDiffWith ...
  33. func (s *Service) QueryBranchDiffWith(c context.Context, req *model.BranchDiffWithRequest) (resp []*model.BranchDiffWithResponse, err error) {
  34. var (
  35. branchDiff []*model.BranchDiffWithResponse
  36. )
  37. // 默认主分支为master
  38. if req.Master == "" {
  39. req.Master = "master"
  40. }
  41. if branchDiff, err = s.AllBranchDiffWithMaster(c, req.ProjectID, req.Master); err != nil {
  42. return
  43. }
  44. if req.Branch != "" {
  45. for _, branch := range branchDiff {
  46. if branch.Branch == req.Branch {
  47. resp = append(resp, branch)
  48. return
  49. }
  50. }
  51. }
  52. switch req.SortBy {
  53. case "update":
  54. sort.Slice(branchDiff, func(i, j int) bool {
  55. if branchDiff[i].LatestUpdateTime.After(*branchDiff[j].LatestUpdateTime) {
  56. return false
  57. }
  58. if branchDiff[i].LatestUpdateTime.Before(*branchDiff[j].LatestUpdateTime) {
  59. return true
  60. }
  61. return true
  62. })
  63. case "sync":
  64. sort.Slice(branchDiff, func(i, j int) bool {
  65. if branchDiff[i].LatestSyncTime.After(*branchDiff[j].LatestSyncTime) {
  66. return false
  67. }
  68. if branchDiff[i].LatestSyncTime.Before(*branchDiff[j].LatestSyncTime) {
  69. return true
  70. }
  71. return true
  72. })
  73. case "ahead":
  74. sort.Slice(branchDiff, func(i, j int) bool {
  75. if branchDiff[i].Ahead > branchDiff[j].Ahead {
  76. return true
  77. }
  78. if branchDiff[i].Ahead < branchDiff[j].Ahead {
  79. return false
  80. }
  81. return true
  82. })
  83. case "behind":
  84. sort.Slice(branchDiff, func(i, j int) bool {
  85. if branchDiff[i].Behind > branchDiff[j].Behind {
  86. return true
  87. }
  88. if branchDiff[i].Behind < branchDiff[j].Behind {
  89. return false
  90. }
  91. return true
  92. })
  93. }
  94. resp = branchDiff[:100]
  95. return
  96. }
  97. // AllBranchDiffWithMaster ...
  98. func (s *Service) AllBranchDiffWithMaster(c context.Context, project int, master string) (branchDiff []*model.BranchDiffWithResponse, err error) {
  99. var (
  100. branches map[string]*model.CommitTreeNode
  101. tree []*model.CommitTreeNode
  102. cacheKey string
  103. )
  104. log.Info("sync Branch diff start => %s", cacheKey)
  105. if branches, err = s.AllProjectBranchInfo(c, project); err != nil {
  106. return
  107. }
  108. if tree, err = s.BuildCommitTree(c, project); err != nil {
  109. return
  110. }
  111. for k, v := range branches {
  112. var (
  113. base *gitlab.Commit
  114. ahead int
  115. behind int
  116. )
  117. if base, _, err = s.gitlab.MergeBase(c, project, []string{k, master}); err != nil {
  118. return
  119. }
  120. // 计算领先commit
  121. ahead = s.ComputeCommitNum(v, base, &tree)
  122. // 计算落后commit
  123. behind = s.ComputeCommitNum(branches[master], base, &tree)
  124. log.Info("%s: ahead: %d behind:%d\n", k, ahead, behind)
  125. branchDiff = append(branchDiff, &model.BranchDiffWithResponse{Branch: k, Behind: behind, Ahead: ahead, LatestUpdateTime: v.CreatedAt, LatestSyncTime: base.CreatedAt})
  126. }
  127. log.Info("Redis: Save Branch Diff Info Successfully!")
  128. return
  129. }
  130. // ComputeCommitNum ...
  131. func (s *Service) ComputeCommitNum(head *model.CommitTreeNode, base *gitlab.Commit, tree *[]*model.CommitTreeNode) (num int) {
  132. var (
  133. visitedQueue []string
  134. commitTree = *tree
  135. i int
  136. )
  137. if head.CommitID == base.ID {
  138. return
  139. }
  140. visitedQueue = append(visitedQueue, head.CommitID)
  141. for i = 0; ; i++ {
  142. pointer := visitedQueue[i]
  143. if pointer == base.ID {
  144. break
  145. }
  146. for _, node := range commitTree {
  147. if node.CommitID == pointer {
  148. if utils.InSlice(base.ID, node.Parents) {
  149. visitedQueue = append(visitedQueue, base.ID)
  150. break
  151. }
  152. for _, p := range node.Parents {
  153. if !utils.InSlice(p, visitedQueue) {
  154. visitedQueue = append(visitedQueue, p)
  155. }
  156. }
  157. break
  158. }
  159. }
  160. if i > 999 {
  161. break
  162. }
  163. if i == len(visitedQueue)-1 {
  164. break
  165. }
  166. }
  167. num = i
  168. return
  169. }
  170. // AllMergeBase ...
  171. func (s *Service) AllMergeBase(c context.Context, project int, branches []string, master string) (mergeBase map[string]*gitlab.Commit, err error) {
  172. var base *gitlab.Commit
  173. mergeBase = make(map[string]*gitlab.Commit)
  174. for _, branch := range branches {
  175. if base, _, err = s.gitlab.MergeBase(c, project, []string{master, branch}); err != nil {
  176. return
  177. }
  178. mergeBase[branch] = base
  179. }
  180. return
  181. }
  182. //AllProjectBranchInfo 获取所有分支信息
  183. func (s *Service) AllProjectBranchInfo(c context.Context, projectID int) (branches map[string]*model.CommitTreeNode, err error) {
  184. var projectBranches []*model.StatisticsBranches
  185. branches = make(map[string]*model.CommitTreeNode)
  186. if projectBranches, err = s.dao.QueryProjectBranch(c, projectID); err != nil {
  187. return
  188. }
  189. for _, b := range projectBranches {
  190. var (
  191. commitInfo *model.StatisticsCommits
  192. commitParents []string
  193. )
  194. if commitInfo, err = s.dao.QueryCommitByID(projectID, b.CommitID); err != nil {
  195. log.Error("AllProjectBranchInfo QueryCommitByID err(%+v)", err)
  196. err = nil
  197. continue
  198. }
  199. if commitInfo.ParentIDs != "" {
  200. if err = json.Unmarshal([]byte(commitInfo.ParentIDs), &commitParents); err != nil {
  201. return
  202. }
  203. }
  204. branches[b.BranchName] = &model.CommitTreeNode{CommitID: b.CommitID, Parents: commitParents, CreatedAt: commitInfo.CreatedAt, Author: commitInfo.AuthorName}
  205. }
  206. return
  207. }
  208. // BuildCommitTree 获取到所有的Commits
  209. func (s *Service) BuildCommitTree(c context.Context, project int) (tree []*model.CommitTreeNode, err error) {
  210. var projectCommits []*model.StatisticsCommits
  211. tree = []*model.CommitTreeNode{}
  212. if projectCommits, err = s.dao.QueryProjectCommits(c, project); err != nil {
  213. return
  214. }
  215. for _, c := range projectCommits {
  216. var commitParents []string
  217. commitParentsString := c.ParentIDs
  218. if commitParentsString != "" {
  219. if err = json.Unmarshal([]byte(commitParentsString), &commitParents); err != nil {
  220. log.Error("解析commit parents报错(%+v)", err)
  221. }
  222. }
  223. tree = append(tree, &model.CommitTreeNode{CommitID: c.CommitID, Parents: commitParents, CreatedAt: c.CreatedAt, Author: c.AuthorName})
  224. }
  225. return
  226. }
  227. /*-------------------------------------- sync branch ----------------------------------------*/
  228. // SyncProjectBranch ...
  229. func (s *Service) SyncProjectBranch(c context.Context, projectID int) (result *model.SyncResult, err error) {
  230. var (
  231. branches []*gitlab.Branch
  232. resp *gitlab.Response
  233. projectInfo *model.ProjectInfo
  234. )
  235. result = &model.SyncResult{}
  236. if err = s.dao.DeleteProjectBranch(c, projectID); err != nil {
  237. return
  238. }
  239. if projectInfo, err = s.dao.ProjectInfoByID(projectID); err != nil {
  240. return
  241. }
  242. for page := 1; ; page++ {
  243. result.TotalPage++
  244. if branches, resp, err = s.gitlab.ListProjectBranch(projectID, page); err != nil {
  245. return
  246. }
  247. for _, branch := range branches {
  248. branchDB := &model.StatisticsBranches{
  249. ProjectID: projectID,
  250. ProjectName: projectInfo.Name,
  251. CommitID: branch.Commit.ID,
  252. BranchName: branch.Name,
  253. Protected: branch.Protected,
  254. Merged: branch.Merged,
  255. DevelopersCanPush: branch.DevelopersCanPush,
  256. DevelopersCanMerge: branch.DevelopersCanMerge,
  257. }
  258. if err = s.SaveDatabaseBranch(c, branchDB); err != nil {
  259. log.Error("Branch 存数据库报错(%+V)", err)
  260. err = nil
  261. errData := &model.FailData{
  262. ChildIDStr: branch.Name,
  263. }
  264. result.FailData = append(result.FailData, errData)
  265. continue
  266. }
  267. result.TotalNum++
  268. }
  269. if resp.NextPage == 0 {
  270. break
  271. }
  272. }
  273. return
  274. }
  275. // SaveDatabaseBranch ...
  276. func (s *Service) SaveDatabaseBranch(c context.Context, branchDB *model.StatisticsBranches) (err error) {
  277. var (
  278. total int
  279. branch *model.StatisticsBranches
  280. )
  281. if total, err = s.dao.HasBranch(c, branchDB.ProjectID, branchDB.BranchName); err != nil {
  282. log.Error("SaveDatabaseBranch HasBranch(%+v)", err)
  283. return
  284. }
  285. // found only one, so update
  286. if total == 1 {
  287. if branch, err = s.dao.QueryFirstBranch(c, branchDB.ProjectID, branchDB.BranchName); err != nil {
  288. log.Error("SaveDatabaseBranch QueryFirstBranch(%+v)", err)
  289. return
  290. }
  291. branchDB.ID = branch.ID
  292. if err = s.dao.UpdateBranch(c, branchDB.ProjectID, branchDB.BranchName, branchDB); err != nil {
  293. log.Error("SaveDatabaseBranch UpdateBranch(%+v)", err)
  294. return
  295. }
  296. return
  297. } else if total > 1 {
  298. // found repeated row, this situation will not exist under normal
  299. log.Warn("SaveDatabaseBranch Branch has more rows(%d)", total)
  300. return
  301. }
  302. // insert row now
  303. if err = s.dao.CreateBranch(c, branchDB); err != nil {
  304. log.Error("SaveDatabaseBranch CreateBranch(%+v)", err)
  305. return
  306. }
  307. return
  308. }
  309. /*-------------------------------------- agg branch ----------------------------------------*/
  310. // AggregateProjectBranch ...
  311. func (s *Service) AggregateProjectBranch(c context.Context, projectID int, master string) (err error) {
  312. var (
  313. branches map[string]*model.CommitTreeNode
  314. tree []*model.CommitTreeNode
  315. projectInfo *model.ProjectInfo
  316. )
  317. if err = s.dao.DeleteAggregateBranch(c, projectID); err != nil {
  318. return
  319. }
  320. if projectInfo, err = s.dao.ProjectInfoByID(projectID); err != nil {
  321. return
  322. }
  323. if branches, err = s.AllProjectBranchInfo(c, projectID); err != nil {
  324. return
  325. }
  326. if tree, err = s.BuildCommitTree(c, projectID); err != nil {
  327. return
  328. }
  329. for k, v := range branches {
  330. var (
  331. base *gitlab.Commit
  332. ahead int
  333. behind int
  334. )
  335. if base, _, err = s.gitlab.MergeBase(c, projectID, []string{k, master}); err != nil {
  336. return
  337. }
  338. // 计算领先commit
  339. ahead = s.ComputeCommitNum(v, base, &tree)
  340. // 计算落后commit
  341. behind = s.ComputeCommitNum(branches[master], base, &tree)
  342. branch := &model.AggregateBranches{
  343. ProjectID: projectID,
  344. ProjectName: projectInfo.Name,
  345. BranchName: k,
  346. BranchUserName: v.Author,
  347. BranchMaster: master,
  348. Behind: behind,
  349. Ahead: ahead,
  350. LatestUpdateTime: v.CreatedAt,
  351. LatestSyncTime: base.CreatedAt,
  352. }
  353. if err = s.SaveAggregateBranchDatabase(c, branch); err != nil {
  354. log.Error("AggregateBranch 存数据库报错(%+V)", err)
  355. continue
  356. }
  357. }
  358. return
  359. }
  360. // SaveAggregateBranchDatabase ...
  361. func (s *Service) SaveAggregateBranchDatabase(c context.Context, branchDB *model.AggregateBranches) (err error) {
  362. var (
  363. total int
  364. aggregateBranch *model.AggregateBranches
  365. )
  366. if total, err = s.dao.HasAggregateBranch(c, branchDB.ProjectID, branchDB.BranchName); err != nil {
  367. log.Error("SaveAggregateBranchDatabase HasAggregateBranch(%+v)", err)
  368. return
  369. }
  370. // found only one, so update
  371. if total == 1 {
  372. if aggregateBranch, err = s.dao.QueryFirstAggregateBranch(c, branchDB.ProjectID, branchDB.BranchName); err != nil {
  373. log.Error("SaveDatabaseBranch QueryFirstAggregateBranch(%+v)", err)
  374. return
  375. }
  376. branchDB.ID = aggregateBranch.ID
  377. if err = s.dao.UpdateAggregateBranch(c, branchDB.ProjectID, branchDB.BranchName, branchDB); err != nil {
  378. log.Error("SaveAggregateBranchDatabase UpdateAggregateBranch(%+v)", err)
  379. return
  380. }
  381. return
  382. } else if total > 1 {
  383. // found repeated row, this situation will not exist under normal
  384. log.Warn("SaveAggregateBranchDatabase AggregateBranch has more rows(%d)", total)
  385. return
  386. }
  387. // insert row now
  388. if err = s.dao.CreateAggregateBranch(c, branchDB); err != nil {
  389. log.Error("SaveAggregateBranchDatabase CreateAggregateBranch(%+v)", err)
  390. return
  391. }
  392. return
  393. }