goc/pkg/github/github.go

203 lines
6.4 KiB
Go
Raw Normal View History

2020-05-22 02:33:03 +00:00
/*
Copyright 2020 Qiniu Cloud (qiniu.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package github
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"github.com/google/go-github/github"
2020-06-07 03:58:55 +00:00
"github.com/hashicorp/go-retryablehttp"
2020-05-22 02:33:03 +00:00
"github.com/olekukonko/tablewriter"
"github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"github.com/qiniu/goc/pkg/cover"
)
2020-07-26 09:03:47 +00:00
// CommentsPrefix is the prefix when commenting on Github Pull Requests
// It is also the flag when checking whether the target comment exists or not to avoid duplicate
2020-05-22 02:33:03 +00:00
const CommentsPrefix = "The following is the coverage report on the affected files."
2020-07-24 09:27:03 +00:00
type PrComment interface {
CreateGithubComment(commentPrefix string, diffCovList cover.DeltaCovList) (err error)
PostComment(content, commentPrefix string) error
EraseHistoryComment(commentPrefix string) error
GetPrChangedFiles() (files []string, err error)
GetCommentFlag() string
}
// GithubPrComment is the entry which is able to comment on Github Pull Requests
type GithubPrComment struct {
2020-05-22 02:33:03 +00:00
RobotUserName string
RepoOwner string
RepoName string
CommentFlag string
PrNumber int
Ctx context.Context
opt *github.ListOptions
GithubClient *github.Client
}
2020-06-07 03:58:55 +00:00
// NewPrClient creates an Client which be able to comment on Github Pull Request
2020-07-24 09:27:03 +00:00
func NewPrClient(githubTokenPath, repoOwner, repoName, prNumStr, botUserName, commentFlag string) *GithubPrComment {
2020-05-22 02:33:03 +00:00
var client *github.Client
2020-06-07 03:58:55 +00:00
// performs automatic retries when connection error occurs or a 500-range response code received (except 501)
retryClient := retryablehttp.NewClient()
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, retryClient.StandardClient())
2020-05-22 02:33:03 +00:00
prNum, err := strconv.Atoi(prNumStr)
if err != nil {
logrus.WithError(err).Fatalf("Failed to convert prNumStr(=%v) to int.\n", prNumStr)
}
token, err := ioutil.ReadFile(githubTokenPath)
if err != nil {
logrus.WithError(err).Fatalf("Failed to get github token.\n")
}
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: strings.TrimSpace(string(token))},
)
tc := oauth2.NewClient(ctx, ts)
client = github.NewClient(tc)
2020-07-24 09:27:03 +00:00
return &GithubPrComment{
2020-05-22 02:33:03 +00:00
RobotUserName: botUserName,
RepoOwner: repoOwner,
RepoName: repoName,
PrNumber: prNum,
CommentFlag: commentFlag,
Ctx: ctx,
opt: &github.ListOptions{Page: 1},
GithubClient: client,
}
}
2020-07-26 09:03:47 +00:00
// CreateGithubComment post github comment of diff coverage
2020-07-24 09:27:03 +00:00
func (c *GithubPrComment) CreateGithubComment(commentPrefix string, diffCovList cover.DeltaCovList) (err error) {
2020-05-22 02:33:03 +00:00
if len(diffCovList) == 0 {
logrus.Printf("Detect 0 files coverage diff, will not comment to github.")
return nil
}
content := GenCommentContent(commentPrefix, diffCovList)
err = c.PostComment(content, commentPrefix)
if err != nil {
logrus.WithError(err).Fatalf("Post comment to github failed.")
}
return
}
2020-07-26 09:03:47 +00:00
// PostComment post comment on github. It erased the old one if existed to avoid duplicate
2020-07-24 09:27:03 +00:00
func (c *GithubPrComment) PostComment(content, commentPrefix string) error {
2020-05-22 02:33:03 +00:00
//step1: erase history similar comment to avoid too many comment for same job
err := c.EraseHistoryComment(commentPrefix)
if err != nil {
return err
}
//step2: post comment with new result
comment := &github.IssueComment{
Body: &content,
}
_, _, err = c.GithubClient.Issues.CreateComment(c.Ctx, c.RepoOwner, c.RepoName, c.PrNumber, comment)
if err != nil {
return err
}
return nil
}
2020-07-26 09:03:47 +00:00
// EraseHistoryComment erase history similar comment before post again
2020-07-24 09:27:03 +00:00
func (c *GithubPrComment) EraseHistoryComment(commentPrefix string) error {
2020-05-22 02:33:03 +00:00
comments, _, err := c.GithubClient.Issues.ListComments(c.Ctx, c.RepoOwner, c.RepoName, c.PrNumber, nil)
if err != nil {
logrus.Errorf("list PR comments failed.")
return err
}
logrus.Infof("the count of history comments by %s is: %v", c.RobotUserName, len(comments))
for _, cm := range comments {
if *cm.GetUser().Login == c.RobotUserName && strings.HasPrefix(cm.GetBody(), commentPrefix) {
_, err = c.GithubClient.Issues.DeleteComment(c.Ctx, c.RepoOwner, c.RepoName, *cm.ID)
if err != nil {
logrus.Errorf("delete PR comments %d failed.", *cm.ID)
return err
}
}
}
return nil
}
2020-06-07 03:58:55 +00:00
//GetPrChangedFiles get github pull request changes file list
2020-07-24 09:27:03 +00:00
func (c *GithubPrComment) GetPrChangedFiles() (files []string, err error) {
2020-05-22 02:33:03 +00:00
var commitFiles []*github.CommitFile
for {
f, resp, err := c.GithubClient.PullRequests.ListFiles(c.Ctx, c.RepoOwner, c.RepoName, c.PrNumber, c.opt)
if err != nil {
logrus.Errorf("Get PR changed file failed. repoOwner is: %s, repoName is: %s, prNum is: %d", c.RepoOwner, c.RepoName, c.PrNumber)
return nil, err
}
commitFiles = append(commitFiles, f...)
if resp.NextPage == 0 {
break
}
c.opt.Page = resp.NextPage
}
logrus.Infof("get %d PR changed files:", len(commitFiles))
for _, file := range commitFiles {
files = append(files, *file.Filename)
logrus.Infof("%s", *file.Filename)
}
return
}
2020-07-24 09:27:03 +00:00
// GetCommentFlag get CommentFlag from the GithubPrComment
func (c *GithubPrComment) GetCommentFlag() string {
return c.CommentFlag
}
2020-07-26 09:03:47 +00:00
// GenCommentContent generate github comment content based on diff coverage and commentFlag
2020-05-22 02:33:03 +00:00
func GenCommentContent(commentPrefix string, delta cover.DeltaCovList) string {
var buf bytes.Buffer
table := tablewriter.NewWriter(&buf)
2020-06-27 05:24:57 +00:00
table.SetHeader([]string{"File", "Base Coverage", "New Coverage", "Delta"})
table.SetAutoFormatHeaders(false)
2020-05-22 02:33:03 +00:00
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER})
for _, d := range delta {
table.Append([]string{fmt.Sprintf("[%s](%s)", d.FileName, d.LineCovLink), d.BasePer, d.NewPer, d.DeltaPer})
}
table.Render()
content := []string{
commentPrefix,
fmt.Sprintf("Say `/test %s` to re-run this coverage report", os.Getenv("JOB_NAME")),
buf.String(),
}
return strings.Join(content, "\n")
}