package db import ( "bytes" "context" "encoding/json" "fmt" "log" "net/http" "os" "strconv" "time" ) func UploadBranchData(namespace, project, branch, coverageStr, html string) error { coverage, err := strconv.ParseFloat(coverageStr, 64) if err != nil { return fmt.Errorf("coverage param must be valid float, instead got %s", coverageStr) } // Because we don't have have a primary key, we must do the upsert manually. _, err = QueryByBranch(namespace, project, branch) if err == ErrNoData { _, err = db.Exec("INSERT INTO badge (namespace, project_name, branch, coverage, html) VALUES (?, ?, ?, ?, ?)", namespace, project, branch, coverage, html) if err != nil { return fmt.Errorf("could not update database: %v", err) } } if err != nil { return err } _, err = db.Exec("UPDATE badge SET coverage=?, html=? WHERE namespace=? AND project_name=? AND branch=?", coverage, html, namespace, project, branch) if err != nil { return fmt.Errorf("could not update database: %v", err) } return nil } func UploadPullData(namespace, project, pullStr, coverageStr, html string) error { pull, err := strconv.Atoi(pullStr) if err != nil { return fmt.Errorf("pull param must be valid int, instead got %s", pullStr) } coverage, err := strconv.ParseFloat(coverageStr, 64) if err != nil { return fmt.Errorf("coverage param must be valid float, instead got %s", coverageStr) } // Because we don't have have a primary key, we must do the upsert manually. _, err = QueryByPull(namespace, project, pullStr) if err == ErrNoData { _, err = db.Exec("INSERT INTO badge (namespace, project_name, pull, coverage, html) VALUES (?, ?, ?, ?, ?)", namespace, project, pull, coverage, html) if err != nil { return fmt.Errorf("could not update database: %v", err) } createIssueMessage(namespace, project, pullStr, coverage) return nil } if err != nil { return err } _, err = db.Exec("UPDATE badge SET coverage=?, html=? WHERE namespace=? AND project_name=? AND pull=?", coverage, html, namespace, project, pull) if err != nil { return fmt.Errorf("could not update database: %v", err) } return nil } const urlTemplate = "%s/api/v1/repos/%s/%s/issues/%s/comments" const messageTemplate = `## Coverage Report Ready [![Coverage %.01f%%](%s)](%s) The coverage report can be found [here](%s). You can also click the badge above. --- I am a bot and this was done automatically as part of CI. The badge and report content will automatically stay up to date to the PR.` type messageBody struct { Body string `json:"body"` } func createIssueMessage(namespace, project, pull string, coverage float64) { badgeURL := fmt.Sprintf("https://coverage.teamortix.com/badge/pulls/%s/%s/%s", namespace, project, pull) reportURL := fmt.Sprintf("https://coverage.teamortix.com/report/pulls/%s/%s/%s", namespace, project, pull) message := fmt.Sprintf(messageTemplate, coverage*100, badgeURL, reportURL, reportURL) url := fmt.Sprintf(urlTemplate, os.Getenv("GITEA_HOST"), namespace, project, pull) jsonBytes, err := json.Marshal(messageBody{message}) if err != nil { log.Printf("Could not marshal json: %v\n", err) } body := bytes.NewReader(jsonBytes) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() req, err := http.NewRequestWithContext(ctx, "POST", url, body) if err != nil { log.Printf("Could not make request: %v\n", err) return } req.Header.Set("Authorization", "token "+os.Getenv("COVERAGE_TOKEN")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { log.Printf("Issuecomment request error: %v\n", err) return } err = res.Body.Close() if err != nil { log.Printf("Could not close request body: %v", err) return } if res.StatusCode != 201 { log.Printf("Issuecomment unexpected statuscode: %d\n", res.StatusCode) return } }