Skip to content

Commit

Permalink
Testing GitHub workflow comment report format
Browse files Browse the repository at this point in the history
Signed-off-by: Anders Eknert <anders@styra.com>
  • Loading branch information
anderseknert committed Aug 24, 2023
1 parent 2b3d7ed commit f2dfdc6
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ jobs:
- run: go test ./...
- run: go build
- run: chmod +x regal
- run: ./regal lint bundle
- run: ./regal lint --format github bundle
- run: go test -tags e2e ./e2e
2 changes: 2 additions & 0 deletions bundle/regal/main.rego
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import future.keywords.in

import data.regal.config

this_is_a_test := input[_]

Check failure on line 9 in bundle/regal/main.rego

View workflow job for this annotation

GitHub Actions / build

Iteration in top-level assignment. To learn more, see: https://docs.styra.com/regal/rules/bugs/top-level-iteration

report contains violation if {
not is_object(input)

Expand Down
2 changes: 2 additions & 0 deletions cmd/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ const (
formatPretty = "pretty"
// formatCompact is the compact format value for the --format flag in various commands.
formatCompact = "compact"
// formatGitHub is the GitHub format value for the --format flag in various commands.
formatGitHub = "github"
)
2 changes: 2 additions & 0 deletions cmd/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ func getReporter(format string, outputWriter io.Writer) (reporter.Reporter, erro
return reporter.NewCompactReporter(outputWriter), nil
case formatJSON:
return reporter.NewJSONReporter(outputWriter), nil
case formatGitHub:
return reporter.NewGitHubReporter(outputWriter), nil
default:
return nil, fmt.Errorf("unknown format %s", format)
}
Expand Down
89 changes: 88 additions & 1 deletion pkg/reporter/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
"os"
"strings"

"github.com/fatih/color"
Expand Down Expand Up @@ -33,6 +34,11 @@ type JSONReporter struct {
out io.Writer
}

// GitHubReporter reports violations in a format suitable for GitHub Actions.
type GitHubReporter struct {
out io.Writer
}

// NewPrettyReporter creates a new PrettyReporter.
func NewPrettyReporter(out io.Writer) PrettyReporter {
return PrettyReporter{out: out}
Expand All @@ -48,6 +54,11 @@ func NewJSONReporter(out io.Writer) JSONReporter {
return JSONReporter{out: out}
}

// NewGitHubReporter creates a new GitHubReporter.
func NewGitHubReporter(out io.Writer) GitHubReporter {
return GitHubReporter{out: out}
}

// Publish prints a pretty report to the configured output.
func (tr PrettyReporter) Publish(r report.Report) error {
table := buildPrettyViolationsTable(r.Violations)
Expand Down Expand Up @@ -135,7 +146,7 @@ func (tr CompactReporter) Publish(r report.Report) error {
table.AddRow(violation.Location.String(), violation.Description)
}

_, err := fmt.Fprintln(tr.out, table)
_, err := fmt.Fprintln(tr.out, strings.TrimSuffix(table.String(), " "))

return err //nolint:wrapcheck
}
Expand All @@ -156,6 +167,73 @@ func (tr JSONReporter) Publish(r report.Report) error {
return err //nolint:wrapcheck
}

//nolint:nestif
func (tr GitHubReporter) Publish(r report.Report) error {
if r.Violations == nil {
r.Violations = []report.Violation{}
}

for _, violation := range r.Violations {
_, err := fmt.Fprintf(tr.out,
"::%s file=%s,line=%d,col=%d::%s\n",
violation.Level,
violation.Location.File,
violation.Location.Row,
violation.Location.Column,
fmt.Sprintf("%s. To learn more, see: %s", violation.Description, getDocumentationURL(violation)),
)
if err != nil {
return err //nolint:wrapcheck
}
}

pluralScanned := ""
if r.Summary.FilesScanned == 0 || r.Summary.FilesScanned > 1 {
pluralScanned = "s"
}

// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary
if summaryFileLoc, ok := os.LookupEnv("GITHUB_STEP_SUMMARY"); ok && summaryFileLoc != "" {
summaryFile, err := os.OpenFile(summaryFileLoc, os.O_APPEND|os.O_WRONLY, 0o644)
if err != nil {
return err //nolint:wrapcheck
}

_, _ = fmt.Fprintf(summaryFile, "### Regal Lint Report\n\n")

_, _ = fmt.Fprintf(summaryFile, "%d file%s linted.", r.Summary.FilesScanned, pluralScanned)

if r.Summary.NumViolations == 0 { //nolint:nestif
_, _ = fmt.Fprintf(summaryFile, " No violations found")
} else {
pluralViolations := ""
if r.Summary.NumViolations > 1 {
pluralViolations = "s"
}

_, _ = fmt.Fprintf(summaryFile, " %d violation%s found", r.Summary.NumViolations, pluralViolations)

if r.Summary.FilesScanned > 1 && r.Summary.FilesFailed > 0 {
pluralFailed := ""
if r.Summary.FilesFailed > 1 {
pluralFailed = "s"
}

_, _ = fmt.Fprintf(summaryFile, " in %d file%s.", r.Summary.FilesFailed, pluralFailed)
_, _ = fmt.Fprintf(summaryFile, " See Files tab in PR for locations and details.\n\n")

_, _ = fmt.Fprintf(summaryFile, "#### Violations\n\n")

for description, url := range getUniqueViolationURLs(r.Violations) {
_, _ = fmt.Fprintf(summaryFile, "* [%s](%s)\n", description, url)
}
}
}
}

return nil
}

func getDocumentationURL(violation report.Violation) string {
for _, resource := range violation.RelatedResources {
if resource.Description == "documentation" {
Expand All @@ -165,3 +243,12 @@ func getDocumentationURL(violation report.Violation) string {

return ""
}

func getUniqueViolationURLs(violations []report.Violation) map[string]string {
urls := make(map[string]string)
for _, violation := range violations {
urls[violation.Description] = getDocumentationURL(violation)
}

return urls
}
21 changes: 20 additions & 1 deletion pkg/reporter/reporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func TestCompactReporterPublish(t *testing.T) {
}

expect := `a.rego:1:1 Rego must not break the law!
b.rego:22:18 Questionable decision found
b.rego:22:18 Questionable decision found
`

if buf.String() != expect {
Expand Down Expand Up @@ -221,3 +221,22 @@ func TestJSONReporterPublishNoViolations(t *testing.T) {
t.Errorf("expected %q, got %q", `{"violations":[]}`, buf.String())
}
}

//nolint:paralleltest
func TestGitHubReporterPublishNoViolations(t *testing.T) {
// Can't use t.Parallel() here because use os.Setenv()
t.Setenv("GITHUB_STEP_SUMMARY", "")

var buf bytes.Buffer

cr := NewGitHubReporter(&buf)

err := cr.Publish(report.Report{})
if err != nil {
t.Fatal(err)
}

if buf.String() != "" {
t.Errorf("expected %q, got %q", "", buf.String())
}
}

0 comments on commit f2dfdc6

Please sign in to comment.