2021-10-10 16:43:37 +02:00
|
|
|
package sonarqube
|
2021-07-12 16:58:48 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
2021-10-09 18:09:54 +02:00
|
|
|
"encoding/json"
|
2021-10-03 17:57:22 +02:00
|
|
|
"fmt"
|
2021-07-12 16:58:48 +02:00
|
|
|
"io"
|
2021-10-10 19:01:48 +02:00
|
|
|
"log"
|
2021-10-03 17:57:22 +02:00
|
|
|
"net/http"
|
2021-10-10 19:01:48 +02:00
|
|
|
"strconv"
|
2021-10-10 19:39:33 +02:00
|
|
|
"strings"
|
2021-07-12 16:58:48 +02:00
|
|
|
|
2022-07-12 16:57:12 +02:00
|
|
|
"codeberg.org/justusbunsi/gitea-sonarqube-bot/internal/actions"
|
|
|
|
"codeberg.org/justusbunsi/gitea-sonarqube-bot/internal/settings"
|
2021-07-12 16:58:48 +02:00
|
|
|
)
|
|
|
|
|
2021-10-10 19:01:48 +02:00
|
|
|
func ParsePRIndex(name string) (int, error) {
|
2022-06-18 14:03:56 +02:00
|
|
|
res := settings.Pattern.RegExp.FindSubmatch([]byte(name))
|
2021-10-10 19:01:48 +02:00
|
|
|
if len(res) != 2 {
|
2022-06-18 14:03:56 +02:00
|
|
|
return 0, fmt.Errorf("branch name '%s' does not match regex '%s'", name, settings.Pattern.RegExp.String())
|
2021-10-10 19:01:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return strconv.Atoi(string(res[1]))
|
|
|
|
}
|
|
|
|
|
|
|
|
func PRNameFromIndex(index int64) string {
|
2022-06-18 14:03:56 +02:00
|
|
|
return fmt.Sprintf(settings.Pattern.Template, index)
|
2021-10-10 19:01:48 +02:00
|
|
|
}
|
|
|
|
|
2021-10-10 19:39:33 +02:00
|
|
|
func GetRenderedQualityGate(qg string) string {
|
|
|
|
status := ":white_check_mark:"
|
|
|
|
if qg != "OK" {
|
|
|
|
status = ":x:"
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("**Quality Gate**: %s", status)
|
|
|
|
}
|
|
|
|
|
2022-06-17 20:19:59 +02:00
|
|
|
func retrieveDataFromApi(sdk *SonarQubeSdk, request *http.Request, wrapper interface{}) error {
|
|
|
|
request.Header.Add("Authorization", sdk.basicAuth())
|
|
|
|
rawResponse, err := sdk.client.Do(request)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if rawResponse.StatusCode == http.StatusUnauthorized {
|
|
|
|
return fmt.Errorf("missing or invalid API token")
|
|
|
|
}
|
|
|
|
|
|
|
|
if rawResponse.Body != nil {
|
|
|
|
defer rawResponse.Body.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
body, err := sdk.bodyReader(rawResponse.Body)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(body, wrapper)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type Error struct {
|
|
|
|
Message string `json:"msg"`
|
|
|
|
}
|
|
|
|
|
2021-07-12 16:58:48 +02:00
|
|
|
type SonarQubeSdkInterface interface {
|
2021-10-09 18:09:54 +02:00
|
|
|
GetMeasures(string, string) (*MeasuresResponse, error)
|
2021-10-10 19:01:48 +02:00
|
|
|
GetPullRequestUrl(string, int64) string
|
|
|
|
GetPullRequest(string, int64) (*PullRequest, error)
|
2021-10-10 19:39:33 +02:00
|
|
|
ComposeGiteaComment(*CommentComposeData) (string, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type CommentComposeData struct {
|
|
|
|
Key string
|
|
|
|
PRName string
|
|
|
|
Url string
|
|
|
|
QualityGate string
|
2021-07-12 16:58:48 +02:00
|
|
|
}
|
|
|
|
|
2022-06-17 20:19:59 +02:00
|
|
|
type ClientInterface interface {
|
|
|
|
Do(req *http.Request) (*http.Response, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type BodyReader func(io.Reader) ([]byte, error)
|
|
|
|
type HttpRequest func(method string, target string, body io.Reader) (*http.Request, error)
|
|
|
|
|
2021-07-12 16:58:48 +02:00
|
|
|
type SonarQubeSdk struct {
|
2022-06-17 20:19:59 +02:00
|
|
|
client ClientInterface
|
|
|
|
bodyReader BodyReader
|
|
|
|
httpRequest HttpRequest
|
2022-07-12 12:26:10 +02:00
|
|
|
settings *settings.SonarQubeConfig
|
2021-07-12 16:58:48 +02:00
|
|
|
}
|
|
|
|
|
2021-10-10 19:01:48 +02:00
|
|
|
func (sdk *SonarQubeSdk) GetPullRequestUrl(project string, index int64) string {
|
2022-07-12 12:26:10 +02:00
|
|
|
return fmt.Sprintf("%s/dashboard?id=%s&pullRequest=%s", sdk.settings.Url, project, PRNameFromIndex(index))
|
2021-10-10 19:01:48 +02:00
|
|
|
}
|
|
|
|
|
2022-06-17 20:19:59 +02:00
|
|
|
func (sdk *SonarQubeSdk) fetchPullRequests(project string) (*PullsResponse, error) {
|
2022-07-12 12:26:10 +02:00
|
|
|
url := fmt.Sprintf("%s/api/project_pull_requests/list?project=%s", sdk.settings.Url, project)
|
2022-06-17 20:19:59 +02:00
|
|
|
request, err := sdk.httpRequest(http.MethodGet, url, nil)
|
2021-10-10 19:01:48 +02:00
|
|
|
if err != nil {
|
2022-06-17 20:19:59 +02:00
|
|
|
return nil, err
|
2021-10-10 19:01:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
response := &PullsResponse{}
|
2022-06-17 20:19:59 +02:00
|
|
|
err = retrieveDataFromApi(sdk, request, response)
|
2021-10-10 19:01:48 +02:00
|
|
|
if err != nil {
|
2022-06-17 20:19:59 +02:00
|
|
|
return nil, err
|
2021-10-10 19:01:48 +02:00
|
|
|
}
|
|
|
|
|
2022-06-17 20:19:59 +02:00
|
|
|
if len(response.Errors) != 0 {
|
|
|
|
return nil, fmt.Errorf("%s", response.Errors[0].Message)
|
|
|
|
}
|
|
|
|
|
|
|
|
return response, nil
|
2021-10-10 19:01:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sdk *SonarQubeSdk) GetPullRequest(project string, index int64) (*PullRequest, error) {
|
2022-06-17 20:19:59 +02:00
|
|
|
response, err := sdk.fetchPullRequests(project)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("fetching pull requests failed: %w", err)
|
2021-10-10 19:01:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
name := PRNameFromIndex(index)
|
|
|
|
pr := response.GetPullRequest(name)
|
|
|
|
if pr == nil {
|
|
|
|
return nil, fmt.Errorf("no pull request found with name '%s'", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
return pr, nil
|
|
|
|
}
|
|
|
|
|
2021-10-09 18:09:54 +02:00
|
|
|
func (sdk *SonarQubeSdk) GetMeasures(project string, branch string) (*MeasuresResponse, error) {
|
2022-07-12 12:26:10 +02:00
|
|
|
url := fmt.Sprintf("%s/api/measures/component?additionalFields=metrics&metricKeys=%s&component=%s&pullRequest=%s", sdk.settings.Url, settings.SonarQube.GetMetricsList(), project, branch)
|
2022-06-17 20:19:59 +02:00
|
|
|
request, err := sdk.httpRequest(http.MethodGet, url, nil)
|
2021-07-12 16:58:48 +02:00
|
|
|
if err != nil {
|
2022-06-17 20:19:59 +02:00
|
|
|
return nil, err
|
2021-10-09 18:09:54 +02:00
|
|
|
}
|
2021-07-12 16:58:48 +02:00
|
|
|
|
2021-10-09 18:09:54 +02:00
|
|
|
response := &MeasuresResponse{}
|
2022-06-17 20:19:59 +02:00
|
|
|
err = retrieveDataFromApi(sdk, request, response)
|
2021-10-09 18:09:54 +02:00
|
|
|
if err != nil {
|
2022-06-17 20:19:59 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(response.Errors) != 0 {
|
|
|
|
return nil, fmt.Errorf("%s", response.Errors[0].Message)
|
2021-10-09 18:09:54 +02:00
|
|
|
}
|
2021-07-12 16:58:48 +02:00
|
|
|
|
2021-10-09 18:09:54 +02:00
|
|
|
return response, nil
|
2021-07-12 16:58:48 +02:00
|
|
|
}
|
|
|
|
|
2021-10-10 19:39:33 +02:00
|
|
|
func (sdk *SonarQubeSdk) ComposeGiteaComment(data *CommentComposeData) (string, error) {
|
|
|
|
m, err := sdk.GetMeasures(data.Key, data.PRName)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error composing Gitea comment: %s", err.Error())
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
message := make([]string, 5)
|
|
|
|
message[0] = GetRenderedQualityGate(data.QualityGate)
|
|
|
|
message[1] = m.GetRenderedMarkdownTable()
|
2021-10-12 14:09:24 +02:00
|
|
|
message[2] = fmt.Sprintf(`See <a href="%s" target="_blank" rel="nofollow">SonarQube</a> for details.`, data.Url)
|
2021-10-10 19:39:33 +02:00
|
|
|
message[3] = "---"
|
|
|
|
message[4] = fmt.Sprintf("- If you want the bot to check again, post `%s`", actions.ActionReview)
|
|
|
|
|
|
|
|
return strings.Join(message, "\n\n"), nil
|
|
|
|
}
|
|
|
|
|
2021-07-12 16:58:48 +02:00
|
|
|
func (sdk *SonarQubeSdk) basicAuth() string {
|
2022-07-12 12:26:10 +02:00
|
|
|
auth := []byte(fmt.Sprintf("%s:", sdk.settings.Token.Value))
|
2021-07-12 16:58:48 +02:00
|
|
|
return fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString(auth))
|
|
|
|
}
|
|
|
|
|
2022-07-12 12:26:10 +02:00
|
|
|
func New(configuration *settings.SonarQubeConfig) *SonarQubeSdk {
|
2021-07-12 16:58:48 +02:00
|
|
|
return &SonarQubeSdk{
|
2022-06-17 20:19:59 +02:00
|
|
|
client: &http.Client{},
|
|
|
|
bodyReader: io.ReadAll,
|
|
|
|
httpRequest: http.NewRequest,
|
2022-07-12 12:26:10 +02:00
|
|
|
settings: configuration,
|
2021-07-12 16:58:48 +02:00
|
|
|
}
|
|
|
|
}
|