Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: github-action format #4699

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .golangci.next.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,18 @@ run:
# output configuration options
output:
# The formats used to render issues.
# Format: `colored-line-number`, `line-number`, `json`, `colored-tab`, `tab`, `checkstyle`, `code-climate`, `junit-xml`, `github-actions`, `teamcity`
# Formats:
# - `colored-line-number`
# - `line-number`
# - `json`
# - `colored-tab`
# - `tab`
# - `checkstyle`
# - `code-climate`
# - `junit-xml`
# - `github-actions`
# - `github-actions-problem-matchers`
# - `teamcity`
# Output path can be either `stdout`, `stderr` or path to the file to write to.
#
# For the CLI flag (`--out-format`), multiple formats can be specified by separating them by comma.
Expand Down
13 changes: 12 additions & 1 deletion .golangci.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,18 @@ run:
# output configuration options
output:
# The formats used to render issues.
# Format: `colored-line-number`, `line-number`, `json`, `colored-tab`, `tab`, `checkstyle`, `code-climate`, `junit-xml`, `github-actions`, `teamcity`
# Formats:
# - `colored-line-number`
# - `line-number`
# - `json`
# - `colored-tab`
# - `tab`
# - `checkstyle`
# - `code-climate`
# - `junit-xml`
# - `github-actions`
# - `github-actions-problem-matchers`
# - `teamcity`
# Output path can be either `stdout`, `stderr` or path to the file to write to.
#
# For the CLI flag (`--out-format`), multiple formats can be specified by separating them by comma.
Expand Down
1 change: 1 addition & 0 deletions jsonschema/golangci.jsonschema.json
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@
"code-climate",
"junit-xml",
"github-actions",
"github-actions-problem-matchers",
"teamcity"
]
}
Expand Down
1 change: 1 addition & 0 deletions jsonschema/golangci.next.jsonschema.json
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@
"code-climate",
"junit-xml",
"github-actions",
"github-actions-problem-matchers",
"teamcity"
]
}
Expand Down
24 changes: 13 additions & 11 deletions pkg/config/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ import (
)

const (
OutFormatJSON = "json"
OutFormatLineNumber = "line-number"
OutFormatColoredLineNumber = "colored-line-number"
OutFormatTab = "tab"
OutFormatColoredTab = "colored-tab"
OutFormatCheckstyle = "checkstyle"
OutFormatCodeClimate = "code-climate"
OutFormatHTML = "html"
OutFormatJunitXML = "junit-xml"
OutFormatGithubActions = "github-actions"
OutFormatTeamCity = "teamcity"
OutFormatJSON = "json"
OutFormatLineNumber = "line-number"
OutFormatColoredLineNumber = "colored-line-number"
OutFormatTab = "tab"
OutFormatColoredTab = "colored-tab"
OutFormatCheckstyle = "checkstyle"
OutFormatCodeClimate = "code-climate"
OutFormatHTML = "html"
OutFormatJunitXML = "junit-xml"
OutFormatGithubActions = "github-actions"
OutFormatGithubActionsProblemMatchers = "github-actions-problem-matchers"
OutFormatTeamCity = "teamcity"
)

var AllOutputFormats = []string{
Expand All @@ -32,6 +33,7 @@ var AllOutputFormats = []string{
OutFormatHTML,
OutFormatJunitXML,
OutFormatGithubActions,
OutFormatGithubActionsProblemMatchers,
OutFormatTeamCity,
}

Expand Down
51 changes: 51 additions & 0 deletions pkg/printers/githubaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package printers

import (
"fmt"
"io"
"path/filepath"

"github.com/golangci/golangci-lint/pkg/result"
)

const defaultGithubSeverity = "error"

type GitHubAction struct {
w io.Writer
}

// NewGitHubAction output format outputs issues according to GitHub actions.
func NewGitHubAction(w io.Writer) *GitHubAction {
return &GitHubAction{w: w}
}

func (p *GitHubAction) Print(issues []result.Issue) error {
for ind := range issues {
_, err := fmt.Fprintln(p.w, formatIssueAsGitHub(&issues[ind]))
if err != nil {
return err
}
}
return nil
}

// print each line as: ::error file=app.js,line=10,col=15::Something went wrong
func formatIssueAsGitHub(issue *result.Issue) string {
severity := defaultGithubSeverity
if issue.Severity != "" {
severity = issue.Severity
}

// Convert backslashes to forward slashes.
// This is needed when running on windows.
// Otherwise, GitHub won't be able to show the annotations pointing to the file path with backslashes.
file := filepath.ToSlash(issue.FilePath())

ret := fmt.Sprintf("::%s file=%s,line=%d", severity, file, 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
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"os"
"path/filepath"
"strconv"

"github.com/golangci/golangci-lint/pkg/result"
)
Expand All @@ -14,26 +15,26 @@ const defaultGitHubSeverity = "error"

const filenameGitHubActionProblemMatchers = "golangci-lint-action-problem-matchers.json"

// GitHubProblemMatchers defines the root of problem matchers.
// GHProblemMatchers defines the root of problem matchers.
// - https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md
// - https://github.com/actions/toolkit/blob/main/docs/commands.md#problem-matchers
type GitHubProblemMatchers struct {
Matchers []GitHubMatcher `json:"problemMatcher,omitempty"`
type GHProblemMatchers struct {
Matchers []GHMatcher `json:"problemMatcher,omitempty"`
}

// GitHubMatcher defines a problem matcher.
type GitHubMatcher struct {
// GHMatcher defines a problem matcher.
type GHMatcher struct {
// Owner an ID field that can be used to remove or replace the problem matcher.
// **required**
Owner string `json:"owner,omitempty"`
// Severity indicates the default severity, either 'warning' or 'error' case-insensitive.
// Defaults to 'error'.
Severity string `json:"severity,omitempty"`
Pattern []GitHubPattern `json:"pattern,omitempty"`
Severity string `json:"severity,omitempty"`
Pattern []GHPattern `json:"pattern,omitempty"`
}

// GitHubPattern defines a pattern for a problem matcher.
type GitHubPattern struct {
// GHPattern defines a pattern for a problem matcher.
type GHPattern struct {
// Regexp the regexp pattern that provides the groups to match against.
// **required**
Regexp string `json:"regexp,omitempty"`
Expand All @@ -58,37 +59,41 @@ type GitHubPattern struct {
Loop bool `json:"loop,omitempty"`
}

type GitHub struct {
type GitHubActionProblemMatchers struct {
tempPath string
w io.Writer
}

// NewGitHub output format outputs issues according to GitHub actions the problem matcher regexp.
func NewGitHub(w io.Writer) *GitHub {
return &GitHub{
// NewGitHubActionProblemMatchers output format outputs issues according to GitHub actions the problem matcher regexp.
func NewGitHubActionProblemMatchers(w io.Writer) *GitHubActionProblemMatchers {
return &GitHubActionProblemMatchers{
tempPath: filepath.Join(os.TempDir(), filenameGitHubActionProblemMatchers),
w: w,
}
}

func (p *GitHub) Print(issues []result.Issue) error {
// Note: the file with the problem matcher definition should not be removed.
// A sleep can mitigate this problem but this will be flaky.
//
// Result if the file is removed prematurely:
// Error: Unable to process command '::add-matcher::/tmp/golangci-lint-action-problem-matchers.json' successfully.
// Error: Could not find file '/tmp/golangci-lint-action-problem-matchers.json'.
filename, err := p.storeProblemMatcher()
if err != nil {
return err
}

_, _ = fmt.Fprintln(p.w, "::debug::problem matcher definition file: "+filename)
func (p *GitHubActionProblemMatchers) Print(issues []result.Issue) error {
// Used by the official GitHub Action (https://github.com/golangci/golangci-lint-action).
// The problem matchers is embedded inside the GitHub Action to avoid errors:
// https://github.com/golangci/golangci-lint/issues/4695
if ok, _ := strconv.ParseBool(os.Getenv("GOLANGCI_LINT_SKIP_GHA_PM_INSTALL")); !ok {
// Note: the file with the problem matcher definition should not be removed.
// A sleep can mitigate this problem but this will be flaky.
//
// Result if the file is removed prematurely:
// Error: Unable to process command '::add-matcher::/tmp/golangci-lint-action-problem-matchers.json' successfully.
// Error: Could not find file '/tmp/golangci-lint-action-problem-matchers.json'.
filename, err := p.storeProblemMatcher()
if err != nil {
return err
}

_, _ = fmt.Fprintln(p.w, "::add-matcher::"+filename)
_, _ = fmt.Fprintln(p.w, "::debug::problem matcher definition file: "+filename)
_, _ = fmt.Fprintln(p.w, "::add-matcher::"+filename)
}

for ind := range issues {
_, err := fmt.Fprintln(p.w, formatIssueAsGitHub(&issues[ind]))
_, err := fmt.Fprintln(p.w, formatIssueAsProblemMatcher(&issues[ind]))
if err != nil {
return err
}
Expand All @@ -99,7 +104,7 @@ func (p *GitHub) Print(issues []result.Issue) error {
return nil
}

func (p *GitHub) storeProblemMatcher() (string, error) {
func (p *GitHubActionProblemMatchers) storeProblemMatcher() (string, error) {
file, err := os.Create(p.tempPath)
if err != nil {
return "", err
Expand All @@ -115,13 +120,16 @@ func (p *GitHub) storeProblemMatcher() (string, error) {
return file.Name(), nil
}

func generateProblemMatcher() GitHubProblemMatchers {
return GitHubProblemMatchers{
Matchers: []GitHubMatcher{
// generateProblemMatcher generated the problem matchers file.
// Should be synced with the official GitHub Action.
// https://github.com/golangci/golangci-lint-action/blob/master/problem-matchers.json
func generateProblemMatcher() GHProblemMatchers {
return GHProblemMatchers{
Matchers: []GHMatcher{
{
Owner: "golangci-lint-action",
Severity: "error",
Pattern: []GitHubPattern{
Pattern: []GHPattern{
{
Regexp: `^([^\s]+)\s+([^:]+):(\d+):(?:(\d+):)?\s+(.+)$`,
Severity: 1,
Expand All @@ -136,7 +144,7 @@ func generateProblemMatcher() GitHubProblemMatchers {
}
}

func formatIssueAsGitHub(issue *result.Issue) string {
func formatIssueAsProblemMatcher(issue *result.Issue) string {
severity := defaultGitHubSeverity
if issue.Severity != "" {
severity = issue.Severity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestGitHub_Print(t *testing.T) {

buf := new(bytes.Buffer)

printer := NewGitHub(buf)
printer := NewGitHubActionProblemMatchers(buf)
printer.tempPath = filepath.Join(t.TempDir(), filenameGitHubActionProblemMatchers)

err := printer.Print(issues)
Expand All @@ -67,7 +67,7 @@ error path/to/fileb.go:300:9: another issue (linter-b)
assert.Equal(t, expected, buf.String())
}

func Test_formatIssueAsGitHub(t *testing.T) {
func Test_formatIssueAsProblemMatcher(t *testing.T) {
sampleIssue := result.Issue{
FromLinter: "sample-linter",
Text: "some issue",
Expand All @@ -78,13 +78,13 @@ func Test_formatIssueAsGitHub(t *testing.T) {
Column: 4,
},
}
require.Equal(t, "error\tpath/to/file.go:10:4:\tsome issue (sample-linter)", formatIssueAsGitHub(&sampleIssue))
require.Equal(t, "error\tpath/to/file.go:10:4:\tsome issue (sample-linter)", formatIssueAsProblemMatcher(&sampleIssue))

sampleIssue.Pos.Column = 0
require.Equal(t, "error\tpath/to/file.go:10:\tsome issue (sample-linter)", formatIssueAsGitHub(&sampleIssue))
require.Equal(t, "error\tpath/to/file.go:10:\tsome issue (sample-linter)", formatIssueAsProblemMatcher(&sampleIssue))
}

func Test_formatIssueAsGitHub_Windows(t *testing.T) {
func Test_formatIssueAsProblemMatcher_Windows(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("Skipping test on non Windows")
}
Expand All @@ -99,10 +99,10 @@ func Test_formatIssueAsGitHub_Windows(t *testing.T) {
Column: 4,
},
}
require.Equal(t, "error\tpath/to/file.go:10:4:\tsome issue (sample-linter)", formatIssueAsGitHub(&sampleIssue))
require.Equal(t, "error\tpath/to/file.go:10:4:\tsome issue (sample-linter)", formatIssueAsProblemMatcher(&sampleIssue))

sampleIssue.Pos.Column = 0
require.Equal(t, "error\tpath/to/file.go:10:\tsome issue (sample-linter)", formatIssueAsGitHub(&sampleIssue))
require.Equal(t, "error\tpath/to/file.go:10:\tsome issue (sample-linter)", formatIssueAsProblemMatcher(&sampleIssue))
}

func Test_generateProblemMatcher(t *testing.T) {
Expand Down Expand Up @@ -158,7 +158,7 @@ Message: Foo bar`,
}
}

func createReplacement(pattern *GitHubPattern) string {
func createReplacement(pattern *GHPattern) string {
var repl []string

if pattern.File > 0 {
Expand Down
Loading
Loading