Skip to content

Commit 6310464

Browse files
lafriksldez
authored andcommitted
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 `:`.
1 parent 669852e commit 6310464

File tree

10 files changed

+128
-52
lines changed

10 files changed

+128
-52
lines changed

pkg/commands/run.go

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ func (e *Executor) setExitCodeIfIssuesFound(issues []result.Issue) {
381381
}
382382
}
383383

384+
const defaultFileMode = 0644
385+
384386
func (e *Executor) runAndPrint(ctx context.Context, args []string) error {
385387
if err := e.goenv.Discover(ctx); err != nil {
386388
e.log.Warnf("Failed to discover go env: %s", err)
@@ -400,44 +402,77 @@ func (e *Executor) runAndPrint(ctx context.Context, args []string) error {
400402
return err // XXX: don't loose type
401403
}
402404

403-
p, err := e.createPrinter()
404-
if err != nil {
405-
return err
405+
formats := strings.Split(e.cfg.Output.Format, ",")
406+
for _, format := range formats {
407+
out := strings.SplitN(format, ":", 2)
408+
if len(out) < 2 {
409+
out = append(out, "")
410+
}
411+
w, shouldClose, err := e.createWriter(out[1])
412+
if err != nil {
413+
return fmt.Errorf("can't create output for %s: %w", out[1], err)
414+
}
415+
416+
p, err := e.createPrinter(out[0], w)
417+
if err != nil {
418+
if file, ok := w.(io.Closer); shouldClose && ok {
419+
file.Close()
420+
}
421+
return err
422+
}
423+
if err = p.Print(ctx, issues); err != nil {
424+
if file, ok := w.(io.Closer); shouldClose && ok {
425+
file.Close()
426+
}
427+
return fmt.Errorf("can't print %d issues: %s", len(issues), err)
428+
}
429+
if file, ok := w.(io.Closer); shouldClose && ok {
430+
file.Close()
431+
}
406432
}
407433

408434
e.setExitCodeIfIssuesFound(issues)
409435

410-
if err = p.Print(ctx, issues); err != nil {
411-
return fmt.Errorf("can't print %d issues: %s", len(issues), err)
412-
}
413-
414436
e.fileCache.PrintStats(e.log)
415437

416438
return nil
417439
}
418440

419-
func (e *Executor) createPrinter() (printers.Printer, error) {
441+
func (e *Executor) createWriter(path string) (io.Writer, bool, error) {
442+
if path == "stdout" {
443+
return logutils.StdOut, false, nil
444+
}
445+
if path == "stderr" {
446+
return logutils.StdErr, false, nil
447+
}
448+
f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, defaultFileMode)
449+
if err != nil {
450+
return nil, false, err
451+
}
452+
return f, true, nil
453+
}
454+
455+
func (e *Executor) createPrinter(format string, w io.Writer) (printers.Printer, error) {
420456
var p printers.Printer
421-
format := e.cfg.Output.Format
422457
switch format {
423458
case config.OutFormatJSON:
424-
p = printers.NewJSON(&e.reportData)
459+
p = printers.NewJSON(&e.reportData, w)
425460
case config.OutFormatColoredLineNumber, config.OutFormatLineNumber:
426461
p = printers.NewText(e.cfg.Output.PrintIssuedLine,
427462
format == config.OutFormatColoredLineNumber, e.cfg.Output.PrintLinterName,
428-
e.log.Child("text_printer"))
463+
e.log.Child("text_printer"), w)
429464
case config.OutFormatTab:
430-
p = printers.NewTab(e.cfg.Output.PrintLinterName, e.log.Child("tab_printer"))
465+
p = printers.NewTab(e.cfg.Output.PrintLinterName, e.log.Child("tab_printer"), w)
431466
case config.OutFormatCheckstyle:
432-
p = printers.NewCheckstyle()
467+
p = printers.NewCheckstyle(w)
433468
case config.OutFormatCodeClimate:
434-
p = printers.NewCodeClimate()
469+
p = printers.NewCodeClimate(w)
435470
case config.OutFormatHTML:
436-
p = printers.NewHTML()
471+
p = printers.NewHTML(w)
437472
case config.OutFormatJunitXML:
438-
p = printers.NewJunitXML()
473+
p = printers.NewJunitXML(w)
439474
case config.OutFormatGithubActions:
440-
p = printers.NewGithub()
475+
p = printers.NewGithub(w)
441476
default:
442477
return nil, fmt.Errorf("unknown output format %s", format)
443478
}

pkg/printers/checkstyle.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import (
44
"context"
55
"encoding/xml"
66
"fmt"
7+
"io"
78

89
"github.com/go-xmlfmt/xmlfmt"
910

10-
"github.com/golangci/golangci-lint/pkg/logutils"
1111
"github.com/golangci/golangci-lint/pkg/result"
1212
)
1313

@@ -32,13 +32,17 @@ type checkstyleError struct {
3232

3333
const defaultCheckstyleSeverity = "error"
3434

35-
type Checkstyle struct{}
35+
type Checkstyle struct {
36+
w io.Writer
37+
}
3638

37-
func NewCheckstyle() *Checkstyle {
38-
return &Checkstyle{}
39+
func NewCheckstyle(w io.Writer) *Checkstyle {
40+
return &Checkstyle{
41+
w: w,
42+
}
3943
}
4044

41-
func (Checkstyle) Print(ctx context.Context, issues []result.Issue) error {
45+
func (p Checkstyle) Print(ctx context.Context, issues []result.Issue) error {
4246
out := checkstyleOutput{
4347
Version: "5.0",
4448
}
@@ -82,6 +86,6 @@ func (Checkstyle) Print(ctx context.Context, issues []result.Issue) error {
8286
return err
8387
}
8488

85-
fmt.Fprintf(logutils.StdOut, "%s%s\n", xml.Header, xmlfmt.FormatXML(string(data), "", " "))
89+
fmt.Fprintf(p.w, "%s%s\n", xml.Header, xmlfmt.FormatXML(string(data), "", " "))
8690
return nil
8791
}

pkg/printers/codeclimate.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"io"
78

8-
"github.com/golangci/golangci-lint/pkg/logutils"
99
"github.com/golangci/golangci-lint/pkg/result"
1010
)
1111

@@ -24,10 +24,13 @@ type CodeClimateIssue struct {
2424
}
2525

2626
type CodeClimate struct {
27+
w io.Writer
2728
}
2829

29-
func NewCodeClimate() *CodeClimate {
30-
return &CodeClimate{}
30+
func NewCodeClimate(w io.Writer) *CodeClimate {
31+
return &CodeClimate{
32+
w: w,
33+
}
3134
}
3235

3336
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 {
5255
return err
5356
}
5457

55-
fmt.Fprint(logutils.StdOut, string(outputJSON))
58+
fmt.Fprint(p.w, string(outputJSON))
5659
return nil
5760
}

pkg/printers/github.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,23 @@ package printers
33
import (
44
"context"
55
"fmt"
6+
"io"
67

7-
"github.com/golangci/golangci-lint/pkg/logutils"
88
"github.com/golangci/golangci-lint/pkg/result"
99
)
1010

1111
type github struct {
12+
w io.Writer
1213
}
1314

1415
const defaultGithubSeverity = "error"
1516

1617
// NewGithub output format outputs issues according to GitHub actions format:
1718
// https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
18-
func NewGithub() Printer {
19-
return &github{}
19+
func NewGithub(w io.Writer) Printer {
20+
return &github{
21+
w: w,
22+
}
2023
}
2124

2225
// 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 {
3538
return ret
3639
}
3740

38-
func (g *github) Print(_ context.Context, issues []result.Issue) error {
41+
func (p *github) Print(_ context.Context, issues []result.Issue) error {
3942
for ind := range issues {
40-
_, err := fmt.Fprintln(logutils.StdOut, formatIssueAsGithub(&issues[ind]))
43+
_, err := fmt.Fprintln(p.w, formatIssueAsGithub(&issues[ind]))
4144
if err != nil {
4245
return err
4346
}

pkg/printers/html.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import (
44
"context"
55
"fmt"
66
"html/template"
7+
"io"
78
"strings"
89

9-
"github.com/golangci/golangci-lint/pkg/logutils"
1010
"github.com/golangci/golangci-lint/pkg/result"
1111
)
1212

@@ -123,13 +123,17 @@ type htmlIssue struct {
123123
Code string
124124
}
125125

126-
type HTML struct{}
126+
type HTML struct {
127+
w io.Writer
128+
}
127129

128-
func NewHTML() *HTML {
129-
return &HTML{}
130+
func NewHTML(w io.Writer) *HTML {
131+
return &HTML{
132+
w: w,
133+
}
130134
}
131135

132-
func (h HTML) Print(_ context.Context, issues []result.Issue) error {
136+
func (p HTML) Print(_ context.Context, issues []result.Issue) error {
133137
var htmlIssues []htmlIssue
134138

135139
for i := range issues {
@@ -151,5 +155,5 @@ func (h HTML) Print(_ context.Context, issues []result.Issue) error {
151155
return err
152156
}
153157

154-
return t.Execute(logutils.StdOut, struct{ Issues []htmlIssue }{Issues: htmlIssues})
158+
return t.Execute(p.w, struct{ Issues []htmlIssue }{Issues: htmlIssues})
155159
}

pkg/printers/json.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,21 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"io"
78

8-
"github.com/golangci/golangci-lint/pkg/logutils"
99
"github.com/golangci/golangci-lint/pkg/report"
1010
"github.com/golangci/golangci-lint/pkg/result"
1111
)
1212

1313
type JSON struct {
1414
rd *report.Data
15+
w io.Writer
1516
}
1617

17-
func NewJSON(rd *report.Data) *JSON {
18+
func NewJSON(rd *report.Data, w io.Writer) *JSON {
1819
return &JSON{
1920
rd: rd,
21+
w: w,
2022
}
2123
}
2224

@@ -39,6 +41,6 @@ func (p JSON) Print(ctx context.Context, issues []result.Issue) error {
3941
return err
4042
}
4143

42-
fmt.Fprint(logutils.StdOut, string(outputJSON))
44+
fmt.Fprint(p.w, string(outputJSON))
4345
return nil
4446
}

pkg/printers/junitxml.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ package printers
33
import (
44
"context"
55
"encoding/xml"
6+
"io"
67
"strings"
78

8-
"github.com/golangci/golangci-lint/pkg/logutils"
99
"github.com/golangci/golangci-lint/pkg/result"
1010
)
1111

@@ -35,13 +35,16 @@ type failureXML struct {
3535
}
3636

3737
type JunitXML struct {
38+
w io.Writer
3839
}
3940

40-
func NewJunitXML() *JunitXML {
41-
return &JunitXML{}
41+
func NewJunitXML(w io.Writer) *JunitXML {
42+
return &JunitXML{
43+
w: w,
44+
}
4245
}
4346

44-
func (JunitXML) Print(ctx context.Context, issues []result.Issue) error {
47+
func (p JunitXML) Print(ctx context.Context, issues []result.Issue) error {
4548
suites := make(map[string]testSuiteXML) // use a map to group by file
4649

4750
for ind := range issues {
@@ -70,7 +73,7 @@ func (JunitXML) Print(ctx context.Context, issues []result.Issue) error {
7073
res.TestSuites = append(res.TestSuites, val)
7174
}
7275

73-
enc := xml.NewEncoder(logutils.StdOut)
76+
enc := xml.NewEncoder(p.w)
7477
enc.Indent("", " ")
7578
if err := enc.Encode(res); err != nil {
7679
return err

pkg/printers/tab.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ import (
1515
type Tab struct {
1616
printLinterName bool
1717
log logutils.Log
18+
w io.Writer
1819
}
1920

20-
func NewTab(printLinterName bool, log logutils.Log) *Tab {
21+
func NewTab(printLinterName bool, log logutils.Log, w io.Writer) *Tab {
2122
return &Tab{
2223
printLinterName: printLinterName,
2324
log: log,
25+
w: w,
2426
}
2527
}
2628

@@ -30,7 +32,7 @@ func (p Tab) SprintfColored(ca color.Attribute, format string, args ...interface
3032
}
3133

3234
func (p *Tab) Print(ctx context.Context, issues []result.Issue) error {
33-
w := tabwriter.NewWriter(logutils.StdOut, 0, 0, 2, ' ', 0)
35+
w := tabwriter.NewWriter(p.w, 0, 0, 2, ' ', 0)
3436

3537
for i := range issues {
3638
p.printIssue(&issues[i], w)

0 commit comments

Comments
 (0)