From 63104647b0706d5e7f43ec9a6d4392ba74b734c6 Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Tue, 30 Nov 2021 09:07:13 +0200 Subject: [PATCH 1/6] Add support for multiple outputs Outputs can be specified using `--out-format` by separating them by comma. Support providing file path by separating format name and path by colon symbol `:`. --- pkg/commands/run.go | 69 ++++++++++++++++++++++++++++--------- pkg/printers/checkstyle.go | 16 +++++---- pkg/printers/codeclimate.go | 11 +++--- pkg/printers/github.go | 13 ++++--- pkg/printers/html.go | 16 +++++---- pkg/printers/json.go | 8 +++-- pkg/printers/junitxml.go | 13 ++++--- pkg/printers/tab.go | 6 ++-- pkg/printers/text.go | 11 +++--- test/linters_test.go | 17 +++++++++ 10 files changed, 128 insertions(+), 52 deletions(-) diff --git a/pkg/commands/run.go b/pkg/commands/run.go index ac2e7046f50c..9f1374d97aae 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -381,6 +381,8 @@ func (e *Executor) setExitCodeIfIssuesFound(issues []result.Issue) { } } +const defaultFileMode = 0644 + func (e *Executor) runAndPrint(ctx context.Context, args []string) error { if err := e.goenv.Discover(ctx); err != nil { e.log.Warnf("Failed to discover go env: %s", err) @@ -400,44 +402,77 @@ func (e *Executor) runAndPrint(ctx context.Context, args []string) error { return err // XXX: don't loose type } - p, err := e.createPrinter() - if err != nil { - return err + formats := strings.Split(e.cfg.Output.Format, ",") + for _, format := range formats { + out := strings.SplitN(format, ":", 2) + if len(out) < 2 { + out = append(out, "") + } + w, shouldClose, err := e.createWriter(out[1]) + if err != nil { + return fmt.Errorf("can't create output for %s: %w", out[1], err) + } + + p, err := e.createPrinter(out[0], w) + if err != nil { + if file, ok := w.(io.Closer); shouldClose && ok { + file.Close() + } + return err + } + if err = p.Print(ctx, issues); err != nil { + if file, ok := w.(io.Closer); shouldClose && ok { + file.Close() + } + return fmt.Errorf("can't print %d issues: %s", len(issues), err) + } + if file, ok := w.(io.Closer); shouldClose && ok { + file.Close() + } } e.setExitCodeIfIssuesFound(issues) - if err = p.Print(ctx, issues); err != nil { - return fmt.Errorf("can't print %d issues: %s", len(issues), err) - } - e.fileCache.PrintStats(e.log) return nil } -func (e *Executor) createPrinter() (printers.Printer, error) { +func (e *Executor) createWriter(path string) (io.Writer, bool, error) { + if path == "stdout" { + return logutils.StdOut, false, nil + } + if path == "stderr" { + return logutils.StdErr, false, nil + } + f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, defaultFileMode) + if err != nil { + return nil, false, err + } + return f, true, nil +} + +func (e *Executor) createPrinter(format string, w io.Writer) (printers.Printer, error) { var p printers.Printer - format := e.cfg.Output.Format switch format { case config.OutFormatJSON: - p = printers.NewJSON(&e.reportData) + p = printers.NewJSON(&e.reportData, w) case config.OutFormatColoredLineNumber, config.OutFormatLineNumber: p = printers.NewText(e.cfg.Output.PrintIssuedLine, format == config.OutFormatColoredLineNumber, e.cfg.Output.PrintLinterName, - e.log.Child("text_printer")) + e.log.Child("text_printer"), w) case config.OutFormatTab: - p = printers.NewTab(e.cfg.Output.PrintLinterName, e.log.Child("tab_printer")) + p = printers.NewTab(e.cfg.Output.PrintLinterName, e.log.Child("tab_printer"), w) case config.OutFormatCheckstyle: - p = printers.NewCheckstyle() + p = printers.NewCheckstyle(w) case config.OutFormatCodeClimate: - p = printers.NewCodeClimate() + p = printers.NewCodeClimate(w) case config.OutFormatHTML: - p = printers.NewHTML() + p = printers.NewHTML(w) case config.OutFormatJunitXML: - p = printers.NewJunitXML() + p = printers.NewJunitXML(w) case config.OutFormatGithubActions: - p = printers.NewGithub() + p = printers.NewGithub(w) default: return nil, fmt.Errorf("unknown output format %s", format) } diff --git a/pkg/printers/checkstyle.go b/pkg/printers/checkstyle.go index c5b948a98d29..337c68704ee6 100644 --- a/pkg/printers/checkstyle.go +++ b/pkg/printers/checkstyle.go @@ -4,10 +4,10 @@ import ( "context" "encoding/xml" "fmt" + "io" "github.com/go-xmlfmt/xmlfmt" - "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/pkg/result" ) @@ -32,13 +32,17 @@ type checkstyleError struct { const defaultCheckstyleSeverity = "error" -type Checkstyle struct{} +type Checkstyle struct { + w io.Writer +} -func NewCheckstyle() *Checkstyle { - return &Checkstyle{} +func NewCheckstyle(w io.Writer) *Checkstyle { + return &Checkstyle{ + w: w, + } } -func (Checkstyle) Print(ctx context.Context, issues []result.Issue) error { +func (p Checkstyle) Print(ctx context.Context, issues []result.Issue) error { out := checkstyleOutput{ Version: "5.0", } @@ -82,6 +86,6 @@ func (Checkstyle) Print(ctx context.Context, issues []result.Issue) error { return err } - fmt.Fprintf(logutils.StdOut, "%s%s\n", xml.Header, xmlfmt.FormatXML(string(data), "", " ")) + fmt.Fprintf(p.w, "%s%s\n", xml.Header, xmlfmt.FormatXML(string(data), "", " ")) return nil } diff --git a/pkg/printers/codeclimate.go b/pkg/printers/codeclimate.go index d4e5b5e058a4..c9ca105f7382 100644 --- a/pkg/printers/codeclimate.go +++ b/pkg/printers/codeclimate.go @@ -4,8 +4,8 @@ import ( "context" "encoding/json" "fmt" + "io" - "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/pkg/result" ) @@ -24,10 +24,13 @@ type CodeClimateIssue struct { } type CodeClimate struct { + w io.Writer } -func NewCodeClimate() *CodeClimate { - return &CodeClimate{} +func NewCodeClimate(w io.Writer) *CodeClimate { + return &CodeClimate{ + w: w, + } } func (p CodeClimate) Print(ctx context.Context, issues []result.Issue) error { @@ -52,6 +55,6 @@ func (p CodeClimate) Print(ctx context.Context, issues []result.Issue) error { return err } - fmt.Fprint(logutils.StdOut, string(outputJSON)) + fmt.Fprint(p.w, string(outputJSON)) return nil } diff --git a/pkg/printers/github.go b/pkg/printers/github.go index c7186ac273ed..9ad9bd688dab 100644 --- a/pkg/printers/github.go +++ b/pkg/printers/github.go @@ -3,20 +3,23 @@ package printers import ( "context" "fmt" + "io" - "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/pkg/result" ) type github struct { + w io.Writer } const defaultGithubSeverity = "error" // NewGithub 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{} +func NewGithub(w io.Writer) Printer { + return &github{ + w: w, + } } // print each line as: ::error file=app.js,line=10,col=15::Something went wrong @@ -35,9 +38,9 @@ func formatIssueAsGithub(issue *result.Issue) string { return ret } -func (g *github) Print(_ context.Context, issues []result.Issue) error { +func (p *github) Print(_ context.Context, issues []result.Issue) error { for ind := range issues { - _, err := fmt.Fprintln(logutils.StdOut, formatIssueAsGithub(&issues[ind])) + _, err := fmt.Fprintln(p.w, formatIssueAsGithub(&issues[ind])) if err != nil { return err } diff --git a/pkg/printers/html.go b/pkg/printers/html.go index 65ab753bd512..ac4a01c79c18 100644 --- a/pkg/printers/html.go +++ b/pkg/printers/html.go @@ -4,9 +4,9 @@ import ( "context" "fmt" "html/template" + "io" "strings" - "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/pkg/result" ) @@ -123,13 +123,17 @@ type htmlIssue struct { Code string } -type HTML struct{} +type HTML struct { + w io.Writer +} -func NewHTML() *HTML { - return &HTML{} +func NewHTML(w io.Writer) *HTML { + return &HTML{ + w: w, + } } -func (h HTML) Print(_ context.Context, issues []result.Issue) error { +func (p HTML) Print(_ context.Context, issues []result.Issue) error { var htmlIssues []htmlIssue for i := range issues { @@ -151,5 +155,5 @@ func (h HTML) Print(_ context.Context, issues []result.Issue) error { return err } - return t.Execute(logutils.StdOut, struct{ Issues []htmlIssue }{Issues: htmlIssues}) + return t.Execute(p.w, struct{ Issues []htmlIssue }{Issues: htmlIssues}) } diff --git a/pkg/printers/json.go b/pkg/printers/json.go index d68b82c2f3bf..f2adbec35bf7 100644 --- a/pkg/printers/json.go +++ b/pkg/printers/json.go @@ -4,19 +4,21 @@ import ( "context" "encoding/json" "fmt" + "io" - "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/pkg/report" "github.com/golangci/golangci-lint/pkg/result" ) type JSON struct { rd *report.Data + w io.Writer } -func NewJSON(rd *report.Data) *JSON { +func NewJSON(rd *report.Data, w io.Writer) *JSON { return &JSON{ rd: rd, + w: w, } } @@ -39,6 +41,6 @@ func (p JSON) Print(ctx context.Context, issues []result.Issue) error { return err } - fmt.Fprint(logutils.StdOut, string(outputJSON)) + fmt.Fprint(p.w, string(outputJSON)) return nil } diff --git a/pkg/printers/junitxml.go b/pkg/printers/junitxml.go index 9277cd66f2fe..c3032bb7469c 100644 --- a/pkg/printers/junitxml.go +++ b/pkg/printers/junitxml.go @@ -3,9 +3,9 @@ package printers import ( "context" "encoding/xml" + "io" "strings" - "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/pkg/result" ) @@ -35,13 +35,16 @@ type failureXML struct { } type JunitXML struct { + w io.Writer } -func NewJunitXML() *JunitXML { - return &JunitXML{} +func NewJunitXML(w io.Writer) *JunitXML { + return &JunitXML{ + w: w, + } } -func (JunitXML) Print(ctx context.Context, issues []result.Issue) error { +func (p JunitXML) Print(ctx context.Context, issues []result.Issue) error { suites := make(map[string]testSuiteXML) // use a map to group by file for ind := range issues { @@ -70,7 +73,7 @@ func (JunitXML) Print(ctx context.Context, issues []result.Issue) error { res.TestSuites = append(res.TestSuites, val) } - enc := xml.NewEncoder(logutils.StdOut) + enc := xml.NewEncoder(p.w) enc.Indent("", " ") if err := enc.Encode(res); err != nil { return err diff --git a/pkg/printers/tab.go b/pkg/printers/tab.go index d3cdce673dd8..4a126bde6153 100644 --- a/pkg/printers/tab.go +++ b/pkg/printers/tab.go @@ -15,12 +15,14 @@ import ( type Tab struct { printLinterName bool log logutils.Log + w io.Writer } -func NewTab(printLinterName bool, log logutils.Log) *Tab { +func NewTab(printLinterName bool, log logutils.Log, w io.Writer) *Tab { return &Tab{ printLinterName: printLinterName, log: log, + w: w, } } @@ -30,7 +32,7 @@ func (p Tab) SprintfColored(ca color.Attribute, format string, args ...interface } func (p *Tab) Print(ctx context.Context, issues []result.Issue) error { - w := tabwriter.NewWriter(logutils.StdOut, 0, 0, 2, ' ', 0) + w := tabwriter.NewWriter(p.w, 0, 0, 2, ' ', 0) for i := range issues { p.printIssue(&issues[i], w) diff --git a/pkg/printers/text.go b/pkg/printers/text.go index 1814528884c4..c8960e0e9e00 100644 --- a/pkg/printers/text.go +++ b/pkg/printers/text.go @@ -3,6 +3,7 @@ package printers import ( "context" "fmt" + "io" "strings" "github.com/fatih/color" @@ -17,14 +18,16 @@ type Text struct { printLinterName bool log logutils.Log + w io.Writer } -func NewText(printIssuedLine, useColors, printLinterName bool, log logutils.Log) *Text { +func NewText(printIssuedLine, useColors, printLinterName bool, log logutils.Log, w io.Writer) *Text { return &Text{ printIssuedLine: printIssuedLine, useColors: useColors, printLinterName: printLinterName, log: log, + w: w, } } @@ -61,12 +64,12 @@ func (p Text) printIssue(i *result.Issue) { if i.Pos.Column != 0 { pos += fmt.Sprintf(":%d", i.Pos.Column) } - fmt.Fprintf(logutils.StdOut, "%s: %s\n", pos, text) + fmt.Fprintf(p.w, "%s: %s\n", pos, text) } func (p Text) printSourceCode(i *result.Issue) { for _, line := range i.SourceLines { - fmt.Fprintln(logutils.StdOut, line) + fmt.Fprintln(p.w, line) } } @@ -87,5 +90,5 @@ func (p Text) printUnderLinePointer(i *result.Issue) { } } - fmt.Fprintf(logutils.StdOut, "%s%s\n", string(prefixRunes), p.SprintfColored(color.FgYellow, "^")) + fmt.Fprintf(p.w, "%s%s\n", string(prefixRunes), p.SprintfColored(color.FgYellow, "^")) } diff --git a/test/linters_test.go b/test/linters_test.go index 4431a2882122..c5be4856a5a4 100644 --- a/test/linters_test.go +++ b/test/linters_test.go @@ -97,6 +97,23 @@ func TestGciLocal(t *testing.T) { ExpectHasIssue("testdata/gci/gci.go:7: File is not `gci`-ed") } +func TestMultipleOutputs(t *testing.T) { + sourcePath := filepath.Join(testdataDir, "gci", "gci.go") + args := []string{ + "--disable-all", "--print-issued-lines=false", "--print-linter-name=false", "--out-format=line-number,json", + sourcePath, + } + rc := extractRunContextFromComments(t, sourcePath) + args = append(args, rc.args...) + + cfg, err := yaml.Marshal(rc.config) + require.NoError(t, err) + + testshared.NewLintRunner(t).RunWithYamlConfig(string(cfg), args...). + ExpectHasIssue("testdata/gci/gci.go:7: File is not `gci`-ed"). + ExpectOutputContains(`"Issues":[`) +} + func saveConfig(t *testing.T, cfg map[string]interface{}) (cfgPath string, finishFunc func()) { f, err := os.CreateTemp("", "golangci_lint_test") require.NoError(t, err) From 38ff2e70223ac4b7f19fc8269c8aab9cbc5dce5e Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Tue, 30 Nov 2021 09:16:54 +0200 Subject: [PATCH 2/6] Update documentation --- .golangci.example.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.golangci.example.yml b/.golangci.example.yml index 484f7dfbef25..cdfcdc9ac2ac 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -62,6 +62,10 @@ run: output: # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions # default is "colored-line-number" + # multiple can be specified by separating them by comma, output can be provided + # for each of them by separating format name and path by colon symbol. Output path can + # be either `stdout`, `stderr` or path to the file to write to. + # Example "checkstyle:report.json,colored-line-number" format: colored-line-number # print lines of code with issue, default is true From 5761a803e03f3feed1674b278759d8ea2671cb38 Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Tue, 30 Nov 2021 10:12:23 +0200 Subject: [PATCH 3/6] Fix default output --- pkg/commands/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/commands/run.go b/pkg/commands/run.go index 9f1374d97aae..98a16963645e 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -439,7 +439,7 @@ func (e *Executor) runAndPrint(ctx context.Context, args []string) error { } func (e *Executor) createWriter(path string) (io.Writer, bool, error) { - if path == "stdout" { + if path == "" || path == "stdout" { return logutils.StdOut, false, nil } if path == "stderr" { From ccd778648f9e78cd54ab96ebed2f41460d333f54 Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Wed, 1 Dec 2021 01:06:03 +0200 Subject: [PATCH 4/6] Add tests for stderr and file outputs --- test/linters_test.go | 50 ++++++++++++++++++++++++++++++++++- test/testshared/testshared.go | 5 ++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/test/linters_test.go b/test/linters_test.go index c5be4856a5a4..6676b11c24de 100644 --- a/test/linters_test.go +++ b/test/linters_test.go @@ -2,6 +2,8 @@ package test import ( "bufio" + "fmt" + "io/ioutil" "os" "os/exec" "path/filepath" @@ -100,7 +102,7 @@ func TestGciLocal(t *testing.T) { func TestMultipleOutputs(t *testing.T) { sourcePath := filepath.Join(testdataDir, "gci", "gci.go") args := []string{ - "--disable-all", "--print-issued-lines=false", "--print-linter-name=false", "--out-format=line-number,json", + "--disable-all", "--print-issued-lines=false", "--print-linter-name=false", "--out-format=line-number,json:stdout", sourcePath, } rc := extractRunContextFromComments(t, sourcePath) @@ -114,6 +116,52 @@ func TestMultipleOutputs(t *testing.T) { ExpectOutputContains(`"Issues":[`) } +func TestStderrOutput(t *testing.T) { + sourcePath := filepath.Join(testdataDir, "gci", "gci.go") + args := []string{ + "--disable-all", "--print-issued-lines=false", "--print-linter-name=false", "--out-format=line-number,json:stderr", + sourcePath, + } + rc := extractRunContextFromComments(t, sourcePath) + args = append(args, rc.args...) + + cfg, err := yaml.Marshal(rc.config) + require.NoError(t, err) + + testshared.NewLintRunner(t).RunWithYamlConfig(string(cfg), args...). + ExpectHasIssue("testdata/gci/gci.go:7: File is not `gci`-ed"). + ExpectOutputContains(`"Issues":[`) +} + +func TestFileOutput(t *testing.T) { + f, err := os.CreateTemp("", "golangci_lint_test_result") + require.NoError(t, err) + f.Close() + + resultPath := f.Name() + defer os.Remove(resultPath) + + sourcePath := filepath.Join(testdataDir, "gci", "gci.go") + args := []string{ + "--disable-all", "--print-issued-lines=false", "--print-linter-name=false", + fmt.Sprintf("--out-format=json:%s,line-number", resultPath), + sourcePath, + } + rc := extractRunContextFromComments(t, sourcePath) + args = append(args, rc.args...) + + cfg, err := yaml.Marshal(rc.config) + require.NoError(t, err) + + testshared.NewLintRunner(t).RunWithYamlConfig(string(cfg), args...). + ExpectHasIssue("testdata/gci/gci.go:7: File is not `gci`-ed"). + ExpectOutputNotContains(`"Issues":[`) + + b, err := ioutil.ReadFile(resultPath) + require.NoError(t, err) + require.Contains(t, string(b), `"Issues":[`) +} + func saveConfig(t *testing.T, cfg map[string]interface{}) (cfgPath string, finishFunc func()) { f, err := os.CreateTemp("", "golangci_lint_test") require.NoError(t, err) diff --git a/test/testshared/testshared.go b/test/testshared/testshared.go index 0d1f91c01dd6..fa70c23131ae 100644 --- a/test/testshared/testshared.go +++ b/test/testshared/testshared.go @@ -76,6 +76,11 @@ func (r *RunResult) ExpectOutputContains(s string) *RunResult { return r } +func (r *RunResult) ExpectOutputNotContains(s string) *RunResult { + assert.NotContains(r.t, r.output, s, "exit code is %d", r.exitCode) + return r +} + func (r *RunResult) ExpectOutputEq(s string) *RunResult { assert.Equal(r.t, s, r.output, "exit code is %d", r.exitCode) return r From da5c5954ad49a5246f6a7ba613c3182ab25cdba4 Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Wed, 1 Dec 2021 10:29:24 +0200 Subject: [PATCH 5/6] Fix lint and use test tempdir for output file --- test/linters_test.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/test/linters_test.go b/test/linters_test.go index 6676b11c24de..bb19e212a52c 100644 --- a/test/linters_test.go +++ b/test/linters_test.go @@ -3,9 +3,9 @@ package test import ( "bufio" "fmt" - "io/ioutil" "os" "os/exec" + "path" "path/filepath" "strings" "testing" @@ -134,12 +134,7 @@ func TestStderrOutput(t *testing.T) { } func TestFileOutput(t *testing.T) { - f, err := os.CreateTemp("", "golangci_lint_test_result") - require.NoError(t, err) - f.Close() - - resultPath := f.Name() - defer os.Remove(resultPath) + resultPath := path.Join(t.TempDir(), "golangci_lint_test_result") sourcePath := filepath.Join(testdataDir, "gci", "gci.go") args := []string{ @@ -157,7 +152,7 @@ func TestFileOutput(t *testing.T) { ExpectHasIssue("testdata/gci/gci.go:7: File is not `gci`-ed"). ExpectOutputNotContains(`"Issues":[`) - b, err := ioutil.ReadFile(resultPath) + b, err := os.ReadFile(resultPath) require.NoError(t, err) require.Contains(t, string(b), `"Issues":[`) } From fefa1463ee0bcbbd7060ee495fe702d5f41642a1 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Tue, 4 Jan 2022 17:55:09 +0100 Subject: [PATCH 6/6] review --- .golangci.example.yml | 4 +-- pkg/commands/run.go | 50 +++++++++++++++++++++++-------------- pkg/printers/checkstyle.go | 10 +++++--- pkg/printers/codeclimate.go | 9 ++++--- pkg/printers/github.go | 4 +-- pkg/printers/html.go | 4 +-- pkg/printers/json.go | 9 +------ pkg/printers/junitxml.go | 4 +-- 8 files changed, 48 insertions(+), 46 deletions(-) diff --git a/.golangci.example.yml b/.golangci.example.yml index cdfcdc9ac2ac..95abb3c48087 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -63,8 +63,8 @@ output: # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions # default is "colored-line-number" # multiple can be specified by separating them by comma, output can be provided - # for each of them by separating format name and path by colon symbol. Output path can - # be either `stdout`, `stderr` or path to the file to write to. + # for each of them by separating format name and path by colon symbol. + # Output path can be either `stdout`, `stderr` or path to the file to write to. # Example "checkstyle:report.json,colored-line-number" format: colored-line-number diff --git a/pkg/commands/run.go b/pkg/commands/run.go index 98a16963645e..f75fa82f3bd3 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -26,6 +26,8 @@ import ( "github.com/golangci/golangci-lint/pkg/result/processors" ) +const defaultFileMode = 0644 + func getDefaultIssueExcludeHelp() string { parts := []string{"Use or not use default excludes:"} for _, ep := range config.DefaultExcludePatterns { @@ -381,8 +383,6 @@ func (e *Executor) setExitCodeIfIssuesFound(issues []result.Issue) { } } -const defaultFileMode = 0644 - func (e *Executor) runAndPrint(ctx context.Context, args []string) error { if err := e.goenv.Discover(ctx); err != nil { e.log.Warnf("Failed to discover go env: %s", err) @@ -408,27 +408,11 @@ func (e *Executor) runAndPrint(ctx context.Context, args []string) error { if len(out) < 2 { out = append(out, "") } - w, shouldClose, err := e.createWriter(out[1]) - if err != nil { - return fmt.Errorf("can't create output for %s: %w", out[1], err) - } - p, err := e.createPrinter(out[0], w) + err := e.printReports(ctx, issues, out[1], out[0]) if err != nil { - if file, ok := w.(io.Closer); shouldClose && ok { - file.Close() - } return err } - if err = p.Print(ctx, issues); err != nil { - if file, ok := w.(io.Closer); shouldClose && ok { - file.Close() - } - return fmt.Errorf("can't print %d issues: %s", len(issues), err) - } - if file, ok := w.(io.Closer); shouldClose && ok { - file.Close() - } } e.setExitCodeIfIssuesFound(issues) @@ -438,6 +422,34 @@ func (e *Executor) runAndPrint(ctx context.Context, args []string) error { return nil } +func (e *Executor) printReports(ctx context.Context, issues []result.Issue, path, format string) error { + w, shouldClose, err := e.createWriter(path) + if err != nil { + return fmt.Errorf("can't create output for %s: %w", path, err) + } + + p, err := e.createPrinter(format, w) + if err != nil { + if file, ok := w.(io.Closer); shouldClose && ok { + _ = file.Close() + } + return err + } + + if err = p.Print(ctx, issues); err != nil { + if file, ok := w.(io.Closer); shouldClose && ok { + _ = file.Close() + } + return fmt.Errorf("can't print %d issues: %s", len(issues), err) + } + + if file, ok := w.(io.Closer); shouldClose && ok { + _ = file.Close() + } + + return nil +} + func (e *Executor) createWriter(path string) (io.Writer, bool, error) { if path == "" || path == "stdout" { return logutils.StdOut, false, nil diff --git a/pkg/printers/checkstyle.go b/pkg/printers/checkstyle.go index 337c68704ee6..3cd1fa4cf600 100644 --- a/pkg/printers/checkstyle.go +++ b/pkg/printers/checkstyle.go @@ -37,9 +37,7 @@ type Checkstyle struct { } func NewCheckstyle(w io.Writer) *Checkstyle { - return &Checkstyle{ - w: w, - } + return &Checkstyle{w: w} } func (p Checkstyle) Print(ctx context.Context, issues []result.Issue) error { @@ -86,6 +84,10 @@ func (p Checkstyle) Print(ctx context.Context, issues []result.Issue) error { return err } - fmt.Fprintf(p.w, "%s%s\n", xml.Header, xmlfmt.FormatXML(string(data), "", " ")) + _, err = fmt.Fprintf(p.w, "%s%s\n", xml.Header, xmlfmt.FormatXML(string(data), "", " ")) + if err != nil { + return err + } + return nil } diff --git a/pkg/printers/codeclimate.go b/pkg/printers/codeclimate.go index c9ca105f7382..8127632e74d6 100644 --- a/pkg/printers/codeclimate.go +++ b/pkg/printers/codeclimate.go @@ -28,9 +28,7 @@ type CodeClimate struct { } func NewCodeClimate(w io.Writer) *CodeClimate { - return &CodeClimate{ - w: w, - } + return &CodeClimate{w: w} } func (p CodeClimate) Print(ctx context.Context, issues []result.Issue) error { @@ -55,6 +53,9 @@ func (p CodeClimate) Print(ctx context.Context, issues []result.Issue) error { return err } - fmt.Fprint(p.w, string(outputJSON)) + _, err = fmt.Fprint(p.w, string(outputJSON)) + if err != nil { + return err + } return nil } diff --git a/pkg/printers/github.go b/pkg/printers/github.go index 9ad9bd688dab..6a4d05d46f3b 100644 --- a/pkg/printers/github.go +++ b/pkg/printers/github.go @@ -17,9 +17,7 @@ const defaultGithubSeverity = "error" // NewGithub 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(w io.Writer) Printer { - return &github{ - w: w, - } + return &github{w: w} } // print each line as: ::error file=app.js,line=10,col=15::Something went wrong diff --git a/pkg/printers/html.go b/pkg/printers/html.go index ac4a01c79c18..3d82d7d8bd7f 100644 --- a/pkg/printers/html.go +++ b/pkg/printers/html.go @@ -128,9 +128,7 @@ type HTML struct { } func NewHTML(w io.Writer) *HTML { - return &HTML{ - w: w, - } + return &HTML{w: w} } func (p HTML) Print(_ context.Context, issues []result.Issue) error { diff --git a/pkg/printers/json.go b/pkg/printers/json.go index f2adbec35bf7..cfef51f587fb 100644 --- a/pkg/printers/json.go +++ b/pkg/printers/json.go @@ -3,7 +3,6 @@ package printers import ( "context" "encoding/json" - "fmt" "io" "github.com/golangci/golangci-lint/pkg/report" @@ -36,11 +35,5 @@ func (p JSON) Print(ctx context.Context, issues []result.Issue) error { res.Issues = []result.Issue{} } - outputJSON, err := json.Marshal(res) - if err != nil { - return err - } - - fmt.Fprint(p.w, string(outputJSON)) - return nil + return json.NewEncoder(p.w).Encode(res) } diff --git a/pkg/printers/junitxml.go b/pkg/printers/junitxml.go index c3032bb7469c..7a68821eff6b 100644 --- a/pkg/printers/junitxml.go +++ b/pkg/printers/junitxml.go @@ -39,9 +39,7 @@ type JunitXML struct { } func NewJunitXML(w io.Writer) *JunitXML { - return &JunitXML{ - w: w, - } + return &JunitXML{w: w} } func (p JunitXML) Print(ctx context.Context, issues []result.Issue) error {