Skip to content

Commit

Permalink
feat: adjust golangci-lint and send notifaction when unexpected messa…
Browse files Browse the repository at this point in the history
…ge existed
  • Loading branch information
CarlJi committed Jun 19, 2024
1 parent 18cd216 commit 560094d
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 43 deletions.
5 changes: 0 additions & 5 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ globalDefaultConfig: # global default settings, will be overridden by qbox org a
golangcilintConfig: "config/linters-config/.golangci.yml" # golangci-lint config file to use

customConfig: # custom config for specific orgs or repos
qbox: # github organization name
golangci-lint:
enable: true
args: ["run", "--allow-parallel-runners=true","--timeout=5m0s"]

qbox/net-cache:
golangci-lint:
enable: true
Expand Down
2 changes: 1 addition & 1 deletion deploy/reviewbot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ spec:
- command:
- /reviewbot
args:
- -log-level=0
- -log-level=1
- -webhook-secret=$(GITHUB_WEBHOOK_SECRET)
- -config=/etc/config/config.yaml
- -app-id=$(GITHUB_APP_ID)
Expand Down
3 changes: 3 additions & 0 deletions internal/linters/go/golangci_lint/golangci_lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ func golangciLintHandler(log *xlog.Logger, a linters.Agent) error {
if linters.IsEmpty(a.LinterConfig.Args...) {
// refer: https://github.com/qiniu/reviewbot/issues/146
a.LinterConfig.Args = append([]string{}, "run", "--timeout=5m0s", "--allow-parallel-runners=true")
// recommend to use the line-number format and disable the issued lines
// since these are more friendly to the reviewbot
a.LinterConfig.Args = append(a.LinterConfig.Args, "--out-format=line-number", "--print-issued-lines=false")
}

if a.LinterConfig.ConfigPath != "" {
Expand Down
60 changes: 37 additions & 23 deletions internal/linters/linters.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package linters

import (
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
Expand Down Expand Up @@ -200,7 +199,7 @@ func Report(log *xlog.Logger, a Agent, lintResults map[string][]LinterOutput) er
return err
}
log.Infof("[%s] create check run success, HTML_URL: %v", linterName, ch.GetHTMLURL())
if err := metric.SendAlertMessageIfNeeded(constructMKAlertMessage(linterName, a.PullRequestEvent.GetPullRequest().GetHTMLURL(), ch.GetHTMLURL(), lintResults)); err != nil {
if err := metric.NotifyWebhook(constructMessage(linterName, a.PullRequestEvent.GetPullRequest().GetHTMLURL(), ch.GetHTMLURL(), lintResults)); err != nil {

Check warning on line 202 in internal/linters/linters.go

View check run for this annotation

Codecov / codecov/patch

internal/linters/linters.go#L202

Added line #L202 was not covered by tests
log.Errorf("failed to send alert message: %v", err)
// just log the error, not return
}
Expand Down Expand Up @@ -241,7 +240,7 @@ func Report(log *xlog.Logger, a Agent, lintResults map[string][]LinterOutput) er
return err
}
log.Infof("[%s] add %d comments for this PR %d (%s) \n", linterName, len(addedCmts), num, orgRepo)
if err := metric.SendAlertMessageIfNeeded(constructMKAlertMessage(linterName, a.PullRequestEvent.GetPullRequest().GetHTMLURL(), addedCmts[0].GetHTMLURL(), lintResults)); err != nil {
if err := metric.NotifyWebhook(constructMessage(linterName, a.PullRequestEvent.GetPullRequest().GetHTMLURL(), addedCmts[0].GetHTMLURL(), lintResults)); err != nil {

Check warning on line 243 in internal/linters/linters.go

View check run for this annotation

Codecov / codecov/patch

internal/linters/linters.go#L243

Added line #L243 was not covered by tests
log.Errorf("failed to send alert message: %v", err)
// just log the error, not return
}
Expand All @@ -259,14 +258,15 @@ type LineParser func(line string) (*LinterOutput, error)
func Parse(log *xlog.Logger, output []byte, lineParser LineParser) (map[string][]LinterOutput, error) {
lines := strings.Split(string(output), "\n")

var unexpectedLines []string

Check warning on line 261 in internal/linters/linters.go

View check run for this annotation

Codecov / codecov/patch

internal/linters/linters.go#L261

Added line #L261 was not covered by tests
var result = make(map[string][]LinterOutput)
for _, line := range lines {
if line == "" {
continue
}
output, err := lineParser(line)
if err != nil {
log.Warnf("unexpected linter output: %v", line)
unexpectedLines = append(unexpectedLines, line)

Check warning on line 269 in internal/linters/linters.go

View check run for this annotation

Codecov / codecov/patch

internal/linters/linters.go#L269

Added line #L269 was not covered by tests
continue
}

Expand All @@ -292,6 +292,34 @@ func Parse(log *xlog.Logger, output []byte, lineParser LineParser) (map[string][
}
}

go func() {
var message string
for _, line := range unexpectedLines {

Check warning on line 297 in internal/linters/linters.go

View check run for this annotation

Codecov / codecov/patch

internal/linters/linters.go#L295-L297

Added lines #L295 - L297 were not covered by tests
// trim the message to avoid too long
// wework webhook only support 4096 characters for markdown message
// see https://open.work.weixin.qq.com/api/doc/90000/90136/91770
if len(message) > 3000 {
message += "..."
break

Check warning on line 303 in internal/linters/linters.go

View check run for this annotation

Codecov / codecov/patch

internal/linters/linters.go#L301-L303

Added lines #L301 - L303 were not covered by tests
}
message += line + "\n"

Check warning on line 305 in internal/linters/linters.go

View check run for this annotation

Codecov / codecov/patch

internal/linters/linters.go#L305

Added line #L305 was not covered by tests
}

if message != "" {
message = fmt.Sprintf("unexpected lines:\n%v", message)
log.Warnf(message)
var msg metric.MessageBody
msg.MsgType = "markdown"
msg.Markdown.Content = fmt.Sprintf("<font color=\"warning\">[ATTENTION PLEASE]</font> %v", message)

Check warning on line 313 in internal/linters/linters.go

View check run for this annotation

Codecov / codecov/patch

internal/linters/linters.go#L308-L313

Added lines #L308 - L313 were not covered by tests
// seed the alert message if the linter output is unexpected
// so that we can know the issue and fix it
if err := metric.NotifyWebhook(msg); err != nil {
log.Errorf("failed to send alert message: %v", err)

Check warning on line 317 in internal/linters/linters.go

View check run for this annotation

Codecov / codecov/patch

internal/linters/linters.go#L316-L317

Added lines #L316 - L317 were not covered by tests
// just log the error, not return
}
}
}()

return result, nil
}

Expand Down Expand Up @@ -373,9 +401,9 @@ func countLinterErrors(lintResults map[string][]LinterOutput) int {
return count
}

func constructMKAlertMessage(linter, pr, link string, linterResults map[string][]LinterOutput) string {
func constructMessage(linter, pr, link string, linterResults map[string][]LinterOutput) (msg metric.MessageBody) {
if len(linterResults) == 0 {
return ""
return
}

var message string
Expand All @@ -385,21 +413,7 @@ func constructMKAlertMessage(linter, pr, link string, linterResults map[string][
}
}

type mkMessage struct {
Msgtype string `json:"msgtype"`
Text struct {
Content string `json:"content"`
} `json:"text"`
}

var m mkMessage
m.Msgtype = "text"
m.Text.Content = fmt.Sprintf("Linter: %v \nPR: %v \nLink: %v \nDetails:\n%v\n", linter, pr, link, message)
b, err := json.Marshal(m)
if err != nil {
log.Errorf("failed to marshal message: %v", err)
return ""
}

return string(b)
msg.MsgType = "text"
msg.Text.Content = fmt.Sprintf("Linter: %v \nPR: %v \nLink: %v \nDetails:\n%v\n", linter, pr, link, message)
return
}
25 changes: 17 additions & 8 deletions internal/linters/linters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
package linters

Check warning on line 17 in internal/linters/linters_test.go

View check run for this annotation

qiniu-x / golangci-lint

internal/linters/linters_test.go#L17

package should be `linters_test` instead of `linters` (testpackage)

import (
"fmt"
"reflect"
"testing"

"github.com/qiniu/reviewbot/internal/metric"

Check warning on line 24 in internal/linters/linters_test.go

View check run for this annotation

qiniu-x / golangci-lint

internal/linters/linters_test.go#L24

import 'github.com/qiniu/reviewbot/internal/metric' is not allowed from list 'Main' (depguard)
)

func TestFormatStaticcheckLine(t *testing.T) {

Check warning on line 27 in internal/linters/linters_test.go

View check run for this annotation

qiniu-x / golangci-lint

internal/linters/linters_test.go#L27

calculated cyclomatic complexity for function TestFormatStaticcheckLine is 11, max is 10 (cyclop)
Expand Down Expand Up @@ -86,18 +90,18 @@ func TestIsEmpty(t *testing.T) {
}
}

func TestConstructMKAlertMessage(t *testing.T) {
func TestConstructMessage(t *testing.T) {
tcs := []struct {
Linter string
PR string
Link string
linterResults map[string][]LinterOutput
expected string
expected metric.MessageBody
}{
{
Linter: "golangci-lint",
PR: "",
Link: "",
PR: "1",
Link: "http://",
linterResults: map[string][]LinterOutput{
"cdn-admin.v2/client/dns/dnsapi.go": {
{

Check warning on line 107 in internal/linters/linters_test.go

View check run for this annotation

qiniu-x / golangci-lint

internal/linters/linters_test.go#L107

linters.LinterOutput is missing field StartLine (exhaustruct)
Expand All @@ -108,20 +112,25 @@ func TestConstructMKAlertMessage(t *testing.T) {
},
},
},
expected: `{"msgtype":"text","text":{"content":"Linter: golangci-lint \nPR: \nLink: \nDetails:\ncdn-admin.v2/client/dns/dnsapi.go:59:3: assignment to err\n\n"}}`,
expected: metric.MessageBody{

Check warning on line 115 in internal/linters/linters_test.go

View check run for this annotation

qiniu-x / golangci-lint

internal/linters/linters_test.go#L115

metric.MessageBody is missing field Markdown (exhaustruct)
MsgType: "text",
Text: metric.MsgContent{
Content: fmt.Sprintf("Linter: %v \nPR: %v \nLink: %v \nDetails:\n%v\n", "golangci-lint", "1", "http://", "cdn-admin.v2/client/dns/dnsapi.go:59:3: assignment to err\n"),
},
},
},
{
Linter: "golangci-lint",
PR: "",
Link: "",
linterResults: map[string][]LinterOutput{},
expected: "",
expected: metric.MessageBody{},

Check warning on line 127 in internal/linters/linters_test.go

View check run for this annotation

qiniu-x / golangci-lint

internal/linters/linters_test.go#L127

metric.MessageBody is missing fields MsgType, Text, Markdown (exhaustruct)
},
}

for _, tc := range tcs {
actual := constructMKAlertMessage(tc.Linter, tc.PR, tc.Link, tc.linterResults)
if actual != tc.expected {
actual := constructMessage(tc.Linter, tc.PR, tc.Link, tc.linterResults)
if !reflect.DeepEqual(tc.expected, actual) {
t.Errorf("expected: %v, got: %v", tc.expected, actual)
}
}
Expand Down
33 changes: 27 additions & 6 deletions internal/metric/metrics.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package metric

import (
"encoding/json"
"fmt"
"net/http"
"os"
Expand All @@ -27,22 +28,42 @@ func IncIssueCounter(repo, linter, pull_request, commit string, count float64) {
issueCounter.WithLabelValues(repo, linter, pull_request, commit).Add(count)
}

// SendAlertMessageIfNeeded sends alert message to wework group if needed
type MessageBody struct {
MsgType string `json:"msgtype"`
Text MsgContent `json:"text,omitempty"`
Markdown MsgContent `json:"markdown,omitempty"`
}

type MsgContent struct {
Content string `json:"content"`
}

// NotifyWebhook sends message to wework group
// refer: https://developer.work.weixin.qq.com/document/path/91770
// The mkMessage is the markdown format message
func SendAlertMessageIfNeeded(message string) error {
if WEWORK_WEBHOOK == "" || message == "" {
func NotifyWebhook(message MessageBody) error {
if WEWORK_WEBHOOK == "" {
return nil
}

resp, err := http.DefaultClient.Post(WEWORK_WEBHOOK, "application/json", strings.NewReader(message))
body, err := json.Marshal(message)
if err != nil {
return err
}

resp, err := http.DefaultClient.Post(WEWORK_WEBHOOK, "application/json", strings.NewReader(string(body)))

Check warning on line 53 in internal/metric/metrics.go

View check run for this annotation

qiniu-x / golangci-lint

internal/metric/metrics.go#L53

(*net/http.Client).Post must not be called (noctx)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("send alert message failed: %v", resp)
return fmt.Errorf("send message failed: %v", resp)

Check warning on line 60 in internal/metric/metrics.go

View check run for this annotation

qiniu-x / golangci-lint

internal/metric/metrics.go#L60

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"send message failed: %v\", resp)" (err113)
}

errCode := resp.Header.Get("Error-Code")
if errCode != "0" {
return fmt.Errorf("send message failed, errCode: %v, errMsg: %v", errCode, resp.Header.Get("Error-Msg"))

Check warning on line 65 in internal/metric/metrics.go

View check run for this annotation

qiniu-x / golangci-lint

internal/metric/metrics.go#L65

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"send message failed, errCode: %v, errMsg: %v\", errCode, resp.Header.Get(\"Error-Msg\"))" (err113)
}

return nil
}

0 comments on commit 560094d

Please sign in to comment.