Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f52415e

Browse files
committed
Add support for "json" output format
1 parent e6ebbb7 commit f52415e

File tree

6 files changed

+282
-28
lines changed

6 files changed

+282
-28
lines changed

‎check/check.go‎

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,15 @@
22
package check
33

44
import (
5-
"bytes"
65
"fmt"
76
"os"
8-
"text/template"
97

108
"github.com/arduino/arduino-check/check/checkconfigurations"
119
"github.com/arduino/arduino-check/check/checkdata"
12-
"github.com/arduino/arduino-check/check/checklevel"
13-
"github.com/arduino/arduino-check/check/checkresult"
1410
"github.com/arduino/arduino-check/configuration"
1511
"github.com/arduino/arduino-check/configuration/checkmode"
1612
"github.com/arduino/arduino-check/project"
13+
"github.com/arduino/arduino-check/result"
1714
"github.com/arduino/arduino-cli/cli/errorcodes"
1815
"github.com/arduino/arduino-cli/cli/feedback"
1916
"github.com/sirupsen/logrus"
@@ -37,20 +34,23 @@ func RunChecks(project project.Type) {
3734
continue
3835
}
3936

40-
fmt.Printf("Running check %s: ", checkConfiguration.ID)
41-
result, output := checkConfiguration.CheckFunction()
42-
fmt.Printf("%s\n", result.String())
43-
if result == checkresult.NotRun {
44-
// TODO: make the check functions output an explanation for why they didn't run
45-
fmt.Printf("%s: %s\n", checklevel.Notice, output)
46-
} else if result != checkresult.Pass {
47-
checkLevel, err := checklevel.CheckLevel(checkConfiguration)
48-
if err != nil {
49-
feedback.Errorf("Error while determining check level: %v", err)
50-
os.Exit(errorcodes.ErrGeneric)
51-
}
52-
fmt.Printf("%s: %s\n", checkLevel.String(), message(checkConfiguration.MessageTemplate, output))
37+
// Output will be printed after all checks are finished when configured for "json" output format
38+
if configuration.OutputFormat() == "text" {
39+
fmt.Printf("Running check %s: ", checkConfiguration.ID)
5340
}
41+
checkResult, checkOutput := checkConfiguration.CheckFunction()
42+
reportText := result.Record(project, checkConfiguration, checkResult, checkOutput)
43+
if configuration.OutputFormat() == "text" {
44+
fmt.Print(reportText)
45+
}
46+
}
47+
48+
// Checks are finished for this project, so summarize its check results in the report.
49+
result.AddProjectSummaryReport(project)
50+
51+
if configuration.OutputFormat() == "text" {
52+
// Print the project check results summary.
53+
fmt.Print(result.ProjectSummaryText(project))
5454
}
5555
}
5656

@@ -89,14 +89,3 @@ func shouldRun(checkConfiguration checkconfigurations.Type, currentProject proje
8989

9090
return false, fmt.Errorf("Check %s is incorrectly configured", checkConfiguration.ID)
9191
}
92-
93-
// message fills the message template provided by the check configuration with the check output.
94-
// TODO: make checkOutput a struct to allow for more advanced message templating
95-
func message(templateText string, checkOutput string) string {
96-
messageTemplate := template.Must(template.New("messageTemplate").Parse(templateText))
97-
98-
messageBuffer := new(bytes.Buffer)
99-
messageTemplate.Execute(messageBuffer, checkOutput)
100-
101-
return messageBuffer.String()
102-
}

‎configuration/configuration.go‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ func Initialize() {
1616
// TODO configuration according to command line input
1717
// TODO validate target path value, exit if not found
1818
// TODO support multiple paths
19+
// TODO validate output format input
20+
1921
targetPath = paths.New("e:/electronics/arduino/libraries/arduino-check-test-library")
2022

2123
// customCheckModes[checkmode.Permissive] = false
@@ -24,6 +26,8 @@ func Initialize() {
2426
// customCheckModes[checkmode.Official] = false
2527
// superprojectType = projecttype.All
2628

29+
outputFormat = "text"
30+
2731
feedback.SetFormat(feedback.JSON)
2832
logrus.SetLevel(logrus.PanicLevel)
2933

@@ -55,6 +59,13 @@ func Recursive() bool {
5559
return recursive
5660
}
5761

62+
var outputFormat string
63+
64+
// OutputFormat returns the tool output format configuration value.
65+
func OutputFormat() string {
66+
return outputFormat
67+
}
68+
5869
var targetPath *paths.Path
5970

6071
// TargetPath returns the projects search path.

‎configuration/defaults.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
func setDefaults() {
1111
superprojectTypeFilter = projecttype.All
1212
recursive = true
13+
outputFormat = "text"
1314
// TODO: targetPath defaults to current path
1415
}
1516

‎go.sum‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
9898
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
9999
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
100100
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
101+
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
101102
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
102103
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
103104
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=

‎main.go‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,44 @@
11
package main
22

33
import (
4+
"fmt"
45
"os"
56

67
"github.com/arduino/arduino-check/check"
78
"github.com/arduino/arduino-check/configuration"
89
"github.com/arduino/arduino-check/project"
10+
"github.com/arduino/arduino-check/result"
911
"github.com/arduino/arduino-cli/cli/errorcodes"
1012
"github.com/arduino/arduino-cli/cli/feedback"
1113
)
1214

1315
func main() {
1416
configuration.Initialize()
17+
// Must be called after configuration.Initialize()
18+
result.Initialize()
19+
1520
projects, err := project.FindProjects()
1621
if err != nil {
1722
feedback.Errorf("Error while finding projects: %v", err)
1823
os.Exit(errorcodes.ErrGeneric)
1924
}
25+
2026
for _, project := range projects {
2127
check.RunChecks(project)
2228
}
29+
30+
// All projects have been checked, so summarize their check results in the report.
31+
result.AddSummaryReport()
32+
33+
if configuration.OutputFormat() == "text" {
34+
if len(projects) > 1 {
35+
// There are multiple projects, print the summary of check results for all projects.
36+
fmt.Print(result.SummaryText())
37+
}
38+
} else {
39+
// Print the complete JSON formatted report.
40+
fmt.Println(result.JSONReport())
41+
}
42+
2343
// TODO: set exit status according to check results
2444
}

‎result/result.go‎

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
// Package result records check results and provides reports and summary text on those results.
2+
package result
3+
4+
import (
5+
"bytes"
6+
"encoding/json"
7+
"fmt"
8+
"html/template"
9+
"os"
10+
11+
"github.com/arduino/arduino-check/check/checkconfigurations"
12+
"github.com/arduino/arduino-check/check/checklevel"
13+
"github.com/arduino/arduino-check/check/checkresult"
14+
"github.com/arduino/arduino-check/configuration"
15+
"github.com/arduino/arduino-check/configuration/checkmode"
16+
"github.com/arduino/arduino-check/project"
17+
"github.com/arduino/arduino-cli/cli/errorcodes"
18+
"github.com/arduino/arduino-cli/cli/feedback"
19+
"github.com/arduino/go-paths-helper"
20+
)
21+
22+
type reportType struct {
23+
Configuration toolConfigurationReportType `json:"configuration"`
24+
Projects []projectReportType `json:"projects"`
25+
Summary summaryReportType `json:"summary"`
26+
}
27+
28+
type toolConfigurationReportType struct {
29+
Paths []*paths.Path `json:"paths"`
30+
ProjectType string `json:"projectType"`
31+
Recursive bool `json:"recursive"`
32+
}
33+
34+
type projectReportType struct {
35+
Path *paths.Path `json:"path"`
36+
ProjectType string `json:"projectType"`
37+
Configuration projectConfigurationReportType `json:"configuration"`
38+
Checks []checkReportType `json:"checks"`
39+
Summary summaryReportType `json:"summary"`
40+
}
41+
42+
type projectConfigurationReportType struct {
43+
Permissive bool `json:"permissive"`
44+
LibraryManagerSubmit bool `json:"libraryManagerSubmit"`
45+
LibraryManagerUpdate bool `json:"libraryManagerUpdate"`
46+
Official bool `json:"official"`
47+
}
48+
49+
type checkReportType struct {
50+
Category string `json:"category"`
51+
Subcategory string `json:"subcategory"`
52+
ID string `json:"ID"`
53+
Brief string `json:"brief"`
54+
Description string `json:"description"`
55+
Result string `json:"result"`
56+
Level string `json:"level"`
57+
Message string `json:"message"`
58+
}
59+
60+
type summaryReportType struct {
61+
Pass bool `json:"pass"`
62+
WarningCount int `json:"warningCount"`
63+
ErrorCount int `json:"errorCount"`
64+
}
65+
66+
var report reportType
67+
68+
// Initialize adds the tool configuration data to the report.
69+
func Initialize() {
70+
report.Configuration = toolConfigurationReportType{
71+
Paths: []*paths.Path{configuration.TargetPath()},
72+
ProjectType: configuration.SuperprojectTypeFilter().String(),
73+
Recursive: configuration.Recursive(),
74+
}
75+
}
76+
77+
// Record records the result of a check and returns a text summary for it.
78+
func Record(checkedProject project.Type, checkConfiguration checkconfigurations.Type, checkResult checkresult.Type, checkOutput string) string {
79+
checkMessage := message(checkConfiguration.MessageTemplate, checkOutput)
80+
81+
checkLevel, err := checklevel.CheckLevel(checkConfiguration)
82+
if err != nil {
83+
feedback.Errorf("Error while determining check level: %v", err)
84+
os.Exit(errorcodes.ErrGeneric)
85+
}
86+
87+
summaryText := fmt.Sprintf("%v\n", checkResult.String())
88+
89+
if checkResult == checkresult.NotRun {
90+
// TODO: make the check functions output an explanation for why they didn't run
91+
summaryText += fmt.Sprintf("%s: %s\n", checklevel.Notice.String(), checkOutput)
92+
} else if checkResult != checkresult.Pass {
93+
summaryText += fmt.Sprintf("%s: %s\n", checkLevel.String(), checkMessage)
94+
}
95+
96+
checkReport := checkReportType{
97+
Category: checkConfiguration.Category,
98+
Subcategory: checkConfiguration.Subcategory,
99+
ID: checkConfiguration.ID,
100+
Brief: checkConfiguration.Brief,
101+
Description: checkConfiguration.Description,
102+
Result: checkResult.String(),
103+
Level: checkLevel.String(),
104+
Message: checkMessage,
105+
}
106+
107+
reportExists, projectReportIndex := getProjectReportIndex(checkedProject.Path)
108+
if !reportExists {
109+
// There is no existing report for this project.
110+
report.Projects = append(
111+
report.Projects,
112+
projectReportType{
113+
Path: checkedProject.Path,
114+
ProjectType: checkedProject.ProjectType.String(),
115+
Configuration: projectConfigurationReportType{
116+
Permissive: configuration.CheckModes(checkedProject.ProjectType)[checkmode.Permissive],
117+
LibraryManagerSubmit: configuration.CheckModes(checkedProject.ProjectType)[checkmode.Permissive],
118+
LibraryManagerUpdate: configuration.CheckModes(checkedProject.ProjectType)[checkmode.LibraryManagerIndexed],
119+
Official: configuration.CheckModes(checkedProject.ProjectType)[checkmode.Official],
120+
},
121+
Checks: []checkReportType{checkReport},
122+
},
123+
)
124+
} else {
125+
// There's already a report for this project, just add the checks report to it
126+
report.Projects[projectReportIndex].Checks = append(report.Projects[projectReportIndex].Checks, checkReport)
127+
}
128+
129+
return summaryText
130+
}
131+
132+
// AddProjectSummaryReport summarizes the results of all checks on the given project and adds it to the report.
133+
func AddProjectSummaryReport(checkedProject project.Type) {
134+
reportExists, projectReportIndex := getProjectReportIndex(checkedProject.Path)
135+
if !reportExists {
136+
panic(fmt.Sprintf("Unable to find report for %v when generating report summary", checkedProject.Path))
137+
}
138+
139+
pass := true
140+
warningCount := 0
141+
errorCount := 0
142+
for _, checkReport := range report.Projects[projectReportIndex].Checks {
143+
if checkReport.Result == checkresult.Fail.String() {
144+
if checkReport.Level == checklevel.Warning.String() {
145+
warningCount += 1
146+
} else if checkReport.Level == checklevel.Error.String() {
147+
errorCount += 1
148+
pass = false
149+
}
150+
}
151+
}
152+
153+
report.Projects[projectReportIndex].Summary = summaryReportType{
154+
Pass: pass,
155+
WarningCount: warningCount,
156+
ErrorCount: errorCount,
157+
}
158+
}
159+
160+
// ProjectSummaryText returns a text summary of the check results for the given project.
161+
func ProjectSummaryText(checkedProject project.Type) string {
162+
reportExists, projectReportIndex := getProjectReportIndex(checkedProject.Path)
163+
if !reportExists {
164+
panic(fmt.Sprintf("Unable to find report for %v when generating report summary text", checkedProject.Path))
165+
}
166+
167+
projectSummaryReport := report.Projects[projectReportIndex].Summary
168+
return fmt.Sprintf("\nFinished checking project. Results:\nWarning count: %v\nError count: %v\nChecks passed: %v\n\n", projectSummaryReport.WarningCount, projectSummaryReport.ErrorCount, projectSummaryReport.Pass)
169+
}
170+
171+
// AddSummaryReport summarizes the check results for all projects and adds it to the report.
172+
func AddSummaryReport() {
173+
pass := true
174+
warningCount := 0
175+
errorCount := 0
176+
for _, projectReport := range report.Projects {
177+
if !projectReport.Summary.Pass {
178+
pass = false
179+
}
180+
warningCount += projectReport.Summary.WarningCount
181+
errorCount += projectReport.Summary.ErrorCount
182+
}
183+
184+
report.Summary = summaryReportType{
185+
Pass: pass,
186+
WarningCount: warningCount,
187+
ErrorCount: errorCount,
188+
}
189+
}
190+
191+
// SummaryText returns a text summary of the cumulative check results.
192+
func SummaryText() string {
193+
return fmt.Sprintf("Finished checking projects. Results:\nWarning count: %v\nError count: %v\nChecks passed: %v\n", report.Summary.WarningCount, report.Summary.ErrorCount, report.Summary.Pass)
194+
}
195+
196+
// Report returns a JSON formatted report of checks on all projects.
197+
func JSONReport() string {
198+
return string(jsonReportRaw())
199+
}
200+
201+
func jsonReportRaw() []byte {
202+
reportJSON, err := json.MarshalIndent(report, "", " ")
203+
if err != nil {
204+
panic(fmt.Sprintf("Error while formatting checks report: %v", err))
205+
}
206+
207+
return reportJSON
208+
}
209+
210+
func getProjectReportIndex(projectPath *paths.Path) (bool, int) {
211+
var index int
212+
var projectReport projectReportType
213+
for index, projectReport = range report.Projects {
214+
if projectReport.Path == projectPath {
215+
return true, index
216+
}
217+
}
218+
219+
// There is no element in the report for this project.
220+
return false, index + 1
221+
}
222+
223+
// message fills the message template provided by the check configuration with the check output.
224+
// TODO: make checkOutput a struct to allow for more advanced message templating
225+
func message(templateText string, checkOutput string) string {
226+
messageTemplate := template.Must(template.New("messageTemplate").Parse(templateText))
227+
228+
messageBuffer := new(bytes.Buffer)
229+
messageTemplate.Execute(messageBuffer, checkOutput)
230+
231+
return messageBuffer.String()
232+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /