goc/pkg/prow/job.go

240 lines
7.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.
*/
2020-07-26 10:15:35 +00:00
2020-05-22 02:33:03 +00:00
package prow
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"strconv"
"strings"
"time"
"github.com/sirupsen/logrus"
"github.com/qiniu/goc/pkg/cover"
"github.com/qiniu/goc/pkg/github"
"github.com/qiniu/goc/pkg/qiniu"
)
// IProwAction defines the normal action in prow system
type IProwAction interface {
Fetch(BuildID, name string) []byte
RunPresubmit() error
RunPostsubmit() error
RunPeriodic() error
}
// Job is a prowjob in prow
type Job struct {
JobName string
Org string
RepoName string
PRNumStr string
BuildId string //prow job build number
PostSubmitJob string
PostSubmitCoverProfile string
CovThreshold int
LocalProfilePath string
2020-07-24 09:27:03 +00:00
QiniuClient qiniu.Client
2020-07-24 11:24:07 +00:00
LocalArtifacts qiniu.Artifacts
2020-07-24 09:27:03 +00:00
GithubComment github.PrComment
2020-05-22 02:33:03 +00:00
FullDiff bool
}
// Fetch the file from cloud
func (j *Job) Fetch(BuildID, name string) []byte {
return []byte{}
}
// RunPresubmit run a presubmit job
func (j *Job) RunPresubmit() error {
2020-07-24 09:27:03 +00:00
// step1: get local profile cov
2020-05-22 02:33:03 +00:00
localP, err := cover.ReadFileToCoverList(j.LocalProfilePath)
if err != nil {
2020-07-24 11:24:07 +00:00
return fmt.Errorf("failed to get remote cover profile: %s", err.Error())
2020-05-22 02:33:03 +00:00
}
2020-07-24 09:27:03 +00:00
//step2: find the remote healthy cover profile from qiniu bucket
2020-05-22 02:33:03 +00:00
remoteProfile, err := qiniu.FindBaseProfileFromQiniu(j.QiniuClient, j.PostSubmitJob, j.PostSubmitCoverProfile)
if err != nil {
2020-07-24 11:24:07 +00:00
return fmt.Errorf("failed to get remote cover profile: %s", err.Error())
2020-05-22 02:33:03 +00:00
}
if remoteProfile == nil {
logrus.Infof("get non healthy remoteProfile, do nothing")
return nil
}
baseP, err := cover.CovList(bytes.NewReader(remoteProfile))
if err != nil {
2020-07-24 11:24:07 +00:00
return fmt.Errorf("failed to get remote cover profile: %s", err.Error())
2020-05-22 02:33:03 +00:00
}
2020-07-24 09:27:03 +00:00
// step3: get github pull request changed files' name and calculate diff cov between local and remote profile
changedFiles, deltaCovList, err := getFilesAndCovList(j.FullDiff, j.GithubComment, localP, baseP)
if err != nil {
2020-07-24 11:24:07 +00:00
return fmt.Errorf("Get files and covlist failed: %s", err.Error())
2020-05-22 02:33:03 +00:00
}
2020-07-24 09:27:03 +00:00
// step4: generate changed file html coverage
2020-05-22 02:33:03 +00:00
err = j.WriteChangedCov(changedFiles)
if err != nil {
2020-07-24 11:24:07 +00:00
return fmt.Errorf("filter local profile to %s with changed files failed: %s", j.LocalArtifacts.GetChangedProfileName(), err.Error())
2020-05-22 02:33:03 +00:00
}
err = j.CreateChangedCovHtml()
if err != nil {
2020-07-24 11:24:07 +00:00
return fmt.Errorf("create changed file related coverage html failed: %s", err.Error())
2020-05-22 02:33:03 +00:00
}
j.SetDeltaCovLinks(deltaCovList)
2020-07-24 11:24:07 +00:00
// step5: post comment to github
2020-05-22 02:33:03 +00:00
commentPrefix := github.CommentsPrefix
2020-07-24 09:27:03 +00:00
if j.GithubComment.GetCommentFlag() != "" {
commentPrefix = fmt.Sprintf("**%s** ", j.GithubComment.GetCommentFlag()) + commentPrefix
2020-05-22 02:33:03 +00:00
}
if len(deltaCovList) > 0 {
totalDelta := cover.PercentStr(cover.TotalDelta(localP, baseP))
deltaCovList = append(deltaCovList, cover.DeltaCov{FileName: "Total", BasePer: baseP.TotalPercentage(), NewPer: localP.TotalPercentage(), DeltaPer: totalDelta})
}
2020-05-22 02:33:03 +00:00
err = j.GithubComment.CreateGithubComment(commentPrefix, deltaCovList)
if err != nil {
2020-07-24 11:24:07 +00:00
return fmt.Errorf("Post comment to github failed: %s", err.Error())
2020-05-22 02:33:03 +00:00
}
return nil
}
// RunPostsubmit run a postsubmit job
func (j *Job) RunPostsubmit() error {
return nil
}
// RunPeriodic run a periodic job
func (j *Job) RunPeriodic() error {
return nil
}
//trim github filename to profile format:
// src/qiniu.com/kodo/io/io/io_svr.go -> qiniu.com/kodo/io/io/io_svr.go
func trimGhFileToProfile(ghFiles []string) (pFiles []string) {
//TODO: need compatible other situation
logrus.Infof("trim PR changed file name to:")
for _, f := range ghFiles {
file := strings.TrimPrefix(f, "src/")
logrus.Infof("%s", file)
pFiles = append(pFiles, file)
}
return
}
// filter local profile with changed files and save to j.LocalArtifacts.ChangedProfileName
func (j *Job) WriteChangedCov(changedFiles []string) error {
p, err := ioutil.ReadFile(j.LocalProfilePath)
if err != nil {
logrus.Printf("Open file %s failed", j.LocalProfilePath)
return err
}
cp := j.LocalArtifacts.CreateChangedProfile()
defer cp.Close()
s := bufio.NewScanner(bytes.NewReader(p))
s.Scan()
writeLine(cp, s.Text())
for s.Scan() {
for _, file := range changedFiles {
if strings.HasPrefix(s.Text(), file) {
writeLine(cp, s.Text())
}
}
}
return nil
}
// writeLine writes a line in the given file, if the file pointer is not nil
func writeLine(file *os.File, content string) {
if file != nil {
fmt.Fprintln(file, content)
}
}
func (j *Job) JobPrefixOnQiniu() string {
return path.Join("pr-logs", "pull", j.Org+"_"+j.RepoName, j.PRNumStr, j.JobName, j.BuildId)
}
func (j *Job) HtmlProfile() string {
return fmt.Sprintf("%s-%s-pr%s-coverage.html", j.Org, j.RepoName, j.PRNumStr)
}
func (j *Job) SetDeltaCovLinks(c cover.DeltaCovList) {
c.Sort()
for i := 0; i < len(c); i++ {
qnKey := path.Join(j.JobPrefixOnQiniu(), "artifacts", j.HtmlProfile())
authQnKey := j.QiniuClient.GetAccessURL(qnKey, time.Hour*24*7)
c[i].SetLineCovLink(authQnKey + "#file" + strconv.Itoa(i))
logrus.Printf("file %s html coverage link is: %s\n", c[i].FileName, c[i].GetLineCovLink())
}
}
// CreateChangedCovHtml create changed file related coverage html base on the local artifact
func (j *Job) CreateChangedCovHtml() error {
2020-07-24 11:24:07 +00:00
if j.LocalArtifacts.GetChangedProfileName() == "" {
2020-05-22 02:33:03 +00:00
logrus.Errorf("param LocalArtifacts.ChangedProfileName is empty")
}
2020-07-24 11:24:07 +00:00
pathProfileCov := j.LocalArtifacts.GetChangedProfileName()
2020-05-22 02:33:03 +00:00
pathHtmlCov := path.Join(os.Getenv("ARTIFACTS"), j.HtmlProfile())
cmdTxt := fmt.Sprintf("go tool cover -html=%s -o %s", pathProfileCov, pathHtmlCov)
logrus.Printf("Running command '%s'\n", cmdTxt)
cmd := exec.Command("go", "tool", "cover", "-html="+pathProfileCov, "-o", pathHtmlCov)
stdOut, err := cmd.CombinedOutput()
if err != nil {
logrus.Printf("Error executing cmd: %v; combinedOutput=%s", err, stdOut)
}
return err
}
2020-07-24 09:27:03 +00:00
func getFilesAndCovList(fullDiff bool, prComment github.PrComment, localP, baseP cover.CoverageList) (changedFiles []string, deltaCovList cover.DeltaCovList, err error) {
if !fullDiff {
// get github pull request changed files' name
var ghChangedFiles, err = prComment.GetPrChangedFiles()
if err != nil {
logrus.Errorf("Get pull request changed file failed.")
return nil, nil, err
}
if len(ghChangedFiles) == 0 {
logrus.Printf("0 files changed in github pull request, don't need to run coverage profile in presubmit.\n")
return nil, nil, nil
}
changedFiles = trimGhFileToProfile(ghChangedFiles)
// calculate diff cov between local and remote profile
deltaCovList = cover.GetChFileDeltaCov(localP, baseP, changedFiles)
return changedFiles, deltaCovList, nil
}
deltaCovList = cover.GetDeltaCov(localP, baseP)
logrus.Infof("get delta file name is:")
for _, d := range deltaCovList {
logrus.Infof("%s", d.FileName)
changedFiles = append(changedFiles, d.FileName)
}
return changedFiles, deltaCovList, nil
}