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

     1  package gerrit
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os/exec"
     7  	"strings"
     8  
     9  	"golang.org/x/build/gerrit"
    10  
    11  	"github.com/reviewdog/reviewdog"
    12  	"github.com/reviewdog/reviewdog/service/serviceutil"
    13  )
    14  
    15  const (
    16  	stripDiffResult = 1
    17  )
    18  
    19  var _ reviewdog.DiffService = &ChangeDiff{}
    20  
    21  // ChangeDiff is a diff service for Gerrit changes.
    22  type ChangeDiff struct {
    23  	cli      *gerrit.Client
    24  	changeID string
    25  	branch   string
    26  
    27  	// wd is working directory relative to root of repository.
    28  	wd string
    29  }
    30  
    31  // NewChangeDiff returns a new ChangeDiff service,
    32  // it needs git command in $PATH.
    33  func NewChangeDiff(cli *gerrit.Client, branch, changeID string) (*ChangeDiff, error) {
    34  	workDir, err := serviceutil.GitRelWorkdir()
    35  	if err != nil {
    36  		return nil, fmt.Errorf("ChangeDiff needs 'git' command: %w", err)
    37  	}
    38  	return &ChangeDiff{
    39  		cli:      cli,
    40  		branch:   branch,
    41  		changeID: changeID,
    42  		wd:       workDir,
    43  	}, nil
    44  }
    45  
    46  // Diff returns a diff of MergeRequest. It runs `git diff` locally instead of
    47  // diff_url of GitLab Merge Request because diff of diff_url is not suited for
    48  // comment API in a sense that diff of diff_url is equivalent to
    49  // `git diff --no-renames`, we want diff which is equivalent to
    50  // `git diff --find-renames`.
    51  func (g *ChangeDiff) Diff(ctx context.Context) ([]byte, error) {
    52  	change, err := g.cli.GetChangeDetail(ctx, g.changeID, gerrit.QueryChangesOpt{
    53  		Fields: []string{"CURRENT_REVISION"},
    54  	})
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	return g.gitDiff(ctx, change.CurrentRevision, g.branch)
    59  }
    60  
    61  func (g *ChangeDiff) gitDiff(_ context.Context, baseSha, targetSha string) ([]byte, error) {
    62  	b, err := exec.Command("git", "merge-base", targetSha, baseSha).Output() // #nosec
    63  	if err != nil {
    64  		return nil, fmt.Errorf("failed to get merge-base commit: %w", err)
    65  	}
    66  	mergeBase := strings.Trim(string(b), "\n")
    67  	bytes, err := exec.Command("git", "diff", "--find-renames", mergeBase, baseSha).Output()
    68  	if err != nil {
    69  		return nil, fmt.Errorf("failed to run git diff: %w", err)
    70  	}
    71  	return bytes, nil
    72  }
    73  
    74  // Strip returns 1 as a strip of git diff.
    75  func (g *ChangeDiff) Strip() int {
    76  	return stripDiffResult
    77  }