github.com/mistwind/reviewdog@v0.0.0-20230322024206-9cfa11856d58/service/gitlab/gitlab_push_commits.go (about) 1 package gitlab 2 3 import ( 4 "context" 5 "fmt" 6 "path/filepath" 7 "sync" 8 9 "github.com/xanzy/go-gitlab" 10 "golang.org/x/sync/errgroup" 11 12 "github.com/mistwind/reviewdog" 13 "github.com/mistwind/reviewdog/service/commentutil" 14 "github.com/mistwind/reviewdog/service/serviceutil" 15 ) 16 17 var _ reviewdog.CommentService = &PushCommitsCommenter{} 18 19 // PushCommitsCommenter is a comment service for GitLab commit in push. 20 // 21 // API: 22 // https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit 23 // POST /projects/:id/repository/commits/:sha/comments 24 type PushCommitsCommenter struct { 25 cli *gitlab.Client 26 sha string 27 projects string 28 29 sync.Mutex 30 postComments []*reviewdog.Comment 31 32 postedcs commentutil.PostedComments 33 34 // wd is working directory relative to root of repository. 35 wd string 36 } 37 38 // NewGitLabPushCommitsCommenter returns a new PushCommitsCommenter service. 39 // PushCommitsCommenter service needs git command in $PATH. 40 func NewGitLabPushCommitsCommenter(cli *gitlab.Client, owner, repo string, sha string) (*PushCommitsCommenter, error) { 41 workDir, err := serviceutil.GitRelWorkdir() 42 if err != nil { 43 return nil, fmt.Errorf("PushCommitsCommenter needs 'git' command: %w", err) 44 } 45 return &PushCommitsCommenter{ 46 cli: cli, 47 sha: sha, 48 projects: owner + "/" + repo, 49 wd: workDir, 50 }, nil 51 } 52 53 // NewGitLabPushCommitsCommenterWithProjectID returns a new PushCommitsCommenter service. 54 // PushCommitsCommenter service needs git command in $PATH. 55 func NewGitLabPushCommitsCommenterWithProjectID(cli *gitlab.Client, projectID string, sha string) (*PushCommitsCommenter, error) { 56 workDir, err := serviceutil.GitRelWorkdir() 57 if err != nil { 58 return nil, fmt.Errorf("PushCommitsCommenter needs 'git' command: %w", err) 59 } 60 return &PushCommitsCommenter{ 61 cli: cli, 62 sha: sha, 63 projects: projectID, 64 wd: workDir, 65 }, nil 66 } 67 68 // Post accepts a comment and holds it. Flush method actually posts comments to 69 // GitLab in parallel. 70 func (g *PushCommitsCommenter) Post(_ context.Context, c *reviewdog.Comment) error { 71 c.Result.Diagnostic.GetLocation().Path = filepath.ToSlash( 72 filepath.Join(g.wd, c.Result.Diagnostic.GetLocation().GetPath())) 73 g.Lock() 74 defer g.Unlock() 75 g.postComments = append(g.postComments, c) 76 return nil 77 } 78 79 // Flush posts comments which has not been posted yet. 80 func (g *PushCommitsCommenter) Flush(ctx context.Context) error { 81 g.Lock() 82 defer g.Unlock() 83 84 return g.postCommentsForEach(ctx) 85 } 86 87 func (g *PushCommitsCommenter) postCommentsForEach(ctx context.Context) error { 88 var eg errgroup.Group 89 for _, c := range g.postComments { 90 c := c 91 loc := c.Result.Diagnostic.GetLocation() 92 lnum := int(loc.GetRange().GetStart().GetLine()) 93 body := commentutil.MarkdownComment(c) 94 if !c.Result.InDiffFile || lnum == 0 || g.postedcs.IsPosted(c, lnum, body) { 95 continue 96 } 97 eg.Go(func() error { 98 prcomment := &gitlab.PostCommitCommentOptions{ 99 Note: gitlab.String(body), 100 Path: gitlab.String(loc.GetPath()), 101 Line: gitlab.Int(lnum), 102 LineType: gitlab.String("new"), 103 } 104 _, _, err := g.cli.Commits.PostCommitComment(g.projects, g.sha, prcomment, gitlab.WithContext(ctx)) 105 return err 106 }) 107 } 108 return eg.Wait() 109 }