github.com/Mistwind/reviewdog@v0.0.0-20230317041057-48e69b6d9e86/reviewdog.go (about)

     1  package reviewdog
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"log"
     9  	"os"
    10  
    11  	"github.com/reviewdog/reviewdog/diff"
    12  	"github.com/reviewdog/reviewdog/filter"
    13  	"github.com/reviewdog/reviewdog/parser"
    14  	"github.com/reviewdog/reviewdog/proto/rdf"
    15  )
    16  
    17  // Reviewdog represents review dog application which parses result of compiler
    18  // or linter, get diff and filter the results by diff, and report filtered
    19  // results.
    20  type Reviewdog struct {
    21  	toolname    string
    22  	p           parser.Parser
    23  	c           CommentService
    24  	d           DiffService
    25  	filterMode  filter.Mode
    26  	failOnError bool
    27  }
    28  
    29  // NewReviewdog returns a new Reviewdog.
    30  func NewReviewdog(toolname string, p parser.Parser, c CommentService, d DiffService, filterMode filter.Mode, failOnError bool) *Reviewdog {
    31  	return &Reviewdog{p: p, c: c, d: d, toolname: toolname, filterMode: filterMode, failOnError: failOnError}
    32  }
    33  
    34  // RunFromResult creates a new Reviewdog and runs it with check results.
    35  func RunFromResult(ctx context.Context, c CommentService, results []*rdf.Diagnostic,
    36  	filediffs []*diff.FileDiff, strip int, toolname string, filterMode filter.Mode, failOnError bool) error {
    37  	return (&Reviewdog{c: c, toolname: toolname, filterMode: filterMode, failOnError: failOnError}).runFromResult(ctx, results, filediffs, strip, failOnError)
    38  }
    39  
    40  // Comment represents a reported result as a comment.
    41  type Comment struct {
    42  	Result   *filter.FilteredDiagnostic
    43  	ToolName string
    44  }
    45  
    46  // CommentService is an interface which posts Comment.
    47  type CommentService interface {
    48  	Post(context.Context, *Comment) error
    49  }
    50  
    51  // BulkCommentService posts comments all at once when Flush() is called.
    52  // Flush() will be called at the end of reviewdog run.
    53  type BulkCommentService interface {
    54  	CommentService
    55  	Flush(context.Context) error
    56  }
    57  
    58  // DiffService is an interface which get diff.
    59  type DiffService interface {
    60  	Diff(context.Context) ([]byte, error)
    61  	Strip() int
    62  }
    63  
    64  func (w *Reviewdog) runFromResult(ctx context.Context, results []*rdf.Diagnostic,
    65  	filediffs []*diff.FileDiff, strip int, failOnError bool) error {
    66  	wd, err := os.Getwd()
    67  	if err != nil {
    68  		return err
    69  	}
    70  	fmt.Printf("getcwd ok: %s\n", wd)
    71  
    72  	checks := filter.FilterCheck(results, filediffs, strip, wd, w.filterMode)
    73  	hasViolations := false
    74  	fmt.Printf("checks length is %d\n", len(checks))
    75  
    76  	for _, check := range checks {
    77  		if !check.ShouldReport {
    78  			fmt.Printf("comment for %s should not be reported\n", check.Diagnostic.Message)
    79  			continue
    80  		}
    81  		fmt.Printf("comment for %s should be reported\n", check.Diagnostic.Message)
    82  		comment := &Comment{
    83  			Result:   check,
    84  			ToolName: w.toolname,
    85  		}
    86  
    87  		fmt.Printf("posting comment for %s...\n", check.Diagnostic.Message)
    88  
    89  		if err := w.c.Post(ctx, comment); err != nil {
    90  			fmt.Printf("posting comment for %s error: %s\n", check.Diagnostic.Message, err)
    91  			return err
    92  		}
    93  		fmt.Printf("posting comment for %s ok\n", check.Diagnostic.Message)
    94  		hasViolations = true
    95  	}
    96  
    97  	if bulk, ok := w.c.(BulkCommentService); ok {
    98  		if err := bulk.Flush(ctx); err != nil {
    99  			return err
   100  		}
   101  	}
   102  
   103  	if failOnError && hasViolations {
   104  		return fmt.Errorf("input data has violations")
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  // Run runs Reviewdog application.
   111  func (w *Reviewdog) Run(ctx context.Context, r io.Reader) error {
   112  	results, err := w.p.Parse(r)
   113  	if err != nil {
   114  		return fmt.Errorf("parse error: %w", err)
   115  	}
   116  
   117  	log.Printf("reviewdog: parse result: %#v\n", results)
   118  
   119  	d, err := w.d.Diff(ctx)
   120  	if err != nil {
   121  		return fmt.Errorf("fail to get diff: %w", err)
   122  	}
   123  	log.Printf("reviewdog: diff ok\n")
   124  
   125  	filediffs, err := diff.ParseMultiFile(bytes.NewReader(d))
   126  	if err != nil {
   127  		return fmt.Errorf("fail to parse diff: %w", err)
   128  	}
   129  	log.Printf("reviewdog: parse multifile ok\n")
   130  
   131  	return w.runFromResult(ctx, results, filediffs, w.d.Strip(), w.failOnError)
   132  }