github.com/saurabh-prakash/go-cover-view@v0.0.1/markdown.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"strings"
    12  	"text/template"
    13  
    14  	"github.com/google/go-github/v32/github"
    15  	"golang.org/x/oauth2"
    16  	"golang.org/x/tools/cover"
    17  )
    18  
    19  const (
    20  	gitHubCommentCharLimit = 65536
    21  	toBeContdMsg           = "... to be contd."
    22  )
    23  
    24  var reportTmp = template.Must(template.New("report").Parse(`
    25  # go-cover-view
    26  
    27  {{range .}}
    28  <details> <summary> {{.FileName}} </summary>
    29  {{.Report}}
    30  </details>
    31  {{end}}
    32  `))
    33  
    34  type markdownRenderer struct{}
    35  
    36  var _ renderer = (*markdownRenderer)(nil)
    37  
    38  func (r *markdownRenderer) Render(w io.Writer, profiles []*cover.Profile, path string) error {
    39  	results, err := getMarkdownReports(profiles, path)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	return reportTmp.ExecuteTemplate(w, "report", results)
    44  }
    45  
    46  type markdownReport struct {
    47  	FileName string
    48  	Report   string
    49  }
    50  
    51  func newMarkdownReport(fileName string, lines []string) *markdownReport {
    52  	return &markdownReport{
    53  		FileName: fileName,
    54  		Report:   buildReport(lines),
    55  	}
    56  }
    57  
    58  func getMarkdownReports(profiles []*cover.Profile, path string) ([]*markdownReport, error) {
    59  	diffs, err := getDiffs()
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	reports := make([]*markdownReport, 0, len(profiles))
    64  	for _, profile := range profiles {
    65  		lines, err := getLines(profile, path)
    66  		if err != nil {
    67  			return nil, err
    68  		}
    69  		if gitDiffOnly {
    70  			if containsDiff(profile.FileName, path, diffs) {
    71  				reports = append(reports, newMarkdownReport(profile.FileName, lines))
    72  			}
    73  			continue
    74  		}
    75  		reports = append(reports, newMarkdownReport(profile.FileName, lines))
    76  	}
    77  	return reports, nil
    78  }
    79  
    80  func buildReport(lines []string) string {
    81  	var b strings.Builder
    82  	fmt.Fprintln(&b)
    83  	fmt.Fprintln(&b, "```")
    84  	for _, line := range lines {
    85  		fmt.Fprintln(&b, line)
    86  	}
    87  	fmt.Fprintln(&b)
    88  	fmt.Fprintln(&b, "```")
    89  	return b.String()
    90  }
    91  
    92  func upsertGitHubPullRequestComment(profiles []*cover.Profile, path string) error {
    93  	eventPath := os.Getenv("GITHUB_EVENT_PATH")
    94  	if eventPath == "" {
    95  		return errors.New("env: GITHUB_EVENT_PATH is missing")
    96  	}
    97  	f, err := os.Open(eventPath)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	defer f.Close()
   102  	var event github.PullRequestEvent
   103  	if err := json.NewDecoder(f).Decode(&event); err != nil {
   104  		return err
   105  	}
   106  	ctx := context.Background()
   107  	token := os.Getenv("GITHUB_TOKEN")
   108  	if token == "" {
   109  		return errors.New("env: GITHUB_TOKEN is missing")
   110  	}
   111  
   112  	var buf bytes.Buffer
   113  	r := &markdownRenderer{}
   114  	if err := r.Render(&buf, profiles, path); err != nil {
   115  		return err
   116  	}
   117  
   118  	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
   119  	httpClient := oauth2.NewClient(ctx, ts)
   120  	gc := github.NewClient(httpClient)
   121  	pr := event.GetPullRequest()
   122  	_repo := strings.Split(os.Getenv("GITHUB_REPOSITORY"), "/")
   123  	if len(_repo) != 2 {
   124  		return fmt.Errorf("invalid env: GITHUB_REPOSITORY=%v", _repo)
   125  	}
   126  	owner := _repo[0]
   127  	repo := _repo[1]
   128  	comments, _, err := gc.Issues.ListComments(ctx, owner, repo, pr.GetNumber(), nil)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	var commentID int64
   133  	for _, c := range comments {
   134  		u := c.GetUser()
   135  		if u.GetLogin() == "github-actions[bot]" && u.GetType() == "Bot" && strings.Contains(c.GetBody(), "# go-cover-view") {
   136  			commentID = c.GetID()
   137  			break
   138  		}
   139  	}
   140  	body := buf.String()
   141  	if len(body) > gitHubCommentCharLimit {
   142  		body = body[:gitHubCommentCharLimit-len(toBeContdMsg)] + toBeContdMsg
   143  	}
   144  	if commentID == 0 {
   145  		_, _, err := gc.Issues.CreateComment(ctx, owner, repo, pr.GetNumber(), &github.IssueComment{
   146  			Body: &body,
   147  		})
   148  		if err != nil {
   149  			return err
   150  		}
   151  	} else {
   152  		_, _, err := gc.Issues.EditComment(ctx, owner, repo, commentID, &github.IssueComment{
   153  			Body: &body,
   154  		})
   155  		if err != nil {
   156  			return err
   157  		}
   158  	}
   159  	return nil
   160  }