1
1
package printers
2
2
3
3
import (
4
+ "encoding/json"
4
5
"fmt"
5
6
"io"
7
+ "os"
6
8
"path/filepath"
7
9
8
10
"github.com/golangci/golangci-lint/pkg/result"
9
11
)
10
12
11
- type GitHub struct {
12
- w io.Writer
13
+ const defaultGitHubSeverity = "error"
14
+
15
+ const filenameGitHubActionProblemMatchers = "golangci-lint-action-problem-matchers.json"
16
+
17
+ // GitHubProblemMatchers defines the root of problem matchers.
18
+ // - https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md
19
+ // - https://github.com/actions/toolkit/blob/main/docs/commands.md#problem-matchers
20
+ type GitHubProblemMatchers struct {
21
+ Matchers []GitHubMatcher `json:"problemMatcher,omitempty"`
13
22
}
14
23
15
- const defaultGithubSeverity = "error"
24
+ // GitHubMatcher defines a problem matcher.
25
+ type GitHubMatcher struct {
26
+ // Owner an ID field that can be used to remove or replace the problem matcher.
27
+ // **required**
28
+ Owner string `json:"owner,omitempty"`
29
+ // Severity indicates the default severity, either 'warning' or 'error' case-insensitive.
30
+ // Defaults to 'error'.
31
+ Severity string `json:"severity,omitempty"`
32
+ Pattern []GitHubPattern `json:"pattern,omitempty"`
33
+ }
34
+
35
+ // GitHubPattern defines a pattern for a problem matcher.
36
+ type GitHubPattern struct {
37
+ // Regexp the regexp pattern that provides the groups to match against.
38
+ // **required**
39
+ Regexp string `json:"regexp,omitempty"`
40
+ // File a group number containing the file name.
41
+ File int `json:"file,omitempty"`
42
+ // FromPath a group number containing a filepath used to root the file (e.g. a project file).
43
+ FromPath int `json:"fromPath,omitempty"`
44
+ // Line a group number containing the line number.
45
+ Line int `json:"line,omitempty"`
46
+ // Column a group number containing the column information.
47
+ Column int `json:"column,omitempty"`
48
+ // Severity a group number containing either 'warning' or 'error' case-insensitive.
49
+ // Defaults to `error`.
50
+ Severity int `json:"severity,omitempty"`
51
+ // Code a group number containing the error code.
52
+ Code int `json:"code,omitempty"`
53
+ // Message a group number containing the error message.
54
+ // **required** at least one pattern must set the message.
55
+ Message int `json:"message,omitempty"`
56
+ // Loop whether to loop until a match is not found,
57
+ // only valid on the last pattern of a multi-pattern matcher.
58
+ Loop bool `json:"loop,omitempty"`
59
+ }
16
60
17
- // NewGitHub output format outputs issues according to GitHub actions format:
18
- // https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message
61
+ type GitHub struct {
62
+ tempPath string
63
+ w io.Writer
64
+ }
65
+
66
+ // NewGitHub output format outputs issues according to GitHub actions the problem matcher regexp.
19
67
func NewGitHub (w io.Writer ) * GitHub {
20
- return & GitHub {w : w }
68
+ return & GitHub {
69
+ tempPath : filepath .Join (os .TempDir (), filenameGitHubActionProblemMatchers ),
70
+ w : w ,
71
+ }
72
+ }
73
+
74
+ func (p * GitHub ) Print (issues []result.Issue ) error {
75
+ // Note: the file with the problem matcher definition should not be removed.
76
+ // A sleep can mitigate this problem but this will be flaky.
77
+ //
78
+ // Result if the file is removed prematurely:
79
+ // Error: Unable to process command '::add-matcher::/tmp/golangci-lint-action-problem-matchers.json' successfully.
80
+ // Error: Could not find file '/tmp/golangci-lint-action-problem-matchers.json'.
81
+ filename , err := p .storeProblemMatcher ()
82
+ if err != nil {
83
+ return err
84
+ }
85
+
86
+ _ , _ = fmt .Fprintln (p .w , "::debug::problem matcher definition file: " + filename )
87
+
88
+ _ , _ = fmt .Fprintln (p .w , "::add-matcher::" + filename )
89
+
90
+ for ind := range issues {
91
+ _ , err := fmt .Fprintln (p .w , formatIssueAsGitHub (& issues [ind ]))
92
+ if err != nil {
93
+ return err
94
+ }
95
+ }
96
+
97
+ _ , _ = fmt .Fprintln (p .w , "::remove-matcher owner=golangci-lint-action::" )
98
+
99
+ return nil
100
+ }
101
+
102
+ func (p * GitHub ) storeProblemMatcher () (string , error ) {
103
+ file , err := os .Create (p .tempPath )
104
+ if err != nil {
105
+ return "" , err
106
+ }
107
+
108
+ defer file .Close ()
109
+
110
+ err = json .NewEncoder (file ).Encode (generateProblemMatcher ())
111
+ if err != nil {
112
+ return "" , err
113
+ }
114
+
115
+ return file .Name (), nil
116
+ }
117
+
118
+ func generateProblemMatcher () GitHubProblemMatchers {
119
+ return GitHubProblemMatchers {
120
+ Matchers : []GitHubMatcher {
121
+ {
122
+ Owner : "golangci-lint-action" ,
123
+ Severity : "error" ,
124
+ Pattern : []GitHubPattern {
125
+ {
126
+ Regexp : `^([^\s]+)\s+([^:]+):(\d+):(?:(\d+):)?\s+(.+)$` ,
127
+ Severity : 1 ,
128
+ File : 2 ,
129
+ Line : 3 ,
130
+ Column : 4 ,
131
+ Message : 5 ,
132
+ },
133
+ },
134
+ },
135
+ },
136
+ }
21
137
}
22
138
23
- // print each line as: ::error file=app.js,line=10,col=15::Something went wrong
24
- func formatIssueAsGithub (issue * result.Issue ) string {
25
- severity := defaultGithubSeverity
139
+ func formatIssueAsGitHub (issue * result.Issue ) string {
140
+ severity := defaultGitHubSeverity
26
141
if issue .Severity != "" {
27
142
severity = issue .Severity
28
143
}
@@ -32,21 +147,11 @@ func formatIssueAsGithub(issue *result.Issue) string {
32
147
// Otherwise, GitHub won't be able to show the annotations pointing to the file path with backslashes.
33
148
file := filepath .ToSlash (issue .FilePath ())
34
149
35
- ret := fmt .Sprintf ("::%s file=%s,line=%d " , severity , file , issue .Line ())
150
+ ret := fmt .Sprintf ("%s \t %s:%d: " , severity , file , issue .Line ())
36
151
if issue .Pos .Column != 0 {
37
- ret += fmt .Sprintf (",col=%d " , issue .Pos .Column )
152
+ ret += fmt .Sprintf ("%d: " , issue .Pos .Column )
38
153
}
39
154
40
- ret += fmt .Sprintf (":: %s (%s)" , issue .Text , issue .FromLinter )
155
+ ret += fmt .Sprintf ("\t %s (%s)" , issue .Text , issue .FromLinter )
41
156
return ret
42
157
}
43
-
44
- func (p * GitHub ) Print (issues []result.Issue ) error {
45
- for ind := range issues {
46
- _ , err := fmt .Fprintln (p .w , formatIssueAsGithub (& issues [ind ]))
47
- if err != nil {
48
- return err
49
- }
50
- }
51
- return nil
52
- }
0 commit comments