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 }