446 lines
12 KiB
Go
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
|
|
}
|