diff --git a/cmd/pint/ci.go b/cmd/pint/ci.go index a7e8af50..d2b4052f 100644 --- a/cmd/pint/ci.go +++ b/cmd/pint/ci.go @@ -125,24 +125,20 @@ func actionCI(c *cli.Context) error { } reps := []reporter.Reporter{} - if c.String(checkStyleFlag) != "" { - f, fileErr := os.Create(c.String(checkStyleFlag)) - if fileErr != nil { - return fileErr - } - // execute here so we can close the file right after - errRep := reporter.NewCheckStyleReporter(f).Submit(summary) - slog.Error("Error encountered", "error:", errRep) - cerr := f.Close() - if cerr != nil { - return cerr - } - } if c.Bool(teamCityFlag) { reps = append(reps, reporter.NewTeamCityReporter(os.Stderr)) } else { reps = append(reps, reporter.NewConsoleReporter(os.Stderr, checks.Information)) } + if c.String(checkStyleFlag) != "" { + var f *os.File + f, err = os.Create(c.String(checkStyleFlag)) + if err != nil { + return err + } + defer f.Close() + reps = append(reps, reporter.NewCheckStyleReporter(f)) + } if meta.cfg.Repository != nil && meta.cfg.Repository.BitBucket != nil { token, ok := os.LookupEnv("BITBUCKET_AUTH_TOKEN") diff --git a/cmd/pint/lint.go b/cmd/pint/lint.go index 6cb8e2b2..2fc9534d 100644 --- a/cmd/pint/lint.go +++ b/cmd/pint/lint.go @@ -114,17 +114,13 @@ func actionLint(c *cli.Context) error { } if c.String(checkStyleFlag) != "" { - f, fileErr := os.Create(c.String(checkStyleFlag)) - if fileErr != nil { - return fileErr - } - // execute here so we can close the file right after - errRep := reporter.NewCheckStyleReporter(f).Submit(summary) - slog.Error("Error encountered", "error:", errRep) - cerr := f.Close() - if cerr != nil { - return cerr + var f *os.File + f, err = os.Create(c.String(checkStyleFlag)) + if err != nil { + return err } + defer f.Close() + reps = append(reps, reporter.NewCheckStyleReporter(f)) } for _, rep := range reps { diff --git a/cmd/pint/tests/0194_lint_checkstyle.txt b/cmd/pint/tests/0194_lint_checkstyle.txt index 8f73dc98..98d80e0d 100644 --- a/cmd/pint/tests/0194_lint_checkstyle.txt +++ b/cmd/pint/tests/0194_lint_checkstyle.txt @@ -1,6 +1,5 @@ -env NO_COLOR=1 ! exec pint --no-color lint --min-severity=info --checkstyle=checkstyle.xml rules -cmp checkstyle.xml checkstyle_check.xml +cmp checkstyle.xml checkstyle_expected.xml -- rules/0001.yml -- groups: @@ -10,11 +9,11 @@ groups: expr: up - alert: Example expr: sum(xxx) with() --- checkstyle_check.xml -- +-- checkstyle_expected.xml -- - - + + - \ No newline at end of file + diff --git a/cmd/pint/tests/0195_lint_checkstyle_no_dir.txt b/cmd/pint/tests/0195_lint_checkstyle_no_dir.txt new file mode 100644 index 00000000..a683c520 --- /dev/null +++ b/cmd/pint/tests/0195_lint_checkstyle_no_dir.txt @@ -0,0 +1,16 @@ +! exec pint --no-color lint --checkstyle=x/y/z/checkstyle.xml rules +! stdout . +cmp stderr stderr.txt + +-- stderr.txt -- +level=INFO msg="Finding all rules to check" paths=["rules"] +level=ERROR msg="Fatal error" err="open x/y/z/checkstyle.xml: no such file or directory" +-- rules/0001.yml -- +groups: +- name: test + rules: + - alert: Example + expr: up + - alert: Example + expr: sum(xxx) with() + diff --git a/cmd/pint/tests/0196_checkstyle_ci.txt b/cmd/pint/tests/0196_checkstyle_ci.txt new file mode 100644 index 00000000..9c14e936 --- /dev/null +++ b/cmd/pint/tests/0196_checkstyle_ci.txt @@ -0,0 +1,52 @@ +mkdir testrepo +cd testrepo +exec git init --initial-branch=main . + +cp ../src/v1.yml rules.yml +cp ../src/.pint.hcl . +env GIT_AUTHOR_NAME=pint +env GIT_AUTHOR_EMAIL=pint@example.com +env GIT_COMMITTER_NAME=pint +env GIT_COMMITTER_EMAIL=pint@example.com +exec git add . +exec git commit -am 'import rules and config' + +exec git checkout -b v2 +cp ../src/v2.yml rules.yml +exec git commit -am 'v2' + +exec pint -l debug --offline --no-color ci --checkstyle=checkstyle.xml +! stdout . +cmp checkstyle.xml ../checkstyle_expected.xml + +-- src/v1.yml -- +- alert: rule1 + expr: sum(foo) by(job) +- alert: rule2 + expr: sum(foo) by(job) + for: 0s + +-- src/v2.yml -- +- alert: rule1 + expr: sum(foo) by(job) + for: 0s +- alert: rule2 + expr: sum(foo) by(job) + for: 0s + +-- src/.pint.hcl -- +ci { + baseBranch = "main" +} +parser { + relaxed = [".*"] +} + +-- checkstyle_expected.xml -- + + + + + + + diff --git a/cmd/pint/tests/0197_ci_checkstyle_no_dir.txt b/cmd/pint/tests/0197_ci_checkstyle_no_dir.txt new file mode 100644 index 00000000..2a76370b --- /dev/null +++ b/cmd/pint/tests/0197_ci_checkstyle_no_dir.txt @@ -0,0 +1,49 @@ +mkdir testrepo +cd testrepo +exec git init --initial-branch=main . + +cp ../src/v1.yml rules.yml +cp ../src/.pint.hcl . +env GIT_AUTHOR_NAME=pint +env GIT_AUTHOR_EMAIL=pint@example.com +env GIT_COMMITTER_NAME=pint +env GIT_COMMITTER_EMAIL=pint@example.com +exec git add . +exec git commit -am 'import rules and config' + +exec git checkout -b v2 +cp ../src/v2.yml rules.yml +exec git commit -am 'v2' + +! exec pint --offline --no-color ci --checkstyle=x/y/z/checkstyle.xml +! stdout . +cmp stderr ../stderr.txt + +-- src/v1.yml -- +- alert: rule1 + expr: sum(foo) by(job) +- alert: rule2 + expr: sum(foo) by(job) + for: 0s + +-- src/v2.yml -- +- alert: rule1 + expr: sum(foo) by(job) + for: 0s +- alert: rule2 + expr: sum(foo) by(job) + for: 0s + +-- src/.pint.hcl -- +ci { + baseBranch = "main" +} +parser { + relaxed = [".*"] +} + +-- stderr.txt -- +level=INFO msg="Loading configuration file" path=.pint.hcl +level=INFO msg="Finding all rules to check on current git branch" base=main +level=INFO msg="Offline mode, skipping Prometheus discovery" +level=ERROR msg="Fatal error" err="open x/y/z/checkstyle.xml: no such file or directory" diff --git a/docs/changelog.md b/docs/changelog.md index 6a7ab99b..216d4f1e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,12 @@ # Changelog +## v0.67.0 + +### Added + +- Added `--checkstyle` flag to `pint lint` & `pint ci` for writing XML report file + in `checkstyle` format - [#1129](https://github.com/cloudflare/pint/pull/1129). + ## v0.66.1 ### Fixed diff --git a/internal/reporter/checkstyle.go b/internal/reporter/checkstyle.go index 83478d16..41719659 100644 --- a/internal/reporter/checkstyle.go +++ b/internal/reporter/checkstyle.go @@ -4,6 +4,7 @@ import ( "encoding/xml" "fmt" "io" + "log/slog" "strconv" ) @@ -27,8 +28,8 @@ func createCheckstyleReport(summary Summary) checkstyleReport { return x } -func (d checkstyleReport) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { - err := e.EncodeToken(xml.StartElement{ +func (d checkstyleReport) MarshalXML(e *xml.Encoder, _ xml.StartElement) (err error) { + err = e.EncodeToken(xml.StartElement{ Name: xml.Name{Local: "checkstyle"}, Attr: []xml.Attr{ { @@ -41,34 +42,35 @@ func (d checkstyleReport) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { return err } for dir, reports := range d { - errEnc := e.EncodeToken(xml.StartElement{ - Name: xml.Name{Local: "file"}, - Attr: []xml.Attr{ - { - Name: xml.Name{Local: "name"}, - Value: dir, + if err = e.EncodeToken( + xml.StartElement{ + Name: xml.Name{Local: "file"}, + Attr: []xml.Attr{ + { + Name: xml.Name{Local: "name"}, + Value: dir, + }, }, - }, - }) - if errEnc != nil { - return errEnc + }); err != nil { + return err } for _, report := range reports { - errEnc2 := e.Encode(report) - if errEnc2 != nil { - return errEnc2 + if err = e.Encode(report); err != nil { + return err } } - errEnc = e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "file"}}) - if errEnc != nil { - return errEnc + if err = e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "file"}}); err != nil { + return err } } - err = e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "checkstyle"}}) - return err + return e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "checkstyle"}}) } -func (r Report) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { +func (r Report) MarshalXML(e *xml.Encoder, _ xml.StartElement) (err error) { + msg := r.Problem.Text + if r.Problem.Details != "" { + msg += "\n" + r.Problem.Details + } startel := xml.StartElement{ Name: xml.Name{Local: "error"}, Attr: []xml.Attr{ @@ -82,7 +84,7 @@ func (r Report) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { }, { Name: xml.Name{Local: "message"}, - Value: fmt.Sprintf("Text:%s\n Details:%s", r.Problem.Text, r.Problem.Details), + Value: msg, }, { Name: xml.Name{Local: "source"}, @@ -90,21 +92,19 @@ func (r Report) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { }, }, } - err := e.EncodeToken(startel) - if err != nil { + if err = e.EncodeToken(startel); err != nil { return err } - err = e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "error"}}) - - return err + return e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "error"}}) } func (cs CheckStyleReporter) Submit(summary Summary) error { checkstyleReport := createCheckstyleReport(summary) xmlString, err := xml.MarshalIndent(checkstyleReport, "", " ") if err != nil { - fmt.Printf("%v", err) + slog.Error("Failed to marshal checkstyle report", slog.Any("err", err)) + return err } - fmt.Fprint(cs.output, string(xml.Header)+string(xmlString)+"\n") - return nil + _, err = fmt.Fprint(cs.output, string(xml.Header)+string(xmlString)+"\n") + return err } diff --git a/internal/reporter/checkstyle_test.go b/internal/reporter/checkstyle_test.go index 0d4714eb..785b9876 100644 --- a/internal/reporter/checkstyle_test.go +++ b/internal/reporter/checkstyle_test.go @@ -61,7 +61,7 @@ func TestCheckstyleReporter(t *testing.T) { output: ` - + `, @@ -90,7 +90,7 @@ func TestCheckstyleReporter(t *testing.T) { output: ` - + `, @@ -121,7 +121,7 @@ func TestCheckstyleReporter(t *testing.T) { output: ` - + `,