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: `
-
+
`,