diff --git a/README.md b/README.md index 0d959130..1ca41e10 100644 --- a/README.md +++ b/README.md @@ -511,7 +511,7 @@ Usage: golangci-lint run [flags] Flags: - --out-format string Format of output: colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml (default "colored-line-number") + --out-format string Format of output: colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions (default "colored-line-number") --print-issued-lines Print lines of code with issue (default true) --print-linter-name Print linter name in issue line (default true) --uniq-by-line Make issues output unique by line (default true) diff --git a/pkg/commands/run.go b/pkg/commands/run.go index b306802a..b805995e 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -396,6 +396,8 @@ func (e *Executor) createPrinter() (printers.Printer, error) { p = printers.NewCodeClimate() case config.OutFormatJunitXML: p = printers.NewJunitXML() + case config.OutFormatGithubActions: + p = printers.NewGithub() default: return nil, fmt.Errorf("unknown output format %s", format) } diff --git a/pkg/config/config.go b/pkg/config/config.go index b0df8ecf..0c440ae9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -15,6 +15,7 @@ const ( OutFormatCheckstyle = "checkstyle" OutFormatCodeClimate = "code-climate" OutFormatJunitXML = "junit-xml" + OutFormatGithubActions = "github-actions" ) var OutFormats = []string{ @@ -25,6 +26,7 @@ var OutFormats = []string{ OutFormatCheckstyle, OutFormatCodeClimate, OutFormatJunitXML, + OutFormatGithubActions, } type ExcludePattern struct { diff --git a/pkg/printers/github.go b/pkg/printers/github.go new file mode 100644 index 00000000..fa11a283 --- /dev/null +++ b/pkg/printers/github.go @@ -0,0 +1,39 @@ +package printers + +import ( + "context" + "fmt" + + "github.com/golangci/golangci-lint/pkg/logutils" + "github.com/golangci/golangci-lint/pkg/result" +) + +type github struct { +} + +// Github output format outputs issues according to Github actions format: +// https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message +func NewGithub() Printer { + return &github{} +} + +// print each line as: ::error file=app.js,line=10,col=15::Something went wrong +func formatIssueAsGithub(issue *result.Issue) string { + ret := fmt.Sprintf("::error file=%s,line=%d", issue.FilePath(), issue.Line()) + if issue.Pos.Column != 0 { + ret += fmt.Sprintf(",col=%d", issue.Pos.Column) + } + + ret += fmt.Sprintf("::%s (%s)", issue.Text, issue.FromLinter) + return ret +} + +func (g *github) Print(_ context.Context, issues []result.Issue) error { + for ind := range issues { + _, err := fmt.Fprintln(logutils.StdOut, formatIssueAsGithub(&issues[ind])) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/printers/github_test.go b/pkg/printers/github_test.go new file mode 100644 index 00000000..0ab79bb8 --- /dev/null +++ b/pkg/printers/github_test.go @@ -0,0 +1,27 @@ +package printers + +import ( + "go/token" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/golangci/golangci-lint/pkg/result" +) + +func TestFormatGithubIssue(t *testing.T) { + sampleIssue := result.Issue{ + FromLinter: "sample-linter", + Text: "some issue", + Pos: token.Position{ + Filename: "path/to/file.go", + Offset: 2, + Line: 10, + Column: 4, + }, + } + require.Equal(t, "::error file=path/to/file.go,line=10,col=4::some issue (sample-linter)", formatIssueAsGithub(&sampleIssue)) + + sampleIssue.Pos.Column = 0 + require.Equal(t, "::error file=path/to/file.go,line=10::some issue (sample-linter)", formatIssueAsGithub(&sampleIssue)) +}