go-common/app/admin/ep/saga/service/branch.go
2019-04-22 18:49:16 +08:00

446 lines
12 KiB
Go

package service
import (
"context"
"encoding/json"
"sort"
"go-common/app/admin/ep/saga/model"
"go-common/app/admin/ep/saga/service/utils"
"go-common/library/log"
"github.com/xanzy/go-gitlab"
)
// QueryProjectBranchList query project commit info according to project id.
func (s *Service) QueryProjectBranchList(c context.Context, req *model.ProjectDataReq) (resp []*gitlab.Branch, err error) {
var (
branch []*gitlab.Branch
response *gitlab.Response
branchItem []*gitlab.Branch
)
if branch, response, err = s.gitlab.ListProjectBranch(req.ProjectID, 1); err != nil {
return
}
page := 2
for page <= response.TotalPages {
if branchItem, _, err = s.gitlab.ListProjectBranch(req.ProjectID, page); err != nil {
return
}
branch = append(branch, branchItem...)
page++
}
resp = branch
return
}
// QueryBranchDiffWith ...
func (s *Service) QueryBranchDiffWith(c context.Context, req *model.BranchDiffWithRequest) (resp []*model.BranchDiffWithResponse, err error) {
var (
branchDiff []*model.BranchDiffWithResponse
)
// 默认主分支为master
if req.Master == "" {
req.Master = "master"
}
if branchDiff, err = s.AllBranchDiffWithMaster(c, req.ProjectID, req.Master); err != nil {
return
}
if req.Branch != "" {
for _, branch := range branchDiff {
if branch.Branch == req.Branch {
resp = append(resp, branch)
return
}
}
}
switch req.SortBy {
case "update":
sort.Slice(branchDiff, func(i, j int) bool {
if branchDiff[i].LatestUpdateTime.After(*branchDiff[j].LatestUpdateTime) {
return false
}
if branchDiff[i].LatestUpdateTime.Before(*branchDiff[j].LatestUpdateTime) {
return true
}
return true
})
case "sync":
sort.Slice(branchDiff, func(i, j int) bool {
if branchDiff[i].LatestSyncTime.After(*branchDiff[j].LatestSyncTime) {
return false
}
if branchDiff[i].LatestSyncTime.Before(*branchDiff[j].LatestSyncTime) {
return true
}
return true
})
case "ahead":
sort.Slice(branchDiff, func(i, j int) bool {
if branchDiff[i].Ahead > branchDiff[j].Ahead {
return true
}
if branchDiff[i].Ahead < branchDiff[j].Ahead {
return false
}
return true
})
case "behind":
sort.Slice(branchDiff, func(i, j int) bool {
if branchDiff[i].Behind > branchDiff[j].Behind {
return true
}
if branchDiff[i].Behind < branchDiff[j].Behind {
return false
}
return true
})
}
resp = branchDiff[:100]
return
}
// AllBranchDiffWithMaster ...
func (s *Service) AllBranchDiffWithMaster(c context.Context, project int, master string) (branchDiff []*model.BranchDiffWithResponse, err error) {
var (
branches map[string]*model.CommitTreeNode
tree []*model.CommitTreeNode
cacheKey string
)
log.Info("sync Branch diff start => %s", cacheKey)
if branches, err = s.AllProjectBranchInfo(c, project); err != nil {
return
}
if tree, err = s.BuildCommitTree(c, project); err != nil {
return
}
for k, v := range branches {
var (
base *gitlab.Commit
ahead int
behind int
)
if base, _, err = s.gitlab.MergeBase(c, project, []string{k, master}); err != nil {
return
}
// 计算领先commit
ahead = s.ComputeCommitNum(v, base, &tree)
// 计算落后commit
behind = s.ComputeCommitNum(branches[master], base, &tree)
log.Info("%s: ahead: %d behind:%d\n", k, ahead, behind)
branchDiff = append(branchDiff, &model.BranchDiffWithResponse{Branch: k, Behind: behind, Ahead: ahead, LatestUpdateTime: v.CreatedAt, LatestSyncTime: base.CreatedAt})
}
log.Info("Redis: Save Branch Diff Info Successfully!")
return
}
// ComputeCommitNum ...
func (s *Service) ComputeCommitNum(head *model.CommitTreeNode, base *gitlab.Commit, tree *[]*model.CommitTreeNode) (num int) {
var (
visitedQueue []string
commitTree = *tree
i int
)
if head.CommitID == base.ID {
return
}
visitedQueue = append(visitedQueue, head.CommitID)
for i = 0; ; i++ {
pointer := visitedQueue[i]
if pointer == base.ID {
break
}
for _, node := range commitTree {
if node.CommitID == pointer {
if utils.InSlice(base.ID, node.Parents) {
visitedQueue = append(visitedQueue, base.ID)
break
}
for _, p := range node.Parents {
if !utils.InSlice(p, visitedQueue) {
visitedQueue = append(visitedQueue, p)
}
}
break
}
}
if i > 999 {
break
}
if i == len(visitedQueue)-1 {
break
}
}
num = i
return
}
// AllMergeBase ...
func (s *Service) AllMergeBase(c context.Context, project int, branches []string, master string) (mergeBase map[string]*gitlab.Commit, err error) {
var base *gitlab.Commit
mergeBase = make(map[string]*gitlab.Commit)
for _, branch := range branches {
if base, _, err = s.gitlab.MergeBase(c, project, []string{master, branch}); err != nil {
return
}
mergeBase[branch] = base
}
return
}
//AllProjectBranchInfo 获取所有分支信息
func (s *Service) AllProjectBranchInfo(c context.Context, projectID int) (branches map[string]*model.CommitTreeNode, err error) {
var projectBranches []*model.StatisticsBranches
branches = make(map[string]*model.CommitTreeNode)
if projectBranches, err = s.dao.QueryProjectBranch(c, projectID); err != nil {
return
}
for _, b := range projectBranches {
var (
commitInfo *model.StatisticsCommits
commitParents []string
)
if commitInfo, err = s.dao.QueryCommitByID(projectID, b.CommitID); err != nil {
log.Error("AllProjectBranchInfo QueryCommitByID err(%+v)", err)
err = nil
continue
}
if commitInfo.ParentIDs != "" {
if err = json.Unmarshal([]byte(commitInfo.ParentIDs), &commitParents); err != nil {
return
}
}
branches[b.BranchName] = &model.CommitTreeNode{CommitID: b.CommitID, Parents: commitParents, CreatedAt: commitInfo.CreatedAt, Author: commitInfo.AuthorName}
}
return
}
// BuildCommitTree 获取到所有的Commits
func (s *Service) BuildCommitTree(c context.Context, project int) (tree []*model.CommitTreeNode, err error) {
var projectCommits []*model.StatisticsCommits
tree = []*model.CommitTreeNode{}
if projectCommits, err = s.dao.QueryProjectCommits(c, project); err != nil {
return
}
for _, c := range projectCommits {
var commitParents []string
commitParentsString := c.ParentIDs
if commitParentsString != "" {
if err = json.Unmarshal([]byte(commitParentsString), &commitParents); err != nil {
log.Error("解析commit parents报错(%+v)", err)
}
}
tree = append(tree, &model.CommitTreeNode{CommitID: c.CommitID, Parents: commitParents, CreatedAt: c.CreatedAt, Author: c.AuthorName})
}
return
}
/*-------------------------------------- sync branch ----------------------------------------*/
// SyncProjectBranch ...
func (s *Service) SyncProjectBranch(c context.Context, projectID int) (result *model.SyncResult, err error) {
var (
branches []*gitlab.Branch
resp *gitlab.Response
projectInfo *model.ProjectInfo
)
result = &model.SyncResult{}
if err = s.dao.DeleteProjectBranch(c, projectID); err != nil {
return
}
if projectInfo, err = s.dao.ProjectInfoByID(projectID); err != nil {
return
}
for page := 1; ; page++ {
result.TotalPage++
if branches, resp, err = s.gitlab.ListProjectBranch(projectID, page); err != nil {
return
}
for _, branch := range branches {
branchDB := &model.StatisticsBranches{
ProjectID: projectID,
ProjectName: projectInfo.Name,
CommitID: branch.Commit.ID,
BranchName: branch.Name,
Protected: branch.Protected,
Merged: branch.Merged,
DevelopersCanPush: branch.DevelopersCanPush,
DevelopersCanMerge: branch.DevelopersCanMerge,
}
if err = s.SaveDatabaseBranch(c, branchDB); err != nil {
log.Error("Branch 存数据库报错(%+V)", err)
err = nil
errData := &model.FailData{
ChildIDStr: branch.Name,
}
result.FailData = append(result.FailData, errData)
continue
}
result.TotalNum++
}
if resp.NextPage == 0 {
break
}
}
return
}
// SaveDatabaseBranch ...
func (s *Service) SaveDatabaseBranch(c context.Context, branchDB *model.StatisticsBranches) (err error) {
var (
total int
branch *model.StatisticsBranches
)
if total, err = s.dao.HasBranch(c, branchDB.ProjectID, branchDB.BranchName); err != nil {
log.Error("SaveDatabaseBranch HasBranch(%+v)", err)
return
}
// found only one, so update
if total == 1 {
if branch, err = s.dao.QueryFirstBranch(c, branchDB.ProjectID, branchDB.BranchName); err != nil {
log.Error("SaveDatabaseBranch QueryFirstBranch(%+v)", err)
return
}
branchDB.ID = branch.ID
if err = s.dao.UpdateBranch(c, branchDB.ProjectID, branchDB.BranchName, branchDB); err != nil {
log.Error("SaveDatabaseBranch UpdateBranch(%+v)", err)
return
}
return
} else if total > 1 {
// found repeated row, this situation will not exist under normal
log.Warn("SaveDatabaseBranch Branch has more rows(%d)", total)
return
}
// insert row now
if err = s.dao.CreateBranch(c, branchDB); err != nil {
log.Error("SaveDatabaseBranch CreateBranch(%+v)", err)
return
}
return
}
/*-------------------------------------- agg branch ----------------------------------------*/
// AggregateProjectBranch ...
func (s *Service) AggregateProjectBranch(c context.Context, projectID int, master string) (err error) {
var (
branches map[string]*model.CommitTreeNode
tree []*model.CommitTreeNode
projectInfo *model.ProjectInfo
)
if err = s.dao.DeleteAggregateBranch(c, projectID); err != nil {
return
}
if projectInfo, err = s.dao.ProjectInfoByID(projectID); err != nil {
return
}
if branches, err = s.AllProjectBranchInfo(c, projectID); err != nil {
return
}
if tree, err = s.BuildCommitTree(c, projectID); err != nil {
return
}
for k, v := range branches {
var (
base *gitlab.Commit
ahead int
behind int
)
if base, _, err = s.gitlab.MergeBase(c, projectID, []string{k, master}); err != nil {
return
}
// 计算领先commit
ahead = s.ComputeCommitNum(v, base, &tree)
// 计算落后commit
behind = s.ComputeCommitNum(branches[master], base, &tree)
branch := &model.AggregateBranches{
ProjectID: projectID,
ProjectName: projectInfo.Name,
BranchName: k,
BranchUserName: v.Author,
BranchMaster: master,
Behind: behind,
Ahead: ahead,
LatestUpdateTime: v.CreatedAt,
LatestSyncTime: base.CreatedAt,
}
if err = s.SaveAggregateBranchDatabase(c, branch); err != nil {
log.Error("AggregateBranch 存数据库报错(%+V)", err)
continue
}
}
return
}
// SaveAggregateBranchDatabase ...
func (s *Service) SaveAggregateBranchDatabase(c context.Context, branchDB *model.AggregateBranches) (err error) {
var (
total int
aggregateBranch *model.AggregateBranches
)
if total, err = s.dao.HasAggregateBranch(c, branchDB.ProjectID, branchDB.BranchName); err != nil {
log.Error("SaveAggregateBranchDatabase HasAggregateBranch(%+v)", err)
return
}
// found only one, so update
if total == 1 {
if aggregateBranch, err = s.dao.QueryFirstAggregateBranch(c, branchDB.ProjectID, branchDB.BranchName); err != nil {
log.Error("SaveDatabaseBranch QueryFirstAggregateBranch(%+v)", err)
return
}
branchDB.ID = aggregateBranch.ID
if err = s.dao.UpdateAggregateBranch(c, branchDB.ProjectID, branchDB.BranchName, branchDB); err != nil {
log.Error("SaveAggregateBranchDatabase UpdateAggregateBranch(%+v)", err)
return
}
return
} else if total > 1 {
// found repeated row, this situation will not exist under normal
log.Warn("SaveAggregateBranchDatabase AggregateBranch has more rows(%d)", total)
return
}
// insert row now
if err = s.dao.CreateAggregateBranch(c, branchDB); err != nil {
log.Error("SaveAggregateBranchDatabase CreateAggregateBranch(%+v)", err)
return
}
return
}