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]
 | 
						|
}
 |