github.com/reviewdog/reviewdog@v0.17.5-0.20240516205324-0cd103a83d58/service/gitlab/gitlab_mr_diff.go (about)

     1  package gitlab
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"strings"
     9  
    10  	"github.com/xanzy/go-gitlab"
    11  
    12  	"github.com/reviewdog/reviewdog"
    13  	"github.com/reviewdog/reviewdog/service/serviceutil"
    14  )
    15  
    16  var _ reviewdog.DiffService = &MergeRequestDiff{}
    17  
    18  // MergeRequestDiff is a diff service for GitLab MergeRequest.
    19  type MergeRequestDiff struct {
    20  	cli      *gitlab.Client
    21  	pr       int
    22  	sha      string
    23  	projects string
    24  
    25  	// wd is working directory relative to root of repository.
    26  	wd string
    27  }
    28  
    29  // NewGitLabMergeRequestDiff returns a new MergeRequestDiff service.
    30  // itLabMergeRequestDiff service needs git command in $PATH.
    31  func NewGitLabMergeRequestDiff(cli *gitlab.Client, owner, repo string, pr int, sha string) (*MergeRequestDiff, error) {
    32  	workDir, err := serviceutil.GitRelWorkdir()
    33  	if err != nil {
    34  		return nil, fmt.Errorf("MergeRequestCommitCommenter needs 'git' command: %w", err)
    35  	}
    36  	return &MergeRequestDiff{
    37  		cli:      cli,
    38  		pr:       pr,
    39  		sha:      sha,
    40  		projects: owner + "/" + repo,
    41  		wd:       workDir,
    42  	}, nil
    43  }
    44  
    45  // Diff returns a diff of MergeRequest. It runs `git diff` locally instead of
    46  // diff_url of GitLab Merge Request because diff of diff_url is not suited for
    47  // comment API in a sense that diff of diff_url is equivalent to
    48  // `git diff --no-renames`, we want diff which is equivalent to
    49  // `git diff --find-renames`.
    50  func (g *MergeRequestDiff) Diff(ctx context.Context) ([]byte, error) {
    51  	mr, _, err := g.cli.MergeRequests.GetMergeRequest(g.projects, g.pr, nil)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	targetBranch, _, err := g.cli.Branches.GetBranch(mr.TargetProjectID, mr.TargetBranch, nil)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	return g.gitDiff(ctx, g.sha, targetBranch.Commit.ID)
    60  }
    61  
    62  func (g *MergeRequestDiff) gitDiff(_ context.Context, baseSha, targetSha string) ([]byte, error) {
    63  	mergeBase := os.Getenv("CI_MERGE_REQUEST_DIFF_BASE_SHA")
    64  	if mergeBase == "" {
    65  		b, err := exec.Command("git", "merge-base", targetSha, baseSha).Output()
    66  		if err != nil {
    67  			return nil, fmt.Errorf("failed to get merge-base commit: %w", err)
    68  		}
    69  		mergeBase = strings.Trim(string(b), "\n")
    70  	}
    71  	bytes, err := exec.Command("git", "diff", "--find-renames", mergeBase, baseSha).Output()
    72  	if err != nil {
    73  		return nil, fmt.Errorf("failed to run git diff: %w", err)
    74  	}
    75  	return bytes, nil
    76  }
    77  
    78  // Strip returns 1 as a strip of git diff.
    79  func (g *MergeRequestDiff) Strip() int {
    80  	return 1
    81  }