123 lines
3.4 KiB
Go
123 lines
3.4 KiB
Go
package printers
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"github.com/golangci/golangci-lint/pkg/result"
|
|
)
|
|
|
|
// Field limits.
|
|
const (
|
|
smallLimit = 255
|
|
largeLimit = 4000
|
|
)
|
|
|
|
// TeamCity printer for TeamCity format.
|
|
type TeamCity struct {
|
|
w io.Writer
|
|
escaper *strings.Replacer
|
|
}
|
|
|
|
// NewTeamCity output format outputs issues according to TeamCity service message format.
|
|
func NewTeamCity(w io.Writer) *TeamCity {
|
|
return &TeamCity{
|
|
w: w,
|
|
// https://www.jetbrains.com/help/teamcity/service-messages.html#Escaped+Values
|
|
escaper: strings.NewReplacer(
|
|
"'", "|'",
|
|
"\n", "|n",
|
|
"\r", "|r",
|
|
"|", "||",
|
|
"[", "|[",
|
|
"]", "|]",
|
|
),
|
|
}
|
|
}
|
|
|
|
func (p *TeamCity) Print(issues []result.Issue) error {
|
|
uniqLinters := map[string]struct{}{}
|
|
|
|
for i := range issues {
|
|
issue := issues[i]
|
|
|
|
_, ok := uniqLinters[issue.FromLinter]
|
|
if !ok {
|
|
inspectionType := InspectionType{
|
|
id: issue.FromLinter,
|
|
name: issue.FromLinter,
|
|
description: issue.FromLinter,
|
|
category: "Golangci-lint reports",
|
|
}
|
|
|
|
_, err := inspectionType.Print(p.w, p.escaper)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uniqLinters[issue.FromLinter] = struct{}{}
|
|
}
|
|
|
|
instance := InspectionInstance{
|
|
typeID: issue.FromLinter,
|
|
message: issue.Text,
|
|
file: issue.FilePath(),
|
|
line: issue.Line(),
|
|
severity: issue.Severity,
|
|
}
|
|
|
|
_, err := instance.Print(p.w, p.escaper)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// InspectionType is the unique description of the conducted inspection. Each specific warning or
|
|
// an error in code (inspection instance) has an inspection type.
|
|
// https://www.jetbrains.com/help/teamcity/service-messages.html#Inspection+Type
|
|
type InspectionType struct {
|
|
id string // (mandatory) limited by 255 characters.
|
|
name string // (mandatory) limited by 255 characters.
|
|
description string // (mandatory) limited by 255 characters.
|
|
category string // (mandatory) limited by 4000 characters.
|
|
}
|
|
|
|
func (i InspectionType) Print(w io.Writer, escaper *strings.Replacer) (int, error) {
|
|
return fmt.Fprintf(w, "##teamcity[InspectionType id='%s' name='%s' description='%s' category='%s']\n",
|
|
limit(i.id, smallLimit), limit(i.name, smallLimit), limit(escaper.Replace(i.description), largeLimit), limit(i.category, smallLimit))
|
|
}
|
|
|
|
// InspectionInstance reports a specific defect, warning, error message.
|
|
// Includes location, description, and various optional and custom attributes.
|
|
// https://www.jetbrains.com/help/teamcity/service-messages.html#Inspection+Instance
|
|
type InspectionInstance struct {
|
|
typeID string // (mandatory) limited by 255 characters.
|
|
message string // (optional) limited by 4000 characters.
|
|
file string // (mandatory) file path limited by 4000 characters.
|
|
line int // (optional) line of the file.
|
|
severity string // (optional) any linter severity.
|
|
}
|
|
|
|
func (i InspectionInstance) Print(w io.Writer, replacer *strings.Replacer) (int, error) {
|
|
return fmt.Fprintf(w, "##teamcity[inspection typeId='%s' message='%s' file='%s' line='%d' SEVERITY='%s']\n",
|
|
limit(i.typeID, smallLimit),
|
|
limit(replacer.Replace(i.message), largeLimit),
|
|
limit(i.file, largeLimit),
|
|
i.line, strings.ToUpper(i.severity))
|
|
}
|
|
|
|
func limit(s string, max int) string {
|
|
var size, count int
|
|
for i := 0; i < max && count < len(s); i++ {
|
|
_, size = utf8.DecodeRuneInString(s[count:])
|
|
count += size
|
|
}
|
|
|
|
return s[:count]
|
|
}
|