-
-
Notifications
You must be signed in to change notification settings - Fork 12
Create tool for linting Arduino projects #1
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
1646215
Add first draft of library.properties schema
per1234 e17c4d7
Initial commit of code
per1234 ecdce06
Use consistent approach to handling custom types
per1234 12411ac
Rename projects package to project
per1234 b261e7b
Schema: update name pattern to new specification
per1234 4d024ed
Schema: remove the non-capturing group syntax from the version regex
per1234 89d6372
Schema: fix the misspelled optional field name regex
per1234 c04946b
Correctly handle "NotRun" check results
per1234 c39f587
Create libraryproperties package and add validation helper functions
per1234 bd5b370
Add "notice" checklevel
per1234 416233a
Rename "Skipped" checkresult to "Skip"
per1234 fce1f2c
Add reminder comment re: handling exit status
per1234 8b527e4
Add more checks
per1234 132ce6d
Schema: make name regex more minimal
per1234 abd360f
Split check functions into separate files according to project type
per1234 3682cee
Add check for .pde sketch file extension
per1234 ac1bec4
Output error messages from projects.FindProjects()
per1234 27a6787
Fix check configuration system
per1234 f88be58
Start setting up logging
per1234 19ee67b
Fix bug with project discovery system
per1234 26046fb
Provide meaningful output when checks don't run due to required data
per1234 ed14838
Add a TODO comment
per1234 2c5c11a
Set up configuration to be able to easily experiment with different s…
per1234 f8da280
Remove "Pass" check level
per1234 cd49ac3
Add/improve code comments
per1234 952112d
Add TODO comment re: coniguration.init() vs configuration.Initialize()
per1234 00178a7
Rename checkconfigurations.Type.Name field to checkconfigurations.Typ…
per1234 c1c3b9c
Rename configuration.SuperprojectType() to configuration.Superproject…
per1234 63a68f5
Rename project.findProjects() to project.findProjectsUnderPath()
per1234 8547011
Add a getter for checkconfigurations.configurations
per1234 f9027f8
Improve organization of code
per1234 42039d9
Return error when check run configuration is missing
per1234 658cdf6
Use packageindex.HasValidExtension() in package index detection code
per1234 b1a0224
Define supported and valid library examples folder names in the libra…
per1234 598ae7f
Define valid platform bundled libraries folder names in the platform …
per1234 e5133ae
Add go.mod files
8af60f3
Only print skipped check information to log
per1234 a93a6d0
Add support for "json" output format
per1234 528aa98
Set exit status according to check results
per1234 2c29553
Add support for report file option
per1234 9bde283
Return boolean variable values directly from functions
per1234 b3457a2
Reduce verbosity of Boolean comparisons
per1234 b40200e
Remove unncessary use of String() method
per1234 a842f06
Eliminate duplicate code in subproject discovery
per1234 2fe0250
Panic if subproject discovery was not configured for project type
per1234 918d845
Refactor .pde sketch check function's output code
per1234 2517a0b
Add doc comments for check functions
per1234 9ddc1bb
Add project type matcher method
per1234 e692238
Use more apropriate project type in log message
per1234 561d88e
Use more appropriate variable name for project type filter
per1234 b9f951b
Refactor project identification functions
per1234 fbe79ee
Support multiple report instances
per1234 6894049
Refactor names in the result package
per1234 09bbd6a
Use an "enum" for the output format types
per1234 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add support for "json" output format
- Loading branch information
commit a93a6d06496ca5198a103676ab8f216c7688d1b7
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,43 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/arduino/arduino-check/check" | ||
"github.com/arduino/arduino-check/configuration" | ||
"github.com/arduino/arduino-check/project" | ||
"github.com/arduino/arduino-check/result" | ||
"github.com/arduino/arduino-check/result/feedback" | ||
) | ||
|
||
func main() { | ||
configuration.Initialize() | ||
// Must be called after configuration.Initialize() | ||
result.Initialize() | ||
|
||
projects, err := project.FindProjects() | ||
if err != nil { | ||
feedback.Errorf("Error while finding projects: %v", err) | ||
os.Exit(1) | ||
} | ||
|
||
for _, project := range projects { | ||
check.RunChecks(project) | ||
} | ||
|
||
// All projects have been checked, so summarize their check results in the report. | ||
result.AddSummaryReport() | ||
|
||
if configuration.OutputFormat() == "text" { | ||
if len(projects) > 1 { | ||
// There are multiple projects, print the summary of check results for all projects. | ||
fmt.Print(result.SummaryText()) | ||
} | ||
} else { | ||
// Print the complete JSON formatted report. | ||
fmt.Println(result.JSONReport()) | ||
} | ||
|
||
// TODO: set exit status according to check results | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
// Package result records check results and provides reports and summary text on those results. | ||
package result | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"html/template" | ||
"os" | ||
|
||
"github.com/arduino/arduino-check/check/checkconfigurations" | ||
"github.com/arduino/arduino-check/check/checklevel" | ||
"github.com/arduino/arduino-check/check/checkresult" | ||
"github.com/arduino/arduino-check/configuration" | ||
"github.com/arduino/arduino-check/configuration/checkmode" | ||
"github.com/arduino/arduino-check/project" | ||
"github.com/arduino/arduino-check/result/feedback" | ||
"github.com/arduino/go-paths-helper" | ||
) | ||
|
||
type reportType struct { | ||
per1234 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Configuration toolConfigurationReportType `json:"configuration"` | ||
Projects []projectReportType `json:"projects"` | ||
Summary summaryReportType `json:"summary"` | ||
} | ||
|
||
type toolConfigurationReportType struct { | ||
Paths []*paths.Path `json:"paths"` | ||
ProjectType string `json:"projectType"` | ||
Recursive bool `json:"recursive"` | ||
} | ||
|
||
type projectReportType struct { | ||
Path *paths.Path `json:"path"` | ||
ProjectType string `json:"projectType"` | ||
Configuration projectConfigurationReportType `json:"configuration"` | ||
Checks []checkReportType `json:"checks"` | ||
Summary summaryReportType `json:"summary"` | ||
} | ||
|
||
type projectConfigurationReportType struct { | ||
Permissive bool `json:"permissive"` | ||
LibraryManagerSubmit bool `json:"libraryManagerSubmit"` | ||
LibraryManagerUpdate bool `json:"libraryManagerUpdate"` | ||
Official bool `json:"official"` | ||
} | ||
|
||
type checkReportType struct { | ||
Category string `json:"category"` | ||
Subcategory string `json:"subcategory"` | ||
ID string `json:"ID"` | ||
Brief string `json:"brief"` | ||
Description string `json:"description"` | ||
Result string `json:"result"` | ||
Level string `json:"level"` | ||
Message string `json:"message"` | ||
} | ||
|
||
type summaryReportType struct { | ||
Pass bool `json:"pass"` | ||
WarningCount int `json:"warningCount"` | ||
ErrorCount int `json:"errorCount"` | ||
} | ||
|
||
var report reportType | ||
|
||
// Initialize adds the tool configuration data to the report. | ||
func Initialize() { | ||
report.Configuration = toolConfigurationReportType{ | ||
Paths: []*paths.Path{configuration.TargetPath()}, | ||
ProjectType: configuration.SuperprojectTypeFilter().String(), | ||
Recursive: configuration.Recursive(), | ||
} | ||
} | ||
|
||
// Record records the result of a check and returns a text summary for it. | ||
func Record(checkedProject project.Type, checkConfiguration checkconfigurations.Type, checkResult checkresult.Type, checkOutput string) string { | ||
checkMessage := message(checkConfiguration.MessageTemplate, checkOutput) | ||
|
||
checkLevel, err := checklevel.CheckLevel(checkConfiguration) | ||
if err != nil { | ||
feedback.Errorf("Error while determining check level: %v", err) | ||
os.Exit(1) | ||
} | ||
|
||
summaryText := fmt.Sprintf("%v\n", checkResult.String()) | ||
|
||
if checkResult == checkresult.NotRun { | ||
// TODO: make the check functions output an explanation for why they didn't run | ||
summaryText += fmt.Sprintf("%s: %s\n", checklevel.Notice.String(), checkOutput) | ||
} else if checkResult != checkresult.Pass { | ||
summaryText += fmt.Sprintf("%s: %s\n", checkLevel.String(), checkMessage) | ||
} | ||
|
||
checkReport := checkReportType{ | ||
Category: checkConfiguration.Category, | ||
Subcategory: checkConfiguration.Subcategory, | ||
ID: checkConfiguration.ID, | ||
Brief: checkConfiguration.Brief, | ||
Description: checkConfiguration.Description, | ||
Result: checkResult.String(), | ||
Level: checkLevel.String(), | ||
Message: checkMessage, | ||
} | ||
|
||
reportExists, projectReportIndex := getProjectReportIndex(checkedProject.Path) | ||
if !reportExists { | ||
// There is no existing report for this project. | ||
report.Projects = append( | ||
report.Projects, | ||
projectReportType{ | ||
Path: checkedProject.Path, | ||
ProjectType: checkedProject.ProjectType.String(), | ||
Configuration: projectConfigurationReportType{ | ||
Permissive: configuration.CheckModes(checkedProject.ProjectType)[checkmode.Permissive], | ||
LibraryManagerSubmit: configuration.CheckModes(checkedProject.ProjectType)[checkmode.Permissive], | ||
LibraryManagerUpdate: configuration.CheckModes(checkedProject.ProjectType)[checkmode.LibraryManagerIndexed], | ||
Official: configuration.CheckModes(checkedProject.ProjectType)[checkmode.Official], | ||
}, | ||
Checks: []checkReportType{checkReport}, | ||
}, | ||
) | ||
} else { | ||
// There's already a report for this project, just add the checks report to it | ||
report.Projects[projectReportIndex].Checks = append(report.Projects[projectReportIndex].Checks, checkReport) | ||
} | ||
|
||
return summaryText | ||
} | ||
|
||
// AddProjectSummaryReport summarizes the results of all checks on the given project and adds it to the report. | ||
func AddProjectSummaryReport(checkedProject project.Type) { | ||
reportExists, projectReportIndex := getProjectReportIndex(checkedProject.Path) | ||
if !reportExists { | ||
panic(fmt.Sprintf("Unable to find report for %v when generating report summary", checkedProject.Path)) | ||
} | ||
|
||
pass := true | ||
warningCount := 0 | ||
errorCount := 0 | ||
for _, checkReport := range report.Projects[projectReportIndex].Checks { | ||
if checkReport.Result == checkresult.Fail.String() { | ||
if checkReport.Level == checklevel.Warning.String() { | ||
warningCount += 1 | ||
} else if checkReport.Level == checklevel.Error.String() { | ||
errorCount += 1 | ||
pass = false | ||
} | ||
} | ||
} | ||
|
||
report.Projects[projectReportIndex].Summary = summaryReportType{ | ||
Pass: pass, | ||
WarningCount: warningCount, | ||
ErrorCount: errorCount, | ||
} | ||
} | ||
|
||
// ProjectSummaryText returns a text summary of the check results for the given project. | ||
func ProjectSummaryText(checkedProject project.Type) string { | ||
reportExists, projectReportIndex := getProjectReportIndex(checkedProject.Path) | ||
if !reportExists { | ||
panic(fmt.Sprintf("Unable to find report for %v when generating report summary text", checkedProject.Path)) | ||
} | ||
|
||
projectSummaryReport := report.Projects[projectReportIndex].Summary | ||
return fmt.Sprintf("\nFinished checking project. Results:\nWarning count: %v\nError count: %v\nChecks passed: %v\n\n", projectSummaryReport.WarningCount, projectSummaryReport.ErrorCount, projectSummaryReport.Pass) | ||
} | ||
|
||
// AddSummaryReport summarizes the check results for all projects and adds it to the report. | ||
func AddSummaryReport() { | ||
pass := true | ||
warningCount := 0 | ||
errorCount := 0 | ||
for _, projectReport := range report.Projects { | ||
if !projectReport.Summary.Pass { | ||
pass = false | ||
} | ||
warningCount += projectReport.Summary.WarningCount | ||
errorCount += projectReport.Summary.ErrorCount | ||
} | ||
|
||
report.Summary = summaryReportType{ | ||
Pass: pass, | ||
WarningCount: warningCount, | ||
ErrorCount: errorCount, | ||
} | ||
} | ||
|
||
// SummaryText returns a text summary of the cumulative check results. | ||
func SummaryText() string { | ||
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) | ||
} | ||
|
||
// Report returns a JSON formatted report of checks on all projects. | ||
func JSONReport() string { | ||
return string(jsonReportRaw()) | ||
} | ||
|
||
func jsonReportRaw() []byte { | ||
reportJSON, err := json.MarshalIndent(report, "", " ") | ||
if err != nil { | ||
panic(fmt.Sprintf("Error while formatting checks report: %v", err)) | ||
} | ||
|
||
return reportJSON | ||
} | ||
|
||
func getProjectReportIndex(projectPath *paths.Path) (bool, int) { | ||
var index int | ||
var projectReport projectReportType | ||
for index, projectReport = range report.Projects { | ||
if projectReport.Path == projectPath { | ||
return true, index | ||
} | ||
} | ||
|
||
// There is no element in the report for this project. | ||
return false, index + 1 | ||
} | ||
|
||
// message fills the message template provided by the check configuration with the check output. | ||
// TODO: make checkOutput a struct to allow for more advanced message templating | ||
func message(templateText string, checkOutput string) string { | ||
messageTemplate := template.Must(template.New("messageTemplate").Parse(templateText)) | ||
|
||
messageBuffer := new(bytes.Buffer) | ||
messageTemplate.Execute(messageBuffer, checkOutput) | ||
|
||
return messageBuffer.String() | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.