From 1646215f27e00a5e52c6994eb7c0568df4673890 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 15 Sep 2020 17:47:59 -0700 Subject: [PATCH 01/54] Add first draft of library.properties schema --- arduino-library-properties-schema.json | 84 ++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 arduino-library-properties-schema.json diff --git a/arduino-library-properties-schema.json b/arduino-library-properties-schema.json new file mode 100644 index 000000000..e45372b20 --- /dev/null +++ b/arduino-library-properties-schema.json @@ -0,0 +1,84 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://github.com/arduino/arduino-check/arduino-library-properties-schema.json", + "title": "Arduino library.properties JSON schema", + "description": "library.properties is the metadata file for Arduino libraries. See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-metadata", + "$comment": "For information on the Arduino library.properties format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 63, + "pattern": "^[a-zA-Z][a-zA-Z\\d _\\.\\-]*$" + }, + "version": { + "type": "string", + "$comment": "https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string adjusted to also allow MAJOR.MINOR", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*))?(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + }, + "author": { + "type": "string", + "minLength": 1 + }, + "maintainer": { + "type": "string", + "minLength": 1 + }, + "sentence": { + "type": "string", + "minLength": 1 + }, + "paragraph": { + "type": "string" + }, + "category": { + "type": "string", + "enum": [ + "Display", + "Communication", + "Signal Input/Output", + "Sensors", + "Device Control", + "Timing", + "Data Storage", + "Data Processing", + "Other" + ] + }, + "url": { + "type": "string", + "format": "uri" + }, + "architectures": { + "type": "string", + "minLength": 1 + }, + "depends": { + "type": "string", + "pattern": "^[a-zA-Z][a-zA-Z\\d _\\.\\-,]*$" + }, + "dot_a_linkage": { + "type": "string", + "enum": ["true", "false"] + }, + "includes": { + "type": "string", + "minLength": 1 + }, + "precompiled": { + "type": "string", + "enum": ["true", "full", "false"] + }, + "ldflags": { + "type": "string" + } + }, + "propertyNames": { + "not": { + "$comment": "Misspelled optional property names", + "pattern": "^(depend)|(D((epends?)|(EPENDS?)))|(dot_a_linkages)|(dot-?a-?linkages?)|(D(((ot)|(OT))[_-]?((a)|(A))[_-]?((linkages?)|(LINKAGES?))))|(include)|(I((ncludes?)|(NCLUDES?)))|(precompile)|(pre[-_]compiled?)|(P((re[-_]?compiled?)|(RE[-_]?COMPILED?)))|(ldflag)|(ld[-_]flags?)|(L((d[-_]?flags?)|(D[-_]?FLAGS?)))$" + } + }, + "required": ["name", "version", "author", "maintainer", "sentence", "paragraph", "category", "url", "architectures"] +} From e17c4d74c89bd405cbcd4e309e08fca236712dee Mon Sep 17 00:00:00 2001 From: per1234 Date: Wed, 16 Sep 2020 20:52:28 -0700 Subject: [PATCH 02/54] Initial commit of code --- README.md | 6 + check/check.go | 66 +++++ .../checkconfigurations.go | 51 ++++ check/checkdata/checkdata.go | 53 ++++ check/checkfunctions/checkfunctions.go | 10 + check/checklevel/checklevel.go | 35 +++ check/checklevel/level_string.go | 26 ++ check/checklevel/levels.go | 12 + check/checkresult/result_string.go | 26 ++ check/checkresult/results.go | 13 + configuration/configuration.go | 63 ++++ configuration/defaults.go | 38 +++ main.go | 15 + projects/library/library.go | 34 +++ projects/library/libraryproperties.go | 27 ++ projects/packageindex/packageindex.go | 34 +++ projects/platform/platform.go | 36 +++ projects/projects.go | 271 ++++++++++++++++++ projects/projecttype/type_string.go | 28 ++ projects/projecttype/types.go | 13 + projects/sketch/sketch.go | 14 + 21 files changed, 871 insertions(+) create mode 100644 README.md create mode 100644 check/check.go create mode 100644 check/checkconfigurations/checkconfigurations.go create mode 100644 check/checkdata/checkdata.go create mode 100644 check/checkfunctions/checkfunctions.go create mode 100644 check/checklevel/checklevel.go create mode 100644 check/checklevel/level_string.go create mode 100644 check/checklevel/levels.go create mode 100644 check/checkresult/result_string.go create mode 100644 check/checkresult/results.go create mode 100644 configuration/configuration.go create mode 100644 configuration/defaults.go create mode 100644 main.go create mode 100644 projects/library/library.go create mode 100644 projects/library/libraryproperties.go create mode 100644 projects/packageindex/packageindex.go create mode 100644 projects/platform/platform.go create mode 100644 projects/projects.go create mode 100644 projects/projecttype/type_string.go create mode 100644 projects/projecttype/types.go create mode 100644 projects/sketch/sketch.go diff --git a/README.md b/README.md new file mode 100644 index 000000000..07fa063f1 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# arduino-check + +`arduino-check` automatically checks for common problems in your [Arduino](https://www.arduino.cc/) projects: + +- Sketches +- Libraries diff --git a/check/check.go b/check/check.go new file mode 100644 index 000000000..b95b326cf --- /dev/null +++ b/check/check.go @@ -0,0 +1,66 @@ +package check + +import ( + "bytes" + "fmt" + "text/template" + + "github.com/arduino/arduino-check/check/checkconfigurations" + "github.com/arduino/arduino-check/check/checkdata" + "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/projects" +) + +func shouldRun(checkConfiguration checkconfigurations.Configuration, currentProject projects.Project) bool { + checkModes := configuration.CheckModes(projects.SuperprojectType(currentProject)) + + if checkConfiguration.ProjectType != currentProject.Type { + return false + } + + for _, disableMode := range checkConfiguration.DisableModes { + if checkModes[disableMode] == true { + return false + } + } + + for _, enableMode := range checkConfiguration.EnableModes { + if checkModes[enableMode] == true { + return true + } + } + return false +} + +// 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() +} + +func RunChecks(project projects.Project) { + fmt.Printf("Checking %s in %s\n", project.Type.String(), project.Path.String()) + + checkdata.Initialize(project) + + for _, checkConfiguration := range checkconfigurations.Configurations { + if !shouldRun(checkConfiguration, project) { + // TODO: this should only be printed to log and in verbose mode + fmt.Printf("Skipping check: %s\n", checkConfiguration.ID) + continue + } + + fmt.Printf("Running check %s: ", checkConfiguration.ID) + result, output := checkConfiguration.CheckFunction() + fmt.Printf("%s\n", result.String()) + if result != checkresult.Pass { + fmt.Printf("%s: %s\n", checklevel.CheckLevel(checkConfiguration).String(), message(checkConfiguration.MessageTemplate, output)) + } + } +} diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go new file mode 100644 index 000000000..db71e26e7 --- /dev/null +++ b/check/checkconfigurations/checkconfigurations.go @@ -0,0 +1,51 @@ +package checkconfigurations + +import ( + "github.com/arduino/arduino-check/check/checkfunctions" + "github.com/arduino/arduino-check/configuration" + "github.com/arduino/arduino-check/projects/projecttype" +) + +type Configuration struct { + ProjectType projecttype.Type + // Arbitrary text for the log + Category string + Subcategory string + // Unique check identifier + ID string + Name string + Description string + // The warning/error message template displayed when the check fails + // The check function output will be filled into the template + MessageTemplate string + // Check is disabled when tool is in any of these modes + DisableModes []configuration.CheckMode + // Check is only enabled when tool is in one of these modes + EnableModes []configuration.CheckMode + // In these modes, failed check is treated as an error, otherwise it's handled as normal. + PromoteModes []configuration.CheckMode + InfoModes []configuration.CheckMode + WarningModes []configuration.CheckMode + ErrorModes []configuration.CheckMode + CheckFunction checkfunctions.CheckFunction +} + +// Checks is an array of structs that define the configuration of each check. +var Configurations = []Configuration{ + { + ProjectType: projecttype.Library, + Category: "library.properties", + Subcategory: "name field", + ID: "LP001", + Name: "invalid format", + Description: "", + MessageTemplate: "library.properties has an invalid format: {{.}}", + DisableModes: nil, + EnableModes: []configuration.CheckMode{configuration.Default}, + PromoteModes: nil, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []configuration.CheckMode{configuration.Default}, + CheckFunction: checkfunctions.CheckLibraryPropertiesFormat, + }, +} diff --git a/check/checkdata/checkdata.go b/check/checkdata/checkdata.go new file mode 100644 index 000000000..f0f82c0e4 --- /dev/null +++ b/check/checkdata/checkdata.go @@ -0,0 +1,53 @@ +package checkdata + +import ( + "github.com/arduino/arduino-check/projects" + "github.com/arduino/arduino-check/projects/library" + "github.com/arduino/arduino-check/projects/projecttype" + "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" + "github.com/xeipuuv/gojsonschema" +) + +var projectType projecttype.Type + +func ProjectType() projecttype.Type { + return projectType +} + +var projectPath *paths.Path + +func ProjectPath() *paths.Path { + return projectPath +} + +var libraryPropertiesLoadError error + +func LibraryPropertiesLoadError() error { + return libraryPropertiesLoadError +} + +var libraryProperties *properties.Map + +func LibraryProperties() *properties.Map { + return libraryProperties +} + +var libraryPropertiesSchemaValidationResult *gojsonschema.Result + +func LibraryPropertiesSchemaValidationResult() *gojsonschema.Result { + return libraryPropertiesSchemaValidationResult +} + +func Initialize(project projects.Project) { + projectType = project.Type + projectPath = project.Path + switch project.Type { + case projecttype.Sketch: + case projecttype.Library: + libraryProperties, libraryPropertiesLoadError = library.Properties(project.Path) + libraryPropertiesSchemaValidationResult = library.ValidateProperties(libraryProperties) + case projecttype.Platform: + case projecttype.PackageIndex: + } +} diff --git a/check/checkfunctions/checkfunctions.go b/check/checkfunctions/checkfunctions.go new file mode 100644 index 000000000..5e44b3639 --- /dev/null +++ b/check/checkfunctions/checkfunctions.go @@ -0,0 +1,10 @@ +package checkfunctions + +import "github.com/arduino/arduino-check/check/checkresult" + +// output is the contextual information that will be added to the stock message +type CheckFunction func() (result checkresult.Result, output string) + +func CheckLibraryPropertiesFormat() (result checkresult.Result, output string) { + return +} diff --git a/check/checklevel/checklevel.go b/check/checklevel/checklevel.go new file mode 100644 index 000000000..db12faa86 --- /dev/null +++ b/check/checklevel/checklevel.go @@ -0,0 +1,35 @@ +package checklevel + +import ( + "github.com/arduino/arduino-check/check/checkconfigurations" + "github.com/arduino/arduino-check/configuration" +) + +func CheckLevel(checkConfiguration checkconfigurations.Configuration) Level { + configurationCheckModes := configuration.CheckModes(checkConfiguration.ProjectType) + for _, promoteMode := range checkConfiguration.PromoteModes { + if configurationCheckModes[promoteMode] == true { + return Error + } + } + + for _, errorMode := range checkConfiguration.ErrorModes { + if configurationCheckModes[errorMode] == true { + return Error + } + } + + for _, warningMode := range checkConfiguration.WarningModes { + if configurationCheckModes[warningMode] == true { + return Warning + } + } + + for _, infoMode := range checkConfiguration.InfoModes { + if configurationCheckModes[infoMode] == true { + return Info + } + } + + return Pass +} diff --git a/check/checklevel/level_string.go b/check/checklevel/level_string.go new file mode 100644 index 000000000..c5337cc5b --- /dev/null +++ b/check/checklevel/level_string.go @@ -0,0 +1,26 @@ +// Code generated by "stringer -type=Level -linecomment"; DO NOT EDIT. + +package checklevel + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Info-0] + _ = x[Warning-1] + _ = x[Error-2] + _ = x[Pass-3] +} + +const _Level_name = "infowarningerrorpass" + +var _Level_index = [...]uint8{0, 4, 11, 16, 20} + +func (i Level) String() string { + if i < 0 || i >= Level(len(_Level_index)-1) { + return "Level(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Level_name[_Level_index[i]:_Level_index[i+1]] +} diff --git a/check/checklevel/levels.go b/check/checklevel/levels.go new file mode 100644 index 000000000..463678640 --- /dev/null +++ b/check/checklevel/levels.go @@ -0,0 +1,12 @@ +package checklevel + +//go:generate stringer -type=Level -linecomment +type Level int + +// Line comments set the string for each level +const ( + Info Level = iota // info + Warning // warning + Error // error + Pass // pass +) diff --git a/check/checkresult/result_string.go b/check/checkresult/result_string.go new file mode 100644 index 000000000..379efd430 --- /dev/null +++ b/check/checkresult/result_string.go @@ -0,0 +1,26 @@ +// Code generated by "stringer -type=Result -linecomment"; DO NOT EDIT. + +package checkresult + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Pass-0] + _ = x[Fail-1] + _ = x[Skipped-2] + _ = x[NotRun-3] +} + +const _Result_name = "passfailskippednot run" + +var _Result_index = [...]uint8{0, 4, 8, 15, 22} + +func (i Result) String() string { + if i < 0 || i >= Result(len(_Result_index)-1) { + return "Result(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Result_name[_Result_index[i]:_Result_index[i+1]] +} diff --git a/check/checkresult/results.go b/check/checkresult/results.go new file mode 100644 index 000000000..36849bf12 --- /dev/null +++ b/check/checkresult/results.go @@ -0,0 +1,13 @@ +package checkresult + +//go:generate stringer -type=Result -linecomment +type Result int + +const ( + Pass Result = iota // pass + Fail // fail + // The check is configured to be skipped in the current tool configuration mode + Skipped // skipped + // An error prevented the check from running + NotRun // not run +) diff --git a/configuration/configuration.go b/configuration/configuration.go new file mode 100644 index 000000000..bf2b47716 --- /dev/null +++ b/configuration/configuration.go @@ -0,0 +1,63 @@ +package configuration + +import ( + "github.com/arduino/arduino-check/projects/projecttype" + "github.com/arduino/go-paths-helper" +) + +type CheckMode int + +const ( + Permissive CheckMode = iota + LibraryManagerSubmission + LibraryManagerIndexed + Official + Default +) + +func Initialize() { + setDefaults() + // TODO configuration according to command line input + // TODO validate target path value, exit if not found + targetPath = paths.New("e:/electronics/arduino/libraries/arduino-check-test-library") + superprojectType = projecttype.Library +} + +var customCheckModes map[CheckMode]bool + +func CheckModes(superprojectType projecttype.Type) map[CheckMode]bool { + checkModes := make(map[CheckMode]bool) + + // Merge the default settings with any custom settings specified by the user + for key, defaultValue := range defaultCheckModes[superprojectType] { + customCheckModeValue, customCheckModeIsConfigured := customCheckModes[key] + if customCheckModeIsConfigured { + checkModes[key] = customCheckModeValue + } else { + checkModes[key] = defaultValue + } + } + + // This mode is always enabled + checkModes[Default] = true + + return checkModes +} + +var superprojectType projecttype.Type + +func SuperprojectType() projecttype.Type { + return superprojectType +} + +var recursive bool + +func Recursive() bool { + return recursive +} + +var targetPath *paths.Path + +func TargetPath() *paths.Path { + return targetPath +} diff --git a/configuration/defaults.go b/configuration/defaults.go new file mode 100644 index 000000000..d3cc1079a --- /dev/null +++ b/configuration/defaults.go @@ -0,0 +1,38 @@ +package configuration + +import "github.com/arduino/arduino-check/projects/projecttype" + +func setDefaults() { + superprojectType = projecttype.All + recursive = true + // TODO: targetPath defaults to current path +} + +// Default check modes for each superproject type +// Subprojects use the same check modes as the superproject +var defaultCheckModes = map[projecttype.Type]map[CheckMode]bool{ + projecttype.Sketch: { + Permissive: false, + LibraryManagerSubmission: false, + LibraryManagerIndexed: false, + Official: false, + }, + projecttype.Library: { + Permissive: false, + LibraryManagerSubmission: true, + LibraryManagerIndexed: false, + Official: false, + }, + projecttype.Platform: { + Permissive: false, + LibraryManagerSubmission: false, + LibraryManagerIndexed: false, + Official: false, + }, + projecttype.PackageIndex: { + Permissive: false, + LibraryManagerSubmission: false, + LibraryManagerIndexed: false, + Official: false, + }, +} diff --git a/main.go b/main.go new file mode 100644 index 000000000..8bf2f9fc7 --- /dev/null +++ b/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/arduino/arduino-check/check" + "github.com/arduino/arduino-check/configuration" + "github.com/arduino/arduino-check/projects" +) + +func main() { + configuration.Initialize() + projects := projects.FindProjects() + for _, project := range projects { + check.RunChecks(project) + } +} diff --git a/projects/library/library.go b/projects/library/library.go new file mode 100644 index 000000000..e3d1d0c0e --- /dev/null +++ b/projects/library/library.go @@ -0,0 +1,34 @@ +package library + +import ( + "github.com/arduino/go-paths-helper" +) + +var empty struct{} + +// reference: https://github.com/arduino/arduino-cli/blob/0.13.0/arduino/libraries/libraries.go#L167 +var headerFileValidExtensions = map[string]struct{}{ + ".h": empty, + ".hpp": empty, + ".hh": empty, +} + +func HasHeaderFileValidExtension(filePath *paths.Path) bool { + _, hasHeaderFileValidExtension := headerFileValidExtensions[filePath.Ext()] + if hasHeaderFileValidExtension { + return true + } + return false +} + +var metadataFilenames = map[string]struct{}{ + "library.properties": empty, +} + +func IsMetadataFile(filePath *paths.Path) bool { + _, isMetadataFile := metadataFilenames[filePath.Base()] + if isMetadataFile { + return true + } + return false +} diff --git a/projects/library/libraryproperties.go b/projects/library/libraryproperties.go new file mode 100644 index 000000000..bcdb9719f --- /dev/null +++ b/projects/library/libraryproperties.go @@ -0,0 +1,27 @@ +package library + +import ( + "os" + + "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" + "github.com/xeipuuv/gojsonschema" +) + +func Properties(libraryPath *paths.Path) (*properties.Map, error) { + libraryProperties, err := properties.Load(libraryPath.Join("library.properties").String()) + if err != nil { + return nil, err + } + return libraryProperties, nil +} + +func ValidateProperties(libraryProperties *properties.Map) *gojsonschema.Result { + workingPath, _ := os.Getwd() + schemaPath := paths.New(workingPath).Join("arduino-library-properties-schema.json") + schemaLoader := gojsonschema.NewReferenceLoader("file://" + schemaPath.String()) + documentLoader := gojsonschema.NewGoLoader(libraryProperties) + + result, _ := gojsonschema.Validate(schemaLoader, documentLoader) + return result +} diff --git a/projects/packageindex/packageindex.go b/projects/packageindex/packageindex.go new file mode 100644 index 000000000..940dc7a6c --- /dev/null +++ b/projects/packageindex/packageindex.go @@ -0,0 +1,34 @@ +package packageindex + +import ( + "regexp" + + "github.com/arduino/go-paths-helper" +) + +var empty struct{} + +var validExtensions = map[string]struct{}{ + ".json": empty, +} + +func HasValidExtension(filePath *paths.Path) bool { + _, hasValidExtension := validExtensions[filePath.Ext()] + if hasValidExtension { + return true + } + return false +} + +// Regular expressions for official and non-official package index filenames +// See: https://arduino.github.io/arduino-cli/latest/package_index_json-specification/#naming-of-the-json-index-file +var validFilenameRegex = map[bool]*regexp.Regexp{ + true: regexp.MustCompile(`^package_(.+_)*index.json$`), + false: regexp.MustCompile(`^package_(.+_)+index.json$`), +} + +func HasValidFilename(filePath *paths.Path, officialCheckMode bool) bool { + regex := validFilenameRegex[officialCheckMode] + filename := filePath.Base() + return regex.MatchString(filename) +} diff --git a/projects/platform/platform.go b/projects/platform/platform.go new file mode 100644 index 000000000..8053dd603 --- /dev/null +++ b/projects/platform/platform.go @@ -0,0 +1,36 @@ +package platform + +import ( + "github.com/arduino/go-paths-helper" +) + +var empty struct{} + +var configurationFilenames = map[string]struct{}{ + "boards.txt": empty, + "boards.local.txt": empty, + "platform.txt": empty, + "platform.local.txt": empty, + "programmers.txt": empty, +} + +func IsConfigurationFile(filePath *paths.Path) bool { + _, isConfigurationFile := configurationFilenames[filePath.Base()] + if isConfigurationFile { + return true + } + return false +} + +var requiredConfigurationFilenames = map[string]struct{}{ + // Arduino platforms must always contain a boards.txt + "boards.txt": empty, +} + +func IsRequiredConfigurationFile(filePath *paths.Path) bool { + _, isRequiredConfigurationFile := requiredConfigurationFilenames[filePath.Base()] + if isRequiredConfigurationFile { + return true + } + return false +} diff --git a/projects/projects.go b/projects/projects.go new file mode 100644 index 000000000..af8f2b6a7 --- /dev/null +++ b/projects/projects.go @@ -0,0 +1,271 @@ +package projects + +import ( + "fmt" + "os" + + "github.com/arduino/arduino-check/configuration" + "github.com/arduino/arduino-check/projects/library" + "github.com/arduino/arduino-check/projects/packageindex" + "github.com/arduino/arduino-check/projects/platform" + "github.com/arduino/arduino-check/projects/projecttype" + "github.com/arduino/arduino-check/projects/sketch" + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/go-paths-helper" +) + +type Project struct { + Path *paths.Path + Type projecttype.Type + SuperprojectType projecttype.Type +} + +func SuperprojectType(project Project) projecttype.Type { + if project.SuperprojectType == 0 { + // SuperprojectType field not set, meaning this is the superproject. Use Type field. + return project.Type + } + return project.SuperprojectType +} + +func FindProjects() []Project { + targetPath := configuration.TargetPath() + projectType := configuration.SuperprojectType() + fmt.Printf("finding projects of type %s", projectType.String()) + recursive := configuration.Recursive() + + var projects []Project + + // If targetPath is a file, targetPath itself is the project, so it's only necessary to determine/verify the type + if targetPath.IsNotDir() { + // The filename provides additional information about the project type. So rather than using isProject(), which doesn't use that information, use a function that does. + isProject, projectType := isProjectIndicatorFile(targetPath, projectType) + if isProject { + project := Project{ + Path: targetPath.Parent(), + Type: projectType, + } + projects = append(projects, project) + + projects = append(projects, findSubprojects(project, projectType)...) + + return projects + } + + fmt.Errorf("error: specified path %s is not an Arduino project", targetPath.String()) + os.Exit(errorcodes.ErrGeneric) + } + + projects = append(projects, findProjects(targetPath, projectType, recursive)...) + + if projects == nil { + fmt.Errorf("error: no projects found under %s", targetPath.String()) + os.Exit(errorcodes.ErrGeneric) + } + + return projects +} + +func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursive bool) []Project { + var projects []Project + + isProject, projectType := isProject(targetPath, projectType) + if isProject { + project := Project{ + Path: targetPath, + Type: projectType, + } + projects = append(projects, project) + + projects = append(projects, findSubprojects(project, project.Type)...) + + // Don't search recursively past a project + return projects + } + + if recursive { + // targetPath was not a project, so search the subfolders + directoryListing, _ := targetPath.ReadDir() + directoryListing.FilterDirs() + for _, potentialProjectDirectory := range directoryListing { + projects = append(projects, findProjects(potentialProjectDirectory, projectType, recursive)...) + } + } + + return projects +} + +func findSubprojects(superproject Project, apexSuperprojectType projecttype.Type) []Project { + var immediateSubprojects []Project + + switch superproject.Type { + case projecttype.Sketch: + // Sketches don't have subprojects + return nil + case projecttype.Library: + subprojectPath := superproject.Path.Join("examples") + immediateSubprojects = append(immediateSubprojects, findProjects(subprojectPath, projecttype.Sketch, true)...) + // Apparently there is some level of official support for "example" in addition to the specification-compliant "examples" + // see: https://github.com/arduino/arduino-cli/blob/0.13.0/arduino/libraries/loader.go#L153 + subprojectPath = superproject.Path.Join("example") + immediateSubprojects = append(immediateSubprojects, findProjects(subprojectPath, projecttype.Sketch, true)...) + case projecttype.Platform: + subprojectPath := superproject.Path.Join("libraries") + immediateSubprojects = append(immediateSubprojects, findProjects(subprojectPath, projecttype.Library, false)...) + case projecttype.PackageIndex: + // Platform indexes don't have subprojects + return nil + } + + var allSubprojects []Project + // Subprojects may have their own subprojects + for _, immediateSubproject := range immediateSubprojects { + // Subprojects at all levels should have SuperprojectType set to the top level superproject's type, not the immediate parent + immediateSubproject.SuperprojectType = apexSuperprojectType + // Each parent project should be followed in the list by its subprojects + allSubprojects = append(allSubprojects, immediateSubproject) + allSubprojects = append(allSubprojects, findSubprojects(immediateSubproject, apexSuperprojectType)...) + } + + return allSubprojects +} + +// isProject determines if a path contains an Arduino project, and if so which type +func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { + if (projectType == projecttype.All || projectType == projecttype.Sketch) && isSketch(potentialProjectPath) { + return true, projecttype.Sketch + } else if (projectType == projecttype.All || projectType == projecttype.Library) && isLibrary(potentialProjectPath) { + return true, projecttype.Library + } else if (projectType == projecttype.All || projectType == projecttype.Platform) && isPlatform(potentialProjectPath) { + return true, projecttype.Platform + } else if (projectType == projecttype.All || projectType == projecttype.PackageIndex) && isPackageIndex(potentialProjectPath) { + return true, projecttype.PackageIndex + } + return false, projecttype.Not +} + +// isProject determines if a file is the indicator file for an Arduino project, and if so which type +func isProjectIndicatorFile(potentialProjectFilePath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { + if (projectType == projecttype.All || projectType == projecttype.Sketch) && isSketchIndicatorFile(potentialProjectFilePath) { + return true, projecttype.Sketch + } else if (projectType == projecttype.All || projectType == projecttype.Library) && isLibraryIndicatorFile(potentialProjectFilePath) { + return true, projecttype.Library + } else if (projectType == projecttype.All || projectType == projecttype.Platform) && isPlatformIndicatorFile(potentialProjectFilePath) { + return true, projecttype.Platform + } else if (projectType == projecttype.All || projectType == projecttype.PackageIndex) && isPackageIndexIndicatorFile(potentialProjectFilePath) { + return true, projecttype.PackageIndex + } + return false, projecttype.Not +} + +// isSketch determines if a path is an Arduino sketch +// Note: this intentionally does not determine the validity of the sketch, only that the developer's intent was for it to be a sketch +func isSketch(potentialProjectPath *paths.Path) bool { + directoryListing, _ := potentialProjectPath.ReadDir() + directoryListing.FilterOutDirs() + for _, potentialSketchFile := range directoryListing { + if isSketchIndicatorFile(potentialSketchFile) { + return true + } + } + + // No file was found with a valid main sketch file extension + return false +} + +func isSketchIndicatorFile(filePath *paths.Path) bool { + if sketch.HasMainFileValidExtension(filePath) { + return true + } + return false +} + +// isLibrary determines if a path is an Arduino library +// Note: this intentionally does not determine the validity of the library, only that the developer's intent was for it to be a library +func isLibrary(potentialProjectPath *paths.Path) bool { + // Arduino libraries will always have one of the following files in its root folder: + // - a library.properties metadata file + // - a header file + directoryListing, _ := potentialProjectPath.ReadDir() + directoryListing.FilterOutDirs() + for _, potentialLibraryFile := range directoryListing { + if isLibraryIndicatorFile(potentialLibraryFile) { + return true + } + } + + // None of the files required for a valid Arduino library were found + return false +} + +func isLibraryIndicatorFile(filePath *paths.Path) bool { + if library.IsMetadataFile(filePath) { + return true + } + + if library.HasHeaderFileValidExtension(filePath) { + return true + } + + return false +} + +// isPlatform determines if a path is an Arduino boards platform +// Note: this intentionally does not determine the validity of the platform, only that the developer's intent was for it to be a platform +func isPlatform(potentialProjectPath *paths.Path) bool { + directoryListing, _ := potentialProjectPath.ReadDir() + directoryListing.FilterOutDirs() + for _, potentialPlatformFile := range directoryListing { + if isStrictPlatformIndicatorFile(potentialPlatformFile) { + return true + } + } + + return false +} + +func isPlatformIndicatorFile(filePath *paths.Path) bool { + if platform.IsConfigurationFile(filePath) { + return true + } + + return false +} + +func isStrictPlatformIndicatorFile(filePath *paths.Path) bool { + if platform.IsRequiredConfigurationFile(filePath) { + return true + } + + return false +} + +// isPackageIndex determines if a path contains an Arduino package index +// Note: this intentionally does not determine the validity of the package index, only that the developer's intent was for it to be a package index +func isPackageIndex(potentialProjectPath *paths.Path) bool { + directoryListing, _ := potentialProjectPath.ReadDir() + directoryListing.FilterOutDirs() + for _, potentialPackageIndexFile := range directoryListing { + if isStrictPackageIndexIndicatorFile(potentialPackageIndexFile) { + return true + } + } + + return false +} + +func isPackageIndexIndicatorFile(filePath *paths.Path) bool { + if filePath.Ext() == ".json" { + return true + } + + return false +} + +func isStrictPackageIndexIndicatorFile(filePath *paths.Path) bool { + if packageindex.HasValidFilename(filePath, true) { + return true + } + + return false +} diff --git a/projects/projecttype/type_string.go b/projects/projecttype/type_string.go new file mode 100644 index 000000000..8098ec4a8 --- /dev/null +++ b/projects/projecttype/type_string.go @@ -0,0 +1,28 @@ +// Code generated by "stringer -type=Type -linecomment"; DO NOT EDIT. + +package projecttype + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Sketch-0] + _ = x[Library-1] + _ = x[Platform-2] + _ = x[PackageIndex-3] + _ = x[All-4] + _ = x[Not-5] +} + +const _Type_name = "sketchlibraryboards platformBoards Manager package indexany project typeN/A" + +var _Type_index = [...]uint8{0, 6, 13, 28, 56, 72, 75} + +func (i Type) String() string { + if i < 0 || i >= Type(len(_Type_index)-1) { + return "Type(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Type_name[_Type_index[i]:_Type_index[i+1]] +} diff --git a/projects/projecttype/types.go b/projects/projecttype/types.go new file mode 100644 index 000000000..f65361117 --- /dev/null +++ b/projects/projecttype/types.go @@ -0,0 +1,13 @@ +package projecttype + +//go:generate stringer -type=Type -linecomment +type Type int + +const ( + Sketch Type = iota // sketch + Library // library + Platform // boards platform + PackageIndex // Boards Manager package index + All // any project type + Not // N/A +) diff --git a/projects/sketch/sketch.go b/projects/sketch/sketch.go new file mode 100644 index 000000000..51a0ea180 --- /dev/null +++ b/projects/sketch/sketch.go @@ -0,0 +1,14 @@ +package sketch + +import ( + "github.com/arduino/arduino-cli/arduino/globals" + "github.com/arduino/go-paths-helper" +) + +func HasMainFileValidExtension(filePath *paths.Path) bool { + _, hasMainFileValidExtension := globals.MainFileValidExtensions[filePath.Ext()] + if hasMainFileValidExtension { + return true + } + return false +} From ecdce0632c2b660fd41286a64528dc5d115789b4 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 11:26:38 -0700 Subject: [PATCH 03/54] Use consistent approach to handling custom types - Put type in dedicated package to create a namespace for it - Name custom type "Type" (the package name provides the identification) --- check/check.go | 10 +-- .../checkconfigurations.go | 24 +++--- check/checkdata/checkdata.go | 13 +++- check/checkfunctions/checkfunctions.go | 4 +- check/checklevel/checklevel.go | 13 +++- check/checklevel/level_string.go | 26 ------- check/checklevel/levels.go | 12 --- check/checklevel/type_string.go | 26 +++++++ .../{results.go => checkresult.go} | 8 +- check/checkresult/result_string.go | 26 ------- check/checkresult/type_string.go | 26 +++++++ configuration/checkmode/checkmode.go | 32 ++++++++ configuration/configuration.go | 32 +------- configuration/defaults.go | 39 +++++----- projects/projects.go | 74 +++++++++---------- .../projecttype/{types.go => projecttype.go} | 0 16 files changed, 187 insertions(+), 178 deletions(-) delete mode 100644 check/checklevel/level_string.go delete mode 100644 check/checklevel/levels.go create mode 100644 check/checklevel/type_string.go rename check/checkresult/{results.go => checkresult.go} (62%) delete mode 100644 check/checkresult/result_string.go create mode 100644 check/checkresult/type_string.go create mode 100644 configuration/checkmode/checkmode.go rename projects/projecttype/{types.go => projecttype.go} (100%) diff --git a/check/check.go b/check/check.go index b95b326cf..35942f42d 100644 --- a/check/check.go +++ b/check/check.go @@ -13,10 +13,10 @@ import ( "github.com/arduino/arduino-check/projects" ) -func shouldRun(checkConfiguration checkconfigurations.Configuration, currentProject projects.Project) bool { - checkModes := configuration.CheckModes(projects.SuperprojectType(currentProject)) +func shouldRun(checkConfiguration checkconfigurations.Type, currentProject projects.Type) bool { + checkModes := configuration.CheckModes(currentProject.SuperprojectType) - if checkConfiguration.ProjectType != currentProject.Type { + if checkConfiguration.ProjectType != currentProject.ProjectType { return false } @@ -44,8 +44,8 @@ func message(templateText string, checkOutput string) string { return messageBuffer.String() } -func RunChecks(project projects.Project) { - fmt.Printf("Checking %s in %s\n", project.Type.String(), project.Path.String()) +func RunChecks(project projects.Type) { + fmt.Printf("Checking %s in %s\n", project.ProjectType.String(), project.Path.String()) checkdata.Initialize(project) diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go index db71e26e7..12357bdbe 100644 --- a/check/checkconfigurations/checkconfigurations.go +++ b/check/checkconfigurations/checkconfigurations.go @@ -2,11 +2,11 @@ package checkconfigurations import ( "github.com/arduino/arduino-check/check/checkfunctions" - "github.com/arduino/arduino-check/configuration" + "github.com/arduino/arduino-check/configuration/checkmode" "github.com/arduino/arduino-check/projects/projecttype" ) -type Configuration struct { +type Type struct { ProjectType projecttype.Type // Arbitrary text for the log Category string @@ -19,19 +19,19 @@ type Configuration struct { // The check function output will be filled into the template MessageTemplate string // Check is disabled when tool is in any of these modes - DisableModes []configuration.CheckMode + DisableModes []checkmode.Type // Check is only enabled when tool is in one of these modes - EnableModes []configuration.CheckMode + EnableModes []checkmode.Type // In these modes, failed check is treated as an error, otherwise it's handled as normal. - PromoteModes []configuration.CheckMode - InfoModes []configuration.CheckMode - WarningModes []configuration.CheckMode - ErrorModes []configuration.CheckMode - CheckFunction checkfunctions.CheckFunction + PromoteModes []checkmode.Type + InfoModes []checkmode.Type + WarningModes []checkmode.Type + ErrorModes []checkmode.Type + CheckFunction checkfunctions.Type } // Checks is an array of structs that define the configuration of each check. -var Configurations = []Configuration{ +var Configurations = []Type{ { ProjectType: projecttype.Library, Category: "library.properties", @@ -41,11 +41,11 @@ var Configurations = []Configuration{ Description: "", MessageTemplate: "library.properties has an invalid format: {{.}}", DisableModes: nil, - EnableModes: []configuration.CheckMode{configuration.Default}, + EnableModes: []checkmode.Type{checkmode.Default}, PromoteModes: nil, InfoModes: nil, WarningModes: nil, - ErrorModes: []configuration.CheckMode{configuration.Default}, + ErrorModes: []checkmode.Type{checkmode.Default}, CheckFunction: checkfunctions.CheckLibraryPropertiesFormat, }, } diff --git a/check/checkdata/checkdata.go b/check/checkdata/checkdata.go index f0f82c0e4..fd51f8030 100644 --- a/check/checkdata/checkdata.go +++ b/check/checkdata/checkdata.go @@ -39,14 +39,19 @@ func LibraryPropertiesSchemaValidationResult() *gojsonschema.Result { return libraryPropertiesSchemaValidationResult } -func Initialize(project projects.Project) { - projectType = project.Type +func Initialize(project projects.Type) { + projectType = project.ProjectType projectPath = project.Path - switch project.Type { + switch project.ProjectType { case projecttype.Sketch: case projecttype.Library: libraryProperties, libraryPropertiesLoadError = library.Properties(project.Path) - libraryPropertiesSchemaValidationResult = library.ValidateProperties(libraryProperties) + if libraryPropertiesLoadError != nil { + libraryPropertiesSchemaValidationResult = library.ValidateProperties(libraryProperties) + } else { + // TODO: can I even do this? + libraryPropertiesSchemaValidationResult = nil + } case projecttype.Platform: case projecttype.PackageIndex: } diff --git a/check/checkfunctions/checkfunctions.go b/check/checkfunctions/checkfunctions.go index 5e44b3639..5b2a42964 100644 --- a/check/checkfunctions/checkfunctions.go +++ b/check/checkfunctions/checkfunctions.go @@ -3,8 +3,8 @@ package checkfunctions import "github.com/arduino/arduino-check/check/checkresult" // output is the contextual information that will be added to the stock message -type CheckFunction func() (result checkresult.Result, output string) +type Type func() (result checkresult.Type, output string) -func CheckLibraryPropertiesFormat() (result checkresult.Result, output string) { +func CheckLibraryPropertiesFormat() (result checkresult.Type, output string) { return } diff --git a/check/checklevel/checklevel.go b/check/checklevel/checklevel.go index db12faa86..40df694a3 100644 --- a/check/checklevel/checklevel.go +++ b/check/checklevel/checklevel.go @@ -5,7 +5,18 @@ import ( "github.com/arduino/arduino-check/configuration" ) -func CheckLevel(checkConfiguration checkconfigurations.Configuration) Level { +//go:generate stringer -type=Type -linecomment +type Type int + +// Line comments set the string for each level +const ( + Info Type = iota // info + Warning // warning + Error // error + Pass // pass +) + +func CheckLevel(checkConfiguration checkconfigurations.Type) Type { configurationCheckModes := configuration.CheckModes(checkConfiguration.ProjectType) for _, promoteMode := range checkConfiguration.PromoteModes { if configurationCheckModes[promoteMode] == true { diff --git a/check/checklevel/level_string.go b/check/checklevel/level_string.go deleted file mode 100644 index c5337cc5b..000000000 --- a/check/checklevel/level_string.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by "stringer -type=Level -linecomment"; DO NOT EDIT. - -package checklevel - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[Info-0] - _ = x[Warning-1] - _ = x[Error-2] - _ = x[Pass-3] -} - -const _Level_name = "infowarningerrorpass" - -var _Level_index = [...]uint8{0, 4, 11, 16, 20} - -func (i Level) String() string { - if i < 0 || i >= Level(len(_Level_index)-1) { - return "Level(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _Level_name[_Level_index[i]:_Level_index[i+1]] -} diff --git a/check/checklevel/levels.go b/check/checklevel/levels.go deleted file mode 100644 index 463678640..000000000 --- a/check/checklevel/levels.go +++ /dev/null @@ -1,12 +0,0 @@ -package checklevel - -//go:generate stringer -type=Level -linecomment -type Level int - -// Line comments set the string for each level -const ( - Info Level = iota // info - Warning // warning - Error // error - Pass // pass -) diff --git a/check/checklevel/type_string.go b/check/checklevel/type_string.go new file mode 100644 index 000000000..e71fa30d1 --- /dev/null +++ b/check/checklevel/type_string.go @@ -0,0 +1,26 @@ +// Code generated by "stringer -type=Type -linecomment"; DO NOT EDIT. + +package checklevel + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Info-0] + _ = x[Warning-1] + _ = x[Error-2] + _ = x[Pass-3] +} + +const _Type_name = "infowarningerrorpass" + +var _Type_index = [...]uint8{0, 4, 11, 16, 20} + +func (i Type) String() string { + if i < 0 || i >= Type(len(_Type_index)-1) { + return "Type(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Type_name[_Type_index[i]:_Type_index[i+1]] +} diff --git a/check/checkresult/results.go b/check/checkresult/checkresult.go similarity index 62% rename from check/checkresult/results.go rename to check/checkresult/checkresult.go index 36849bf12..4110022cf 100644 --- a/check/checkresult/results.go +++ b/check/checkresult/checkresult.go @@ -1,11 +1,11 @@ package checkresult -//go:generate stringer -type=Result -linecomment -type Result int +//go:generate stringer -type=Type -linecomment +type Type int const ( - Pass Result = iota // pass - Fail // fail + Pass Type = iota // pass + Fail // fail // The check is configured to be skipped in the current tool configuration mode Skipped // skipped // An error prevented the check from running diff --git a/check/checkresult/result_string.go b/check/checkresult/result_string.go deleted file mode 100644 index 379efd430..000000000 --- a/check/checkresult/result_string.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by "stringer -type=Result -linecomment"; DO NOT EDIT. - -package checkresult - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[Pass-0] - _ = x[Fail-1] - _ = x[Skipped-2] - _ = x[NotRun-3] -} - -const _Result_name = "passfailskippednot run" - -var _Result_index = [...]uint8{0, 4, 8, 15, 22} - -func (i Result) String() string { - if i < 0 || i >= Result(len(_Result_index)-1) { - return "Result(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _Result_name[_Result_index[i]:_Result_index[i+1]] -} diff --git a/check/checkresult/type_string.go b/check/checkresult/type_string.go new file mode 100644 index 000000000..36918ed4a --- /dev/null +++ b/check/checkresult/type_string.go @@ -0,0 +1,26 @@ +// Code generated by "stringer -type=Type -linecomment"; DO NOT EDIT. + +package checkresult + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Pass-0] + _ = x[Fail-1] + _ = x[Skipped-2] + _ = x[NotRun-3] +} + +const _Type_name = "passfailskippednot run" + +var _Type_index = [...]uint8{0, 4, 8, 15, 22} + +func (i Type) String() string { + if i < 0 || i >= Type(len(_Type_index)-1) { + return "Type(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Type_name[_Type_index[i]:_Type_index[i+1]] +} diff --git a/configuration/checkmode/checkmode.go b/configuration/checkmode/checkmode.go new file mode 100644 index 000000000..959f47673 --- /dev/null +++ b/configuration/checkmode/checkmode.go @@ -0,0 +1,32 @@ +package checkmode + +import "github.com/arduino/arduino-check/projects/projecttype" + +type Type int + +const ( + Permissive Type = iota + LibraryManagerSubmission + LibraryManagerIndexed + Official + Default +) + +func Modes(defaultCheckModes map[projecttype.Type]map[Type]bool, customCheckModes map[Type]bool, superprojectType projecttype.Type) map[Type]bool { + checkModes := make(map[Type]bool) + + // Merge the default settings with any custom settings specified by the user + for key, defaultValue := range defaultCheckModes[superprojectType] { + customCheckModeValue, customCheckModeIsConfigured := customCheckModes[key] + if customCheckModeIsConfigured { + checkModes[key] = customCheckModeValue + } else { + checkModes[key] = defaultValue + } + } + + // This mode is always enabled + checkModes[Default] = true + + return checkModes +} diff --git a/configuration/configuration.go b/configuration/configuration.go index bf2b47716..7a42f08ca 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -1,20 +1,11 @@ package configuration import ( + "github.com/arduino/arduino-check/configuration/checkmode" "github.com/arduino/arduino-check/projects/projecttype" "github.com/arduino/go-paths-helper" ) -type CheckMode int - -const ( - Permissive CheckMode = iota - LibraryManagerSubmission - LibraryManagerIndexed - Official - Default -) - func Initialize() { setDefaults() // TODO configuration according to command line input @@ -23,25 +14,10 @@ func Initialize() { superprojectType = projecttype.Library } -var customCheckModes map[CheckMode]bool - -func CheckModes(superprojectType projecttype.Type) map[CheckMode]bool { - checkModes := make(map[CheckMode]bool) - - // Merge the default settings with any custom settings specified by the user - for key, defaultValue := range defaultCheckModes[superprojectType] { - customCheckModeValue, customCheckModeIsConfigured := customCheckModes[key] - if customCheckModeIsConfigured { - checkModes[key] = customCheckModeValue - } else { - checkModes[key] = defaultValue - } - } - - // This mode is always enabled - checkModes[Default] = true +var customCheckModes map[checkmode.Type]bool - return checkModes +func CheckModes(superprojectType projecttype.Type) map[checkmode.Type]bool { + return checkmode.Modes(defaultCheckModes, customCheckModes, superprojectType) } var superprojectType projecttype.Type diff --git a/configuration/defaults.go b/configuration/defaults.go index d3cc1079a..258125d3e 100644 --- a/configuration/defaults.go +++ b/configuration/defaults.go @@ -1,6 +1,9 @@ package configuration -import "github.com/arduino/arduino-check/projects/projecttype" +import ( + "github.com/arduino/arduino-check/configuration/checkmode" + "github.com/arduino/arduino-check/projects/projecttype" +) func setDefaults() { superprojectType = projecttype.All @@ -10,29 +13,29 @@ func setDefaults() { // Default check modes for each superproject type // Subprojects use the same check modes as the superproject -var defaultCheckModes = map[projecttype.Type]map[CheckMode]bool{ +var defaultCheckModes = map[projecttype.Type]map[checkmode.Type]bool{ projecttype.Sketch: { - Permissive: false, - LibraryManagerSubmission: false, - LibraryManagerIndexed: false, - Official: false, + checkmode.Permissive: false, + checkmode.LibraryManagerSubmission: false, + checkmode.LibraryManagerIndexed: false, + checkmode.Official: false, }, projecttype.Library: { - Permissive: false, - LibraryManagerSubmission: true, - LibraryManagerIndexed: false, - Official: false, + checkmode.Permissive: false, + checkmode.LibraryManagerSubmission: true, + checkmode.LibraryManagerIndexed: false, + checkmode.Official: false, }, projecttype.Platform: { - Permissive: false, - LibraryManagerSubmission: false, - LibraryManagerIndexed: false, - Official: false, + checkmode.Permissive: false, + checkmode.LibraryManagerSubmission: false, + checkmode.LibraryManagerIndexed: false, + checkmode.Official: false, }, projecttype.PackageIndex: { - Permissive: false, - LibraryManagerSubmission: false, - LibraryManagerIndexed: false, - Official: false, + checkmode.Permissive: false, + checkmode.LibraryManagerSubmission: false, + checkmode.LibraryManagerIndexed: false, + checkmode.Official: false, }, } diff --git a/projects/projects.go b/projects/projects.go index af8f2b6a7..12fc09a57 100644 --- a/projects/projects.go +++ b/projects/projects.go @@ -14,73 +14,67 @@ import ( "github.com/arduino/go-paths-helper" ) -type Project struct { +type Type struct { Path *paths.Path - Type projecttype.Type + ProjectType projecttype.Type SuperprojectType projecttype.Type } -func SuperprojectType(project Project) projecttype.Type { - if project.SuperprojectType == 0 { - // SuperprojectType field not set, meaning this is the superproject. Use Type field. - return project.Type - } - return project.SuperprojectType -} - -func FindProjects() []Project { +func FindProjects() []Type { targetPath := configuration.TargetPath() - projectType := configuration.SuperprojectType() - fmt.Printf("finding projects of type %s", projectType.String()) + superprojectTypeConfiguration := configuration.SuperprojectType() recursive := configuration.Recursive() - var projects []Project + var foundProjects []Type // If targetPath is a file, targetPath itself is the project, so it's only necessary to determine/verify the type if targetPath.IsNotDir() { - // The filename provides additional information about the project type. So rather than using isProject(), which doesn't use that information, use a function that does. - isProject, projectType := isProjectIndicatorFile(targetPath, projectType) + // The filename provides additional information about the project type. So rather than using isProject(), which doesn't make use this information, use a specialized function that does. + isProject, projectType := isProjectIndicatorFile(targetPath, superprojectTypeConfiguration) if isProject { - project := Project{ - Path: targetPath.Parent(), - Type: projectType, + foundProject := Type{ + Path: targetPath.Parent(), + ProjectType: projectType, + SuperprojectType: projectType, } - projects = append(projects, project) + foundProjects = append(foundProjects, foundProject) - projects = append(projects, findSubprojects(project, projectType)...) + foundProjects = append(foundProjects, findSubprojects(foundProject, projectType)...) - return projects + return foundProjects } fmt.Errorf("error: specified path %s is not an Arduino project", targetPath.String()) os.Exit(errorcodes.ErrGeneric) } - projects = append(projects, findProjects(targetPath, projectType, recursive)...) + foundProjects = append(foundProjects, findProjects(targetPath, superprojectTypeConfiguration, recursive)...) - if projects == nil { + if foundProjects == nil { fmt.Errorf("error: no projects found under %s", targetPath.String()) os.Exit(errorcodes.ErrGeneric) } - return projects + return foundProjects } -func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursive bool) []Project { - var projects []Project +func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursive bool) []Type { + var foundProjects []Type isProject, projectType := isProject(targetPath, projectType) if isProject { - project := Project{ - Path: targetPath, - Type: projectType, + foundProject := Type{ + Path: targetPath, + ProjectType: projectType, + // findSubprojects() will overwrite this with the correct value when the project is a subproject + SuperprojectType: projectType, } - projects = append(projects, project) + foundProjects = append(foundProjects, foundProject) - projects = append(projects, findSubprojects(project, project.Type)...) + foundProjects = append(foundProjects, findSubprojects(foundProject, foundProject.ProjectType)...) // Don't search recursively past a project - return projects + return foundProjects } if recursive { @@ -88,17 +82,17 @@ func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursiv directoryListing, _ := targetPath.ReadDir() directoryListing.FilterDirs() for _, potentialProjectDirectory := range directoryListing { - projects = append(projects, findProjects(potentialProjectDirectory, projectType, recursive)...) + foundProjects = append(foundProjects, findProjects(potentialProjectDirectory, projectType, recursive)...) } } - return projects + return foundProjects } -func findSubprojects(superproject Project, apexSuperprojectType projecttype.Type) []Project { - var immediateSubprojects []Project +func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) []Type { + var immediateSubprojects []Type - switch superproject.Type { + switch superproject.ProjectType { case projecttype.Sketch: // Sketches don't have subprojects return nil @@ -117,10 +111,10 @@ func findSubprojects(superproject Project, apexSuperprojectType projecttype.Type return nil } - var allSubprojects []Project + var allSubprojects []Type // Subprojects may have their own subprojects for _, immediateSubproject := range immediateSubprojects { - // Subprojects at all levels should have SuperprojectType set to the top level superproject's type, not the immediate parent + // Subprojects at all levels should have SuperprojectType set to the top level superproject's type, not the immediate parent's type immediateSubproject.SuperprojectType = apexSuperprojectType // Each parent project should be followed in the list by its subprojects allSubprojects = append(allSubprojects, immediateSubproject) diff --git a/projects/projecttype/types.go b/projects/projecttype/projecttype.go similarity index 100% rename from projects/projecttype/types.go rename to projects/projecttype/projecttype.go From 12411ac0cd2f3de55240f4ed4ae252090e7a587f Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 11:34:41 -0700 Subject: [PATCH 04/54] Rename projects package to project --- check/check.go | 6 +++--- check/checkconfigurations/checkconfigurations.go | 2 +- check/checkdata/checkdata.go | 8 ++++---- configuration/checkmode/checkmode.go | 2 +- configuration/configuration.go | 2 +- configuration/defaults.go | 2 +- main.go | 4 ++-- {projects => project}/library/library.go | 0 {projects => project}/library/libraryproperties.go | 0 {projects => project}/packageindex/packageindex.go | 0 {projects => project}/platform/platform.go | 0 projects/projects.go => project/project.go | 12 ++++++------ {projects => project}/projecttype/projecttype.go | 0 {projects => project}/projecttype/type_string.go | 0 {projects => project}/sketch/sketch.go | 0 15 files changed, 19 insertions(+), 19 deletions(-) rename {projects => project}/library/library.go (100%) rename {projects => project}/library/libraryproperties.go (100%) rename {projects => project}/packageindex/packageindex.go (100%) rename {projects => project}/platform/platform.go (100%) rename projects/projects.go => project/project.go (96%) rename {projects => project}/projecttype/projecttype.go (100%) rename {projects => project}/projecttype/type_string.go (100%) rename {projects => project}/sketch/sketch.go (100%) diff --git a/check/check.go b/check/check.go index 35942f42d..cfad33b68 100644 --- a/check/check.go +++ b/check/check.go @@ -10,10 +10,10 @@ import ( "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/projects" + "github.com/arduino/arduino-check/project" ) -func shouldRun(checkConfiguration checkconfigurations.Type, currentProject projects.Type) bool { +func shouldRun(checkConfiguration checkconfigurations.Type, currentProject project.Type) bool { checkModes := configuration.CheckModes(currentProject.SuperprojectType) if checkConfiguration.ProjectType != currentProject.ProjectType { @@ -44,7 +44,7 @@ func message(templateText string, checkOutput string) string { return messageBuffer.String() } -func RunChecks(project projects.Type) { +func RunChecks(project project.Type) { fmt.Printf("Checking %s in %s\n", project.ProjectType.String(), project.Path.String()) checkdata.Initialize(project) diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go index 12357bdbe..dc4e7afba 100644 --- a/check/checkconfigurations/checkconfigurations.go +++ b/check/checkconfigurations/checkconfigurations.go @@ -3,7 +3,7 @@ package checkconfigurations import ( "github.com/arduino/arduino-check/check/checkfunctions" "github.com/arduino/arduino-check/configuration/checkmode" - "github.com/arduino/arduino-check/projects/projecttype" + "github.com/arduino/arduino-check/project/projecttype" ) type Type struct { diff --git a/check/checkdata/checkdata.go b/check/checkdata/checkdata.go index fd51f8030..bbaeaa386 100644 --- a/check/checkdata/checkdata.go +++ b/check/checkdata/checkdata.go @@ -1,9 +1,9 @@ package checkdata import ( - "github.com/arduino/arduino-check/projects" - "github.com/arduino/arduino-check/projects/library" - "github.com/arduino/arduino-check/projects/projecttype" + "github.com/arduino/arduino-check/project" + "github.com/arduino/arduino-check/project/library" + "github.com/arduino/arduino-check/project/projecttype" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" "github.com/xeipuuv/gojsonschema" @@ -39,7 +39,7 @@ func LibraryPropertiesSchemaValidationResult() *gojsonschema.Result { return libraryPropertiesSchemaValidationResult } -func Initialize(project projects.Type) { +func Initialize(project project.Type) { projectType = project.ProjectType projectPath = project.Path switch project.ProjectType { diff --git a/configuration/checkmode/checkmode.go b/configuration/checkmode/checkmode.go index 959f47673..00a518539 100644 --- a/configuration/checkmode/checkmode.go +++ b/configuration/checkmode/checkmode.go @@ -1,6 +1,6 @@ package checkmode -import "github.com/arduino/arduino-check/projects/projecttype" +import "github.com/arduino/arduino-check/project/projecttype" type Type int diff --git a/configuration/configuration.go b/configuration/configuration.go index 7a42f08ca..99bd63bfc 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -2,7 +2,7 @@ package configuration import ( "github.com/arduino/arduino-check/configuration/checkmode" - "github.com/arduino/arduino-check/projects/projecttype" + "github.com/arduino/arduino-check/project/projecttype" "github.com/arduino/go-paths-helper" ) diff --git a/configuration/defaults.go b/configuration/defaults.go index 258125d3e..a353cd121 100644 --- a/configuration/defaults.go +++ b/configuration/defaults.go @@ -2,7 +2,7 @@ package configuration import ( "github.com/arduino/arduino-check/configuration/checkmode" - "github.com/arduino/arduino-check/projects/projecttype" + "github.com/arduino/arduino-check/project/projecttype" ) func setDefaults() { diff --git a/main.go b/main.go index 8bf2f9fc7..4761379dc 100644 --- a/main.go +++ b/main.go @@ -3,12 +3,12 @@ package main import ( "github.com/arduino/arduino-check/check" "github.com/arduino/arduino-check/configuration" - "github.com/arduino/arduino-check/projects" + "github.com/arduino/arduino-check/project" ) func main() { configuration.Initialize() - projects := projects.FindProjects() + projects := project.FindProjects() for _, project := range projects { check.RunChecks(project) } diff --git a/projects/library/library.go b/project/library/library.go similarity index 100% rename from projects/library/library.go rename to project/library/library.go diff --git a/projects/library/libraryproperties.go b/project/library/libraryproperties.go similarity index 100% rename from projects/library/libraryproperties.go rename to project/library/libraryproperties.go diff --git a/projects/packageindex/packageindex.go b/project/packageindex/packageindex.go similarity index 100% rename from projects/packageindex/packageindex.go rename to project/packageindex/packageindex.go diff --git a/projects/platform/platform.go b/project/platform/platform.go similarity index 100% rename from projects/platform/platform.go rename to project/platform/platform.go diff --git a/projects/projects.go b/project/project.go similarity index 96% rename from projects/projects.go rename to project/project.go index 12fc09a57..1e874a143 100644 --- a/projects/projects.go +++ b/project/project.go @@ -1,15 +1,15 @@ -package projects +package project import ( "fmt" "os" "github.com/arduino/arduino-check/configuration" - "github.com/arduino/arduino-check/projects/library" - "github.com/arduino/arduino-check/projects/packageindex" - "github.com/arduino/arduino-check/projects/platform" - "github.com/arduino/arduino-check/projects/projecttype" - "github.com/arduino/arduino-check/projects/sketch" + "github.com/arduino/arduino-check/project/library" + "github.com/arduino/arduino-check/project/packageindex" + "github.com/arduino/arduino-check/project/platform" + "github.com/arduino/arduino-check/project/projecttype" + "github.com/arduino/arduino-check/project/sketch" "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/go-paths-helper" ) diff --git a/projects/projecttype/projecttype.go b/project/projecttype/projecttype.go similarity index 100% rename from projects/projecttype/projecttype.go rename to project/projecttype/projecttype.go diff --git a/projects/projecttype/type_string.go b/project/projecttype/type_string.go similarity index 100% rename from projects/projecttype/type_string.go rename to project/projecttype/type_string.go diff --git a/projects/sketch/sketch.go b/project/sketch/sketch.go similarity index 100% rename from projects/sketch/sketch.go rename to project/sketch/sketch.go From b261e7b099e4c7ad70413456d278990fff23d46f Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 19:40:25 -0700 Subject: [PATCH 05/54] Schema: update name pattern to new specification --- arduino-library-properties-schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arduino-library-properties-schema.json b/arduino-library-properties-schema.json index e45372b20..8363969ac 100644 --- a/arduino-library-properties-schema.json +++ b/arduino-library-properties-schema.json @@ -10,7 +10,7 @@ "type": "string", "minLength": 1, "maxLength": 63, - "pattern": "^[a-zA-Z][a-zA-Z\\d _\\.\\-]*$" + "pattern": "^(([a-zA-Z][a-zA-Z0-9 _\\.\\-]*)|([a-zA-Z0-9][a-zA-Z0-9 _\\.\\-]*[a-zA-Z][a-zA-Z0-9 _\\.\\-]*))$" }, "version": { "type": "string", From 4d024edcc6100b07bf1ed4ade46e0db17c6968b4 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 19:41:39 -0700 Subject: [PATCH 06/54] Schema: remove the non-capturing group syntax from the version regex --- arduino-library-properties-schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arduino-library-properties-schema.json b/arduino-library-properties-schema.json index 8363969ac..da4e09993 100644 --- a/arduino-library-properties-schema.json +++ b/arduino-library-properties-schema.json @@ -14,8 +14,8 @@ }, "version": { "type": "string", - "$comment": "https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string adjusted to also allow MAJOR.MINOR", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*))?(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + "$comment": "https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string adjusted to also allow MAJOR.MINOR and with unused non-capturing group syntax removed", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(\\.(0|[1-9]\\d*))?(-((0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\\+([0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*))?$" }, "author": { "type": "string", From 89d6372f74c92ace35394e97e0cd758aa76cd21f Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 19:42:29 -0700 Subject: [PATCH 07/54] Schema: fix the misspelled optional field name regex --- arduino-library-properties-schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arduino-library-properties-schema.json b/arduino-library-properties-schema.json index da4e09993..1aaa630bf 100644 --- a/arduino-library-properties-schema.json +++ b/arduino-library-properties-schema.json @@ -77,7 +77,7 @@ "propertyNames": { "not": { "$comment": "Misspelled optional property names", - "pattern": "^(depend)|(D((epends?)|(EPENDS?)))|(dot_a_linkages)|(dot-?a-?linkages?)|(D(((ot)|(OT))[_-]?((a)|(A))[_-]?((linkages?)|(LINKAGES?))))|(include)|(I((ncludes?)|(NCLUDES?)))|(precompile)|(pre[-_]compiled?)|(P((re[-_]?compiled?)|(RE[-_]?COMPILED?)))|(ldflag)|(ld[-_]flags?)|(L((d[-_]?flags?)|(D[-_]?FLAGS?)))$" + "pattern": "^((depend)|(D((epends?)|(EPENDS?)))|(dot_a_linkages)|(dot-?a-?linkages?)|(D(((ot)|(OT))[_-]?((a)|(A))[_-]?((linkages?)|(LINKAGES?))))|(include)|(I((ncludes?)|(NCLUDES?)))|(precompile)|(pre[-_]compiled?)|(P((re[-_]?compiled?)|(RE[-_]?COMPILED?)))|(ldflag)|(ld[-_]flags?)|(L((d[-_]?flags?)|(D[-_]?FLAGS?))))$" } }, "required": ["name", "version", "author", "maintainer", "sentence", "paragraph", "category", "url", "architectures"] From c04946b74fd370aa72cf6c8f0985241a42ad5074 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 19:43:36 -0700 Subject: [PATCH 08/54] Correctly handle "NotRun" check results --- check/check.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check/check.go b/check/check.go index cfad33b68..6bb04f2fa 100644 --- a/check/check.go +++ b/check/check.go @@ -59,7 +59,7 @@ func RunChecks(project project.Type) { fmt.Printf("Running check %s: ", checkConfiguration.ID) result, output := checkConfiguration.CheckFunction() fmt.Printf("%s\n", result.String()) - if result != checkresult.Pass { + if (result != checkresult.Pass) && (result != checkresult.NotRun) { fmt.Printf("%s: %s\n", checklevel.CheckLevel(checkConfiguration).String(), message(checkConfiguration.MessageTemplate, output)) } } From c39f587d4aa8ee32928edac2fec3aa67b5ca6fb7 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 19:44:45 -0700 Subject: [PATCH 09/54] Create libraryproperties package and add validation helper functions --- check/checkdata/checkdata.go | 8 +-- project/library/libraryproperties.go | 27 -------- .../libraryproperties/libraryproperties.go | 65 +++++++++++++++++++ 3 files changed, 69 insertions(+), 31 deletions(-) delete mode 100644 project/library/libraryproperties.go create mode 100644 project/library/libraryproperties/libraryproperties.go diff --git a/check/checkdata/checkdata.go b/check/checkdata/checkdata.go index bbaeaa386..1fa1b4fe8 100644 --- a/check/checkdata/checkdata.go +++ b/check/checkdata/checkdata.go @@ -2,7 +2,7 @@ package checkdata import ( "github.com/arduino/arduino-check/project" - "github.com/arduino/arduino-check/project/library" + "github.com/arduino/arduino-check/project/library/libraryproperties" "github.com/arduino/arduino-check/project/projecttype" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" @@ -45,12 +45,12 @@ func Initialize(project project.Type) { switch project.ProjectType { case projecttype.Sketch: case projecttype.Library: - libraryProperties, libraryPropertiesLoadError = library.Properties(project.Path) + libraryProperties, libraryPropertiesLoadError = libraryproperties.Properties(project.Path) if libraryPropertiesLoadError != nil { - libraryPropertiesSchemaValidationResult = library.ValidateProperties(libraryProperties) - } else { // TODO: can I even do this? libraryPropertiesSchemaValidationResult = nil + } else { + libraryPropertiesSchemaValidationResult = libraryproperties.Validate(libraryProperties) } case projecttype.Platform: case projecttype.PackageIndex: diff --git a/project/library/libraryproperties.go b/project/library/libraryproperties.go deleted file mode 100644 index bcdb9719f..000000000 --- a/project/library/libraryproperties.go +++ /dev/null @@ -1,27 +0,0 @@ -package library - -import ( - "os" - - "github.com/arduino/go-paths-helper" - "github.com/arduino/go-properties-orderedmap" - "github.com/xeipuuv/gojsonschema" -) - -func Properties(libraryPath *paths.Path) (*properties.Map, error) { - libraryProperties, err := properties.Load(libraryPath.Join("library.properties").String()) - if err != nil { - return nil, err - } - return libraryProperties, nil -} - -func ValidateProperties(libraryProperties *properties.Map) *gojsonschema.Result { - workingPath, _ := os.Getwd() - schemaPath := paths.New(workingPath).Join("arduino-library-properties-schema.json") - schemaLoader := gojsonschema.NewReferenceLoader("file://" + schemaPath.String()) - documentLoader := gojsonschema.NewGoLoader(libraryProperties) - - result, _ := gojsonschema.Validate(schemaLoader, documentLoader) - return result -} diff --git a/project/library/libraryproperties/libraryproperties.go b/project/library/libraryproperties/libraryproperties.go new file mode 100644 index 000000000..e091f4f7f --- /dev/null +++ b/project/library/libraryproperties/libraryproperties.go @@ -0,0 +1,65 @@ +package libraryproperties + +import ( + "net/url" + "os" + "path/filepath" + + "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" + "github.com/xeipuuv/gojsonschema" +) + +func Properties(libraryPath *paths.Path) (*properties.Map, error) { + libraryProperties, err := properties.Load(libraryPath.Join("library.properties").String()) + if err != nil { + return nil, err + } + return libraryProperties, nil +} + +func Validate(libraryProperties *properties.Map) *gojsonschema.Result { + workingPath, _ := os.Getwd() + schemaPath := paths.New(workingPath).Join("arduino-library-properties-schema.json") + uriFriendlySchemaPath := filepath.ToSlash(schemaPath.String()) + schemaPathURI := url.URL{ + Scheme: "file", + Path: uriFriendlySchemaPath, + } + schemaLoader := gojsonschema.NewReferenceLoader(schemaPathURI.String()) + + documentLoader := gojsonschema.NewGoLoader(libraryProperties) + + result, err := gojsonschema.Validate(schemaLoader, documentLoader) + if err != nil { + panic(err.Error()) + } + + return result +} + +func FieldMissing(fieldName string, validationResult *gojsonschema.Result) bool { + return ValidationErrorMatch("required", "(root)", fieldName+" is required", validationResult) +} + +func FieldPatternMismatch(fieldName string, validationResult *gojsonschema.Result) bool { + return ValidationErrorMatch("pattern", fieldName, "", validationResult) +} + +func ValidationErrorMatch(typeQuery string, fieldQuery string, descriptionQuery string, validationResult *gojsonschema.Result) bool { + if validationResult.Valid() { + // No error, so nothing to match + return false + } + for _, validationError := range validationResult.Errors() { + if typeQuery == "" || typeQuery == validationError.Type() { + if fieldQuery == "" || fieldQuery == validationError.Field() { + if descriptionQuery == "" || descriptionQuery == validationError.Description() { + return true + } + } + } + } + + return false +} From bd5b370c13ff6a7dc07a0c344712f0ca4e0f5bb0 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 19:45:49 -0700 Subject: [PATCH 10/54] Add "notice" checklevel I'm thinking it might be useful for the "notrun" checks, but I haven't done anythign with it yet. --- check/checklevel/checklevel.go | 1 + check/checklevel/type_string.go | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/check/checklevel/checklevel.go b/check/checklevel/checklevel.go index 40df694a3..c353e8cec 100644 --- a/check/checklevel/checklevel.go +++ b/check/checklevel/checklevel.go @@ -14,6 +14,7 @@ const ( Warning // warning Error // error Pass // pass + Notice // notice ) func CheckLevel(checkConfiguration checkconfigurations.Type) Type { diff --git a/check/checklevel/type_string.go b/check/checklevel/type_string.go index e71fa30d1..fb32ad7c5 100644 --- a/check/checklevel/type_string.go +++ b/check/checklevel/type_string.go @@ -12,11 +12,12 @@ func _() { _ = x[Warning-1] _ = x[Error-2] _ = x[Pass-3] + _ = x[Notice-4] } -const _Type_name = "infowarningerrorpass" +const _Type_name = "infowarningerrorpassnotice" -var _Type_index = [...]uint8{0, 4, 11, 16, 20} +var _Type_index = [...]uint8{0, 4, 11, 16, 20, 26} func (i Type) String() string { if i < 0 || i >= Type(len(_Type_index)-1) { From 416233a9924bc55854f20944dc87e9af5700f409 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 19:46:45 -0700 Subject: [PATCH 11/54] Rename "Skipped" checkresult to "Skip" To be consistent with the others --- check/checkresult/checkresult.go | 6 +++--- check/checkresult/type_string.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/check/checkresult/checkresult.go b/check/checkresult/checkresult.go index 4110022cf..7c0f29fc5 100644 --- a/check/checkresult/checkresult.go +++ b/check/checkresult/checkresult.go @@ -7,7 +7,7 @@ const ( Pass Type = iota // pass Fail // fail // The check is configured to be skipped in the current tool configuration mode - Skipped // skipped - // An error prevented the check from running - NotRun // not run + Skip // skipped + // An unrelated error prevented the check from running + NotRun // unable to run ) diff --git a/check/checkresult/type_string.go b/check/checkresult/type_string.go index 36918ed4a..427645627 100644 --- a/check/checkresult/type_string.go +++ b/check/checkresult/type_string.go @@ -10,13 +10,13 @@ func _() { var x [1]struct{} _ = x[Pass-0] _ = x[Fail-1] - _ = x[Skipped-2] + _ = x[Skip-2] _ = x[NotRun-3] } -const _Type_name = "passfailskippednot run" +const _Type_name = "passfailskippedunable to run" -var _Type_index = [...]uint8{0, 4, 8, 15, 22} +var _Type_index = [...]uint8{0, 4, 8, 15, 28} func (i Type) String() string { if i < 0 || i >= Type(len(_Type_index)-1) { From fce1f2ce3f1a972e10b08e72ddc037bb7173c65b Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 19:47:03 -0700 Subject: [PATCH 12/54] Add reminder comment re: handling exit status --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index 4761379dc..303cecec0 100644 --- a/main.go +++ b/main.go @@ -12,4 +12,5 @@ func main() { for _, project := range projects { check.RunChecks(project) } + // TODO: set exit status according to check results } From 8b527e42e991723da4fd354800a2f95ba842d9be Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 20 Sep 2020 19:47:39 -0700 Subject: [PATCH 13/54] Add more checks --- .../checkconfigurations.go | 52 ++++++++++++++++++- check/checkfunctions/checkfunctions.go | 47 +++++++++++++++-- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go index dc4e7afba..caf754fba 100644 --- a/check/checkconfigurations/checkconfigurations.go +++ b/check/checkconfigurations/checkconfigurations.go @@ -35,7 +35,7 @@ var Configurations = []Type{ { ProjectType: projecttype.Library, Category: "library.properties", - Subcategory: "name field", + Subcategory: "general", ID: "LP001", Name: "invalid format", Description: "", @@ -46,6 +46,54 @@ var Configurations = []Type{ InfoModes: nil, WarningModes: nil, ErrorModes: []checkmode.Type{checkmode.Default}, - CheckFunction: checkfunctions.CheckLibraryPropertiesFormat, + CheckFunction: checkfunctions.LibraryPropertiesFormat, + }, + { + ProjectType: projecttype.Library, + Category: "library.properties", + Subcategory: "name field", + ID: "LP002", + Name: "missing name field", + Description: "", + MessageTemplate: "missing name field in library.properties", + DisableModes: nil, + EnableModes: []checkmode.Type{checkmode.Default}, + PromoteModes: nil, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []checkmode.Type{checkmode.Default}, + CheckFunction: checkfunctions.LibraryPropertiesNameFieldMissing, + }, + { + ProjectType: projecttype.Library, + Category: "library.properties", + Subcategory: "name field", + ID: "LP003", + Name: "disallowed characters", + Description: "", + MessageTemplate: "disallowed characters in library.properties name field. See: https://arduino.github.io/arduino-cli/latest/library-specification/#libraryproperties-file-format", + DisableModes: nil, + EnableModes: []checkmode.Type{checkmode.Default}, + PromoteModes: nil, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []checkmode.Type{checkmode.Default}, + CheckFunction: checkfunctions.LibraryPropertiesNameFieldDisallowedCharacters, + }, + { + ProjectType: projecttype.Library, + Category: "library.properties", + Subcategory: "version field", + ID: "LP004", + Name: "missing version field", + Description: "", + MessageTemplate: "missing version field in library.properties", + DisableModes: nil, + EnableModes: []checkmode.Type{checkmode.Default}, + PromoteModes: nil, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []checkmode.Type{checkmode.Default}, + CheckFunction: checkfunctions.LibraryPropertiesVersionFieldMissing, }, } diff --git a/check/checkfunctions/checkfunctions.go b/check/checkfunctions/checkfunctions.go index 5b2a42964..e71be2ec0 100644 --- a/check/checkfunctions/checkfunctions.go +++ b/check/checkfunctions/checkfunctions.go @@ -1,10 +1,51 @@ package checkfunctions -import "github.com/arduino/arduino-check/check/checkresult" +import ( + "github.com/arduino/arduino-check/check/checkdata" + "github.com/arduino/arduino-check/check/checkresult" + "github.com/arduino/arduino-check/project/library/libraryproperties" +) // output is the contextual information that will be added to the stock message type Type func() (result checkresult.Type, output string) -func CheckLibraryPropertiesFormat() (result checkresult.Type, output string) { - return +func LibraryPropertiesFormat() (result checkresult.Type, output string) { + if checkdata.LibraryPropertiesLoadError() != nil { + return checkresult.Fail, checkdata.LibraryPropertiesLoadError().Error() + } + return checkresult.Pass, "" +} + +func LibraryPropertiesNameFieldMissing() (result checkresult.Type, output string) { + if checkdata.LibraryPropertiesLoadError() != nil { + return checkresult.NotRun, "" + } + + if libraryproperties.FieldMissing("name", checkdata.LibraryPropertiesSchemaValidationResult()) { + return checkresult.Fail, "" + } + return checkresult.Pass, "" +} + +func LibraryPropertiesNameFieldDisallowedCharacters() (result checkresult.Type, output string) { + if checkdata.LibraryPropertiesLoadError() != nil { + return checkresult.NotRun, "" + } + + if libraryproperties.FieldPatternMismatch("name", checkdata.LibraryPropertiesSchemaValidationResult()) { + return checkresult.Fail, "" + } + + return checkresult.Pass, "" +} + +func LibraryPropertiesVersionFieldMissing() (result checkresult.Type, output string) { + if checkdata.LibraryPropertiesLoadError() != nil { + return checkresult.NotRun, "" + } + + if libraryproperties.FieldMissing("version", checkdata.LibraryPropertiesSchemaValidationResult()) { + return checkresult.Fail, "" + } + return checkresult.Pass, "" } From 132ce6d78d846979f19f7c9bf8350d3a204f3764 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 00:00:48 -0700 Subject: [PATCH 14/54] Schema: make name regex more minimal --- arduino-library-properties-schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arduino-library-properties-schema.json b/arduino-library-properties-schema.json index 1aaa630bf..ffda4d7b6 100644 --- a/arduino-library-properties-schema.json +++ b/arduino-library-properties-schema.json @@ -10,7 +10,7 @@ "type": "string", "minLength": 1, "maxLength": 63, - "pattern": "^(([a-zA-Z][a-zA-Z0-9 _\\.\\-]*)|([a-zA-Z0-9][a-zA-Z0-9 _\\.\\-]*[a-zA-Z][a-zA-Z0-9 _\\.\\-]*))$" + "pattern": "^(([a-zA-Z][a-zA-Z0-9 _\\.\\-]*)|([0-9][a-zA-Z0-9 _\\.\\-]*[a-zA-Z][a-zA-Z0-9 _\\.\\-]*))$" }, "version": { "type": "string", From abd360fd743e7e4bd1e5d7fa86e40a1412b4cad1 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 00:02:10 -0700 Subject: [PATCH 15/54] Split check functions into separate files according to project type --- check/checkfunctions/checkfunctions.go | 43 ----------------------- check/checkfunctions/library.go | 48 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 43 deletions(-) create mode 100644 check/checkfunctions/library.go diff --git a/check/checkfunctions/checkfunctions.go b/check/checkfunctions/checkfunctions.go index e71be2ec0..9154df707 100644 --- a/check/checkfunctions/checkfunctions.go +++ b/check/checkfunctions/checkfunctions.go @@ -1,51 +1,8 @@ package checkfunctions import ( - "github.com/arduino/arduino-check/check/checkdata" "github.com/arduino/arduino-check/check/checkresult" - "github.com/arduino/arduino-check/project/library/libraryproperties" ) // output is the contextual information that will be added to the stock message type Type func() (result checkresult.Type, output string) - -func LibraryPropertiesFormat() (result checkresult.Type, output string) { - if checkdata.LibraryPropertiesLoadError() != nil { - return checkresult.Fail, checkdata.LibraryPropertiesLoadError().Error() - } - return checkresult.Pass, "" -} - -func LibraryPropertiesNameFieldMissing() (result checkresult.Type, output string) { - if checkdata.LibraryPropertiesLoadError() != nil { - return checkresult.NotRun, "" - } - - if libraryproperties.FieldMissing("name", checkdata.LibraryPropertiesSchemaValidationResult()) { - return checkresult.Fail, "" - } - return checkresult.Pass, "" -} - -func LibraryPropertiesNameFieldDisallowedCharacters() (result checkresult.Type, output string) { - if checkdata.LibraryPropertiesLoadError() != nil { - return checkresult.NotRun, "" - } - - if libraryproperties.FieldPatternMismatch("name", checkdata.LibraryPropertiesSchemaValidationResult()) { - return checkresult.Fail, "" - } - - return checkresult.Pass, "" -} - -func LibraryPropertiesVersionFieldMissing() (result checkresult.Type, output string) { - if checkdata.LibraryPropertiesLoadError() != nil { - return checkresult.NotRun, "" - } - - if libraryproperties.FieldMissing("version", checkdata.LibraryPropertiesSchemaValidationResult()) { - return checkresult.Fail, "" - } - return checkresult.Pass, "" -} diff --git a/check/checkfunctions/library.go b/check/checkfunctions/library.go new file mode 100644 index 000000000..ba06a80bb --- /dev/null +++ b/check/checkfunctions/library.go @@ -0,0 +1,48 @@ +package checkfunctions + +import ( + "github.com/arduino/arduino-check/check/checkdata" + "github.com/arduino/arduino-check/check/checkresult" + "github.com/arduino/arduino-check/project/library/libraryproperties" +) + +func LibraryPropertiesFormat() (result checkresult.Type, output string) { + if checkdata.LibraryPropertiesLoadError() != nil { + return checkresult.Fail, checkdata.LibraryPropertiesLoadError().Error() + } + return checkresult.Pass, "" +} + +func LibraryPropertiesNameFieldMissing() (result checkresult.Type, output string) { + if checkdata.LibraryPropertiesLoadError() != nil { + return checkresult.NotRun, "" + } + + if libraryproperties.FieldMissing("name", checkdata.LibraryPropertiesSchemaValidationResult()) { + return checkresult.Fail, "" + } + return checkresult.Pass, "" +} + +func LibraryPropertiesNameFieldDisallowedCharacters() (result checkresult.Type, output string) { + if checkdata.LibraryPropertiesLoadError() != nil { + return checkresult.NotRun, "" + } + + if libraryproperties.FieldPatternMismatch("name", checkdata.LibraryPropertiesSchemaValidationResult()) { + return checkresult.Fail, "" + } + + return checkresult.Pass, "" +} + +func LibraryPropertiesVersionFieldMissing() (result checkresult.Type, output string) { + if checkdata.LibraryPropertiesLoadError() != nil { + return checkresult.NotRun, "" + } + + if libraryproperties.FieldMissing("version", checkdata.LibraryPropertiesSchemaValidationResult()) { + return checkresult.Fail, "" + } + return checkresult.Pass, "" +} From 3682ceec1eee254ceeb050a767eb423c7d9c00a1 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 00:02:29 -0700 Subject: [PATCH 16/54] Add check for .pde sketch file extension --- .../checkconfigurations.go | 16 +++++++++++ check/checkfunctions/sketch.go | 27 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 check/checkfunctions/sketch.go diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go index caf754fba..0d12e5118 100644 --- a/check/checkconfigurations/checkconfigurations.go +++ b/check/checkconfigurations/checkconfigurations.go @@ -96,4 +96,20 @@ var Configurations = []Type{ ErrorModes: []checkmode.Type{checkmode.Default}, CheckFunction: checkfunctions.LibraryPropertiesVersionFieldMissing, }, + { + ProjectType: projecttype.Sketch, + Category: "structure", + Subcategory: "", + ID: "SS001", + Name: ".pde extension", + Description: "The .pde extension is used by both Processing sketches and Arduino sketches. Processing sketches should either be in the \"data\" subfolder of the sketch or in the \"extras\" folder of the library. Arduino sketches should use the modern .ino extension", + MessageTemplate: "{{.}} uses deprecated .pde file extension. Use .ino for Arduino sketches", + DisableModes: nil, + EnableModes: []checkmode.Type{checkmode.Default}, + PromoteModes: nil, + InfoModes: nil, + WarningModes: []checkmode.Type{checkmode.Permissive}, + ErrorModes: []checkmode.Type{checkmode.Default}, + CheckFunction: checkfunctions.PdeSketchExtension, + }, } diff --git a/check/checkfunctions/sketch.go b/check/checkfunctions/sketch.go new file mode 100644 index 000000000..e5f5e0809 --- /dev/null +++ b/check/checkfunctions/sketch.go @@ -0,0 +1,27 @@ +package checkfunctions + +import ( + "github.com/arduino/arduino-check/check/checkdata" + "github.com/arduino/arduino-check/check/checkresult" +) + +func PdeSketchExtension() (result checkresult.Type, output string) { + directoryListing, _ := checkdata.ProjectPath().ReadDir() + directoryListing.FilterOutDirs() + pdeSketches := "" + for _, filePath := range directoryListing { + if filePath.Ext() == ".pde" { + if pdeSketches == "" { + pdeSketches = filePath.Base() + } else { + pdeSketches += ", " + filePath.Base() + } + } + } + + if pdeSketches != "" { + return checkresult.Fail, pdeSketches + } + + return checkresult.Pass, "" +} From ac1bec439f67c7cee624f9e0db3552242899c52a Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 01:57:11 -0700 Subject: [PATCH 17/54] Output error messages from projects.FindProjects() Previously the error message was not returned from the function, so it failed silently. --- main.go | 9 ++++++++- project/project.go | 14 +++++--------- result/feedback/feedback.go | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 result/feedback/feedback.go diff --git a/main.go b/main.go index 303cecec0..0d6be2ec0 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,21 @@ package main import ( + "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/feedback" ) func main() { configuration.Initialize() - projects := project.FindProjects() + 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) } diff --git a/project/project.go b/project/project.go index 1e874a143..c77d0c72d 100644 --- a/project/project.go +++ b/project/project.go @@ -2,7 +2,6 @@ package project import ( "fmt" - "os" "github.com/arduino/arduino-check/configuration" "github.com/arduino/arduino-check/project/library" @@ -10,7 +9,6 @@ import ( "github.com/arduino/arduino-check/project/platform" "github.com/arduino/arduino-check/project/projecttype" "github.com/arduino/arduino-check/project/sketch" - "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/go-paths-helper" ) @@ -20,7 +18,7 @@ type Type struct { SuperprojectType projecttype.Type } -func FindProjects() []Type { +func FindProjects() ([]Type, error) { targetPath := configuration.TargetPath() superprojectTypeConfiguration := configuration.SuperprojectType() recursive := configuration.Recursive() @@ -41,21 +39,19 @@ func FindProjects() []Type { foundProjects = append(foundProjects, findSubprojects(foundProject, projectType)...) - return foundProjects + return foundProjects, nil } - fmt.Errorf("error: specified path %s is not an Arduino project", targetPath.String()) - os.Exit(errorcodes.ErrGeneric) + return nil, fmt.Errorf("specified path %s is not an Arduino project", targetPath.String()) } foundProjects = append(foundProjects, findProjects(targetPath, superprojectTypeConfiguration, recursive)...) if foundProjects == nil { - fmt.Errorf("error: no projects found under %s", targetPath.String()) - os.Exit(errorcodes.ErrGeneric) + return nil, fmt.Errorf("no projects found under %s", targetPath.String()) } - return foundProjects + return foundProjects, nil } func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursive bool) []Type { diff --git a/result/feedback/feedback.go b/result/feedback/feedback.go new file mode 100644 index 000000000..655fc5418 --- /dev/null +++ b/result/feedback/feedback.go @@ -0,0 +1,19 @@ +// Package feedback provides feedback to the user. +package feedback + +import ( + "fmt" + + "github.com/sirupsen/logrus" +) + +// Errorf behaves like fmt.Printf but also logs the error. +func Errorf(format string, v ...interface{}) { + Error(fmt.Sprintf(format, v...)) +} + +// Error behaves like fmt.Print but also logs the error. +func Error(errorMessage string) { + fmt.Printf(errorMessage) + logrus.Error(fmt.Sprint(errorMessage)) +} From 27a67874b7a4a2a661b144f7e5252319fd1e793c Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 05:32:47 -0700 Subject: [PATCH 18/54] Fix check configuration system It's now possible to fully define the configuration of the check for each tool configuration mode. --- check/check.go | 22 ++++++++-- .../checkconfigurations.go | 40 +++++++++--------- check/checklevel/checklevel.go | 41 +++++++++++++++---- check/checklevel/type_string.go | 12 +++--- configuration/checkmode/checkmode.go | 20 +++++---- configuration/checkmode/type_string.go | 28 +++++++++++++ configuration/configuration.go | 3 +- 7 files changed, 123 insertions(+), 43 deletions(-) create mode 100644 configuration/checkmode/type_string.go diff --git a/check/check.go b/check/check.go index 6bb04f2fa..0c156f6c6 100644 --- a/check/check.go +++ b/check/check.go @@ -10,27 +10,43 @@ import ( "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" ) func shouldRun(checkConfiguration checkconfigurations.Type, currentProject project.Type) bool { - checkModes := configuration.CheckModes(currentProject.SuperprojectType) + configurationCheckModes := configuration.CheckModes(currentProject.SuperprojectType) if checkConfiguration.ProjectType != currentProject.ProjectType { return false } for _, disableMode := range checkConfiguration.DisableModes { - if checkModes[disableMode] == true { + if configurationCheckModes[disableMode] == true { return false } } for _, enableMode := range checkConfiguration.EnableModes { - if checkModes[enableMode] == true { + if configurationCheckModes[enableMode] == true { return true } } + + // Use default + for _, disableMode := range checkConfiguration.DisableModes { + if disableMode == checkmode.Default { + return false + } + } + + for _, enableMode := range checkConfiguration.EnableModes { + if enableMode == checkmode.Default { + return true + } + } + + // TODO: this should return an error return false } diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go index 0d12e5118..911695c67 100644 --- a/check/checkconfigurations/checkconfigurations.go +++ b/check/checkconfigurations/checkconfigurations.go @@ -18,15 +18,17 @@ type Type struct { // The warning/error message template displayed when the check fails // The check function output will be filled into the template MessageTemplate string + // The following fields define under which tool configuration modes the check will run // Check is disabled when tool is in any of these modes DisableModes []checkmode.Type // Check is only enabled when tool is in one of these modes EnableModes []checkmode.Type - // In these modes, failed check is treated as an error, otherwise it's handled as normal. - PromoteModes []checkmode.Type - InfoModes []checkmode.Type - WarningModes []checkmode.Type - ErrorModes []checkmode.Type + // The following fields define the check level in each configuration mode + PassModes []checkmode.Type + InfoModes []checkmode.Type + WarningModes []checkmode.Type + ErrorModes []checkmode.Type + // The function that implements the check CheckFunction checkfunctions.Type } @@ -41,11 +43,11 @@ var Configurations = []Type{ Description: "", MessageTemplate: "library.properties has an invalid format: {{.}}", DisableModes: nil, - EnableModes: []checkmode.Type{checkmode.Default}, - PromoteModes: nil, + EnableModes: []checkmode.Type{checkmode.All}, + PassModes: nil, InfoModes: nil, WarningModes: nil, - ErrorModes: []checkmode.Type{checkmode.Default}, + ErrorModes: []checkmode.Type{checkmode.All}, CheckFunction: checkfunctions.LibraryPropertiesFormat, }, { @@ -57,11 +59,11 @@ var Configurations = []Type{ Description: "", MessageTemplate: "missing name field in library.properties", DisableModes: nil, - EnableModes: []checkmode.Type{checkmode.Default}, - PromoteModes: nil, + EnableModes: []checkmode.Type{checkmode.All}, + PassModes: nil, InfoModes: nil, WarningModes: nil, - ErrorModes: []checkmode.Type{checkmode.Default}, + ErrorModes: []checkmode.Type{checkmode.All}, CheckFunction: checkfunctions.LibraryPropertiesNameFieldMissing, }, { @@ -73,11 +75,11 @@ var Configurations = []Type{ Description: "", MessageTemplate: "disallowed characters in library.properties name field. See: https://arduino.github.io/arduino-cli/latest/library-specification/#libraryproperties-file-format", DisableModes: nil, - EnableModes: []checkmode.Type{checkmode.Default}, - PromoteModes: nil, + EnableModes: []checkmode.Type{checkmode.All}, + PassModes: nil, InfoModes: nil, WarningModes: nil, - ErrorModes: []checkmode.Type{checkmode.Default}, + ErrorModes: []checkmode.Type{checkmode.All}, CheckFunction: checkfunctions.LibraryPropertiesNameFieldDisallowedCharacters, }, { @@ -89,11 +91,11 @@ var Configurations = []Type{ Description: "", MessageTemplate: "missing version field in library.properties", DisableModes: nil, - EnableModes: []checkmode.Type{checkmode.Default}, - PromoteModes: nil, + EnableModes: []checkmode.Type{checkmode.All}, + PassModes: nil, InfoModes: nil, WarningModes: nil, - ErrorModes: []checkmode.Type{checkmode.Default}, + ErrorModes: []checkmode.Type{checkmode.All}, CheckFunction: checkfunctions.LibraryPropertiesVersionFieldMissing, }, { @@ -105,8 +107,8 @@ var Configurations = []Type{ Description: "The .pde extension is used by both Processing sketches and Arduino sketches. Processing sketches should either be in the \"data\" subfolder of the sketch or in the \"extras\" folder of the library. Arduino sketches should use the modern .ino extension", MessageTemplate: "{{.}} uses deprecated .pde file extension. Use .ino for Arduino sketches", DisableModes: nil, - EnableModes: []checkmode.Type{checkmode.Default}, - PromoteModes: nil, + EnableModes: []checkmode.Type{checkmode.All}, + PassModes: nil, InfoModes: nil, WarningModes: []checkmode.Type{checkmode.Permissive}, ErrorModes: []checkmode.Type{checkmode.Default}, diff --git a/check/checklevel/checklevel.go b/check/checklevel/checklevel.go index c353e8cec..25ea84a9f 100644 --- a/check/checklevel/checklevel.go +++ b/check/checklevel/checklevel.go @@ -3,6 +3,7 @@ package checklevel import ( "github.com/arduino/arduino-check/check/checkconfigurations" "github.com/arduino/arduino-check/configuration" + "github.com/arduino/arduino-check/configuration/checkmode" ) //go:generate stringer -type=Type -linecomment @@ -10,38 +11,64 @@ type Type int // Line comments set the string for each level const ( - Info Type = iota // info + Pass Type = iota // pass + Info // info Warning // warning Error // error - Pass // pass Notice // notice ) func CheckLevel(checkConfiguration checkconfigurations.Type) Type { configurationCheckModes := configuration.CheckModes(checkConfiguration.ProjectType) - for _, promoteMode := range checkConfiguration.PromoteModes { - if configurationCheckModes[promoteMode] == true { + for _, errorMode := range checkConfiguration.ErrorModes { + if configurationCheckModes[errorMode] == true { return Error } } + for _, warningMode := range checkConfiguration.WarningModes { + if configurationCheckModes[warningMode] == true { + return Warning + } + } + + for _, infoMode := range checkConfiguration.InfoModes { + if configurationCheckModes[infoMode] == true { + return Info + } + } + + for _, passMode := range checkConfiguration.PassModes { + if configurationCheckModes[passMode] == true { + return Pass + } + } + + // Use default level for _, errorMode := range checkConfiguration.ErrorModes { - if configurationCheckModes[errorMode] == true { + if errorMode == checkmode.Default { return Error } } for _, warningMode := range checkConfiguration.WarningModes { - if configurationCheckModes[warningMode] == true { + if warningMode == checkmode.Default { return Warning } } for _, infoMode := range checkConfiguration.InfoModes { - if configurationCheckModes[infoMode] == true { + if infoMode == checkmode.Default { return Info } } + for _, passMode := range checkConfiguration.PassModes { + if passMode == checkmode.Default { + return Pass + } + } + + // TODO: this should return an error return Pass } diff --git a/check/checklevel/type_string.go b/check/checklevel/type_string.go index fb32ad7c5..66c0b888a 100644 --- a/check/checklevel/type_string.go +++ b/check/checklevel/type_string.go @@ -8,16 +8,16 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} - _ = x[Info-0] - _ = x[Warning-1] - _ = x[Error-2] - _ = x[Pass-3] + _ = x[Pass-0] + _ = x[Info-1] + _ = x[Warning-2] + _ = x[Error-3] _ = x[Notice-4] } -const _Type_name = "infowarningerrorpassnotice" +const _Type_name = "passinfowarningerrornotice" -var _Type_index = [...]uint8{0, 4, 11, 16, 20, 26} +var _Type_index = [...]uint8{0, 4, 8, 15, 20, 26} func (i Type) String() string { if i < 0 || i >= Type(len(_Type_index)-1) { diff --git a/configuration/checkmode/checkmode.go b/configuration/checkmode/checkmode.go index 00a518539..dd4bc6c09 100644 --- a/configuration/checkmode/checkmode.go +++ b/configuration/checkmode/checkmode.go @@ -1,15 +1,20 @@ package checkmode -import "github.com/arduino/arduino-check/project/projecttype" +import ( + "github.com/arduino/arduino-check/project/projecttype" + "github.com/sirupsen/logrus" +) type Type int +//go:generate stringer -type=Type -linecomment const ( - Permissive Type = iota - LibraryManagerSubmission - LibraryManagerIndexed - Official - Default + Permissive Type = iota // --permissive + LibraryManagerSubmission // --library-manager=submit + LibraryManagerIndexed // --library-manager=update + Official // ARDUINO_CHECK_OFFICIAL + All // always + Default // default ) func Modes(defaultCheckModes map[projecttype.Type]map[Type]bool, customCheckModes map[Type]bool, superprojectType projecttype.Type) map[Type]bool { @@ -23,10 +28,11 @@ func Modes(defaultCheckModes map[projecttype.Type]map[Type]bool, customCheckMode } else { checkModes[key] = defaultValue } + logrus.Tracef("Check mode option %s set to %t\n", key.String(), checkModes[key]) } // This mode is always enabled - checkModes[Default] = true + checkModes[All] = true return checkModes } diff --git a/configuration/checkmode/type_string.go b/configuration/checkmode/type_string.go new file mode 100644 index 000000000..74fc6c30d --- /dev/null +++ b/configuration/checkmode/type_string.go @@ -0,0 +1,28 @@ +// Code generated by "stringer -type=Type -linecomment"; DO NOT EDIT. + +package checkmode + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Permissive-0] + _ = x[LibraryManagerSubmission-1] + _ = x[LibraryManagerIndexed-2] + _ = x[Official-3] + _ = x[All-4] + _ = x[Default-5] +} + +const _Type_name = "--permissive--library-manager=submit--library-manager=updateARDUINO_CHECK_OFFICIALalwaysdefault" + +var _Type_index = [...]uint8{0, 12, 36, 60, 82, 88, 95} + +func (i Type) String() string { + if i < 0 || i >= Type(len(_Type_index)-1) { + return "Type(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Type_name[_Type_index[i]:_Type_index[i+1]] +} diff --git a/configuration/configuration.go b/configuration/configuration.go index 99bd63bfc..cf5fb43d0 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -12,9 +12,10 @@ func Initialize() { // TODO validate target path value, exit if not found targetPath = paths.New("e:/electronics/arduino/libraries/arduino-check-test-library") superprojectType = projecttype.Library + customCheckModes[checkmode.Permissive] = false } -var customCheckModes map[checkmode.Type]bool +var customCheckModes = make(map[checkmode.Type]bool) func CheckModes(superprojectType projecttype.Type) map[checkmode.Type]bool { return checkmode.Modes(defaultCheckModes, customCheckModes, superprojectType) From f88be5847f95675a1f3b9e62c999c2051b2d4a4c Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 05:36:28 -0700 Subject: [PATCH 19/54] Start setting up logging --- configuration/configuration.go | 8 ++++++++ project/project.go | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/configuration/configuration.go b/configuration/configuration.go index cf5fb43d0..1b3f48967 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -4,6 +4,7 @@ import ( "github.com/arduino/arduino-check/configuration/checkmode" "github.com/arduino/arduino-check/project/projecttype" "github.com/arduino/go-paths-helper" + "github.com/sirupsen/logrus" ) func Initialize() { @@ -13,6 +14,13 @@ func Initialize() { targetPath = paths.New("e:/electronics/arduino/libraries/arduino-check-test-library") superprojectType = projecttype.Library customCheckModes[checkmode.Permissive] = false + logrus.SetLevel(logrus.PanicLevel) + + logrus.WithFields(logrus.Fields{ + "superproject type": SuperprojectType().String(), + "recursive": Recursive(), + "projects path": TargetPath().String(), + }).Debug("Configuration initialized") } var customCheckModes = make(map[checkmode.Type]bool) diff --git a/project/project.go b/project/project.go index c77d0c72d..10b170fbd 100644 --- a/project/project.go +++ b/project/project.go @@ -10,6 +10,7 @@ import ( "github.com/arduino/arduino-check/project/projecttype" "github.com/arduino/arduino-check/project/sketch" "github.com/arduino/go-paths-helper" + "github.com/sirupsen/logrus" ) type Type struct { @@ -27,6 +28,7 @@ func FindProjects() ([]Type, error) { // If targetPath is a file, targetPath itself is the project, so it's only necessary to determine/verify the type if targetPath.IsNotDir() { + logrus.Debug("Projects path is file") // The filename provides additional information about the project type. So rather than using isProject(), which doesn't make use this information, use a specialized function that does. isProject, projectType := isProjectIndicatorFile(targetPath, superprojectTypeConfiguration) if isProject { @@ -59,6 +61,7 @@ func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursiv isProject, projectType := isProject(targetPath, projectType) if isProject { + logrus.Tracef("%s is %s", targetPath.String(), projectType.String()) foundProject := Type{ Path: targetPath, ProjectType: projectType, @@ -122,13 +125,18 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ // isProject determines if a path contains an Arduino project, and if so which type func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { + logrus.Tracef("Checking if %s is %s", potentialProjectPath.String(), projectType.String()) if (projectType == projecttype.All || projectType == projecttype.Sketch) && isSketch(potentialProjectPath) { + logrus.Tracef("%s is %s", potentialProjectPath.String(), projecttype.Sketch.String()) return true, projecttype.Sketch } else if (projectType == projecttype.All || projectType == projecttype.Library) && isLibrary(potentialProjectPath) { + logrus.Tracef("%s is %s", potentialProjectPath.String(), projecttype.Library.String()) return true, projecttype.Library } else if (projectType == projecttype.All || projectType == projecttype.Platform) && isPlatform(potentialProjectPath) { + logrus.Tracef("%s is %s", potentialProjectPath.String(), projecttype.Platform.String()) return true, projecttype.Platform } else if (projectType == projecttype.All || projectType == projecttype.PackageIndex) && isPackageIndex(potentialProjectPath) { + logrus.Tracef("%s is %s", potentialProjectPath.String(), projecttype.PackageIndex.String()) return true, projecttype.PackageIndex } return false, projecttype.Not @@ -136,15 +144,21 @@ func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) ( // isProject determines if a file is the indicator file for an Arduino project, and if so which type func isProjectIndicatorFile(potentialProjectFilePath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { + logrus.Tracef("Checking if %s is %s indicator file", potentialProjectFilePath.String(), projectType.String()) if (projectType == projecttype.All || projectType == projecttype.Sketch) && isSketchIndicatorFile(potentialProjectFilePath) { + logrus.Tracef("%s is %s indicator file", potentialProjectFilePath.String(), projecttype.Sketch.String()) return true, projecttype.Sketch } else if (projectType == projecttype.All || projectType == projecttype.Library) && isLibraryIndicatorFile(potentialProjectFilePath) { + logrus.Tracef("%s is %s indicator file", potentialProjectFilePath.String(), projecttype.Library.String()) return true, projecttype.Library } else if (projectType == projecttype.All || projectType == projecttype.Platform) && isPlatformIndicatorFile(potentialProjectFilePath) { + logrus.Tracef("%s is %s indicator file", potentialProjectFilePath.String(), projecttype.Platform.String()) return true, projecttype.Platform } else if (projectType == projecttype.All || projectType == projecttype.PackageIndex) && isPackageIndexIndicatorFile(potentialProjectFilePath) { + logrus.Tracef("%s is %s indicator file", potentialProjectFilePath.String(), projecttype.PackageIndex.String()) return true, projecttype.PackageIndex } + logrus.Tracef("%s is not indicator file", potentialProjectFilePath.String()) return false, projecttype.Not } From 19ee67b4fd532b964b40cd085784cc99f5f6f7e8 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 05:37:32 -0700 Subject: [PATCH 20/54] Fix bug with project discovery system The bug was caused by a name collision. --- project/project.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/project/project.go b/project/project.go index 10b170fbd..fb3c85eba 100644 --- a/project/project.go +++ b/project/project.go @@ -59,14 +59,14 @@ func FindProjects() ([]Type, error) { func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursive bool) []Type { var foundProjects []Type - isProject, projectType := isProject(targetPath, projectType) + isProject, foundProjectType := isProject(targetPath, projectType) if isProject { logrus.Tracef("%s is %s", targetPath.String(), projectType.String()) foundProject := Type{ Path: targetPath, - ProjectType: projectType, + ProjectType: foundProjectType, // findSubprojects() will overwrite this with the correct value when the project is a subproject - SuperprojectType: projectType, + SuperprojectType: foundProjectType, } foundProjects = append(foundProjects, foundProject) From 26046fb8aede8f2d0dfa543958d0714a47c8f4d7 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 05:42:58 -0700 Subject: [PATCH 21/54] Provide meaningful output when checks don't run due to required data --- check/check.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/check/check.go b/check/check.go index 0c156f6c6..07ec63d06 100644 --- a/check/check.go +++ b/check/check.go @@ -75,7 +75,10 @@ func RunChecks(project project.Type) { fmt.Printf("Running check %s: ", checkConfiguration.ID) result, output := checkConfiguration.CheckFunction() fmt.Printf("%s\n", result.String()) - if (result != checkresult.Pass) && (result != checkresult.NotRun) { + if result == checkresult.NotRun { + // TODO: make the check functions output an explanation for why they didn't run + fmt.Printf("%s: %s\n", checklevel.Notice, output) + } else if result != checkresult.Pass { fmt.Printf("%s: %s\n", checklevel.CheckLevel(checkConfiguration).String(), message(checkConfiguration.MessageTemplate, output)) } } From ed148388825026ae316519966086e1689aea518a Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 05:43:40 -0700 Subject: [PATCH 22/54] Add a TODO comment --- configuration/configuration.go | 1 + 1 file changed, 1 insertion(+) diff --git a/configuration/configuration.go b/configuration/configuration.go index 1b3f48967..3d8b90c36 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -11,6 +11,7 @@ func Initialize() { setDefaults() // TODO configuration according to command line input // TODO validate target path value, exit if not found + // TODO support multiple paths targetPath = paths.New("e:/electronics/arduino/libraries/arduino-check-test-library") superprojectType = projecttype.Library customCheckModes[checkmode.Permissive] = false From 2c5c11a9e8fd10bb3ddedf57b51179735af3b019 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 05:46:51 -0700 Subject: [PATCH 23/54] Set up configuration to be able to easily experiment with different settings --- configuration/configuration.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/configuration/configuration.go b/configuration/configuration.go index 3d8b90c36..450950526 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -13,8 +13,12 @@ func Initialize() { // TODO validate target path value, exit if not found // TODO support multiple paths targetPath = paths.New("e:/electronics/arduino/libraries/arduino-check-test-library") - superprojectType = projecttype.Library - customCheckModes[checkmode.Permissive] = false + + // customCheckModes[checkmode.Permissive] = false + // customCheckModes[checkmode.LibraryManagerSubmission] = false + // customCheckModes[checkmode.LibraryManagerIndexed] = false + // customCheckModes[checkmode.Official] = false + // superprojectType = projecttype.All logrus.SetLevel(logrus.PanicLevel) logrus.WithFields(logrus.Fields{ From f8da28048342860fb5fa824285ec52e45117a99a Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 09:32:11 -0700 Subject: [PATCH 24/54] Remove "Pass" check level There is no valid reason for a check to be defined, only for the results to be ignored. This was used as a default behavior when no level was configured for the check, but that indicates a configuration error and should not be ignored. So I now error exit when this situation occurs. --- check/check.go | 9 ++++- .../checkconfigurations.go | 6 ---- check/checklevel/checklevel.go | 34 ++++++------------- check/checklevel/type_string.go | 13 ++++--- 4 files changed, 25 insertions(+), 37 deletions(-) diff --git a/check/check.go b/check/check.go index 07ec63d06..0623a11d0 100644 --- a/check/check.go +++ b/check/check.go @@ -3,6 +3,7 @@ package check import ( "bytes" "fmt" + "os" "text/template" "github.com/arduino/arduino-check/check/checkconfigurations" @@ -12,6 +13,7 @@ import ( "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" ) func shouldRun(checkConfiguration checkconfigurations.Type, currentProject project.Type) bool { @@ -79,7 +81,12 @@ func RunChecks(project project.Type) { // TODO: make the check functions output an explanation for why they didn't run fmt.Printf("%s: %s\n", checklevel.Notice, output) } else if result != checkresult.Pass { - fmt.Printf("%s: %s\n", checklevel.CheckLevel(checkConfiguration).String(), message(checkConfiguration.MessageTemplate, output)) + checkLevel, err := checklevel.CheckLevel(checkConfiguration) + if err != nil { + feedback.Errorf("Error while determining check level: %v", err) + os.Exit(1) + } + fmt.Printf("%s: %s\n", checkLevel.String(), message(checkConfiguration.MessageTemplate, output)) } } } diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go index 911695c67..5c759f4d0 100644 --- a/check/checkconfigurations/checkconfigurations.go +++ b/check/checkconfigurations/checkconfigurations.go @@ -24,7 +24,6 @@ type Type struct { // Check is only enabled when tool is in one of these modes EnableModes []checkmode.Type // The following fields define the check level in each configuration mode - PassModes []checkmode.Type InfoModes []checkmode.Type WarningModes []checkmode.Type ErrorModes []checkmode.Type @@ -44,7 +43,6 @@ var Configurations = []Type{ MessageTemplate: "library.properties has an invalid format: {{.}}", DisableModes: nil, EnableModes: []checkmode.Type{checkmode.All}, - PassModes: nil, InfoModes: nil, WarningModes: nil, ErrorModes: []checkmode.Type{checkmode.All}, @@ -60,7 +58,6 @@ var Configurations = []Type{ MessageTemplate: "missing name field in library.properties", DisableModes: nil, EnableModes: []checkmode.Type{checkmode.All}, - PassModes: nil, InfoModes: nil, WarningModes: nil, ErrorModes: []checkmode.Type{checkmode.All}, @@ -76,7 +73,6 @@ var Configurations = []Type{ MessageTemplate: "disallowed characters in library.properties name field. See: https://arduino.github.io/arduino-cli/latest/library-specification/#libraryproperties-file-format", DisableModes: nil, EnableModes: []checkmode.Type{checkmode.All}, - PassModes: nil, InfoModes: nil, WarningModes: nil, ErrorModes: []checkmode.Type{checkmode.All}, @@ -92,7 +88,6 @@ var Configurations = []Type{ MessageTemplate: "missing version field in library.properties", DisableModes: nil, EnableModes: []checkmode.Type{checkmode.All}, - PassModes: nil, InfoModes: nil, WarningModes: nil, ErrorModes: []checkmode.Type{checkmode.All}, @@ -108,7 +103,6 @@ var Configurations = []Type{ MessageTemplate: "{{.}} uses deprecated .pde file extension. Use .ino for Arduino sketches", DisableModes: nil, EnableModes: []checkmode.Type{checkmode.All}, - PassModes: nil, InfoModes: nil, WarningModes: []checkmode.Type{checkmode.Permissive}, ErrorModes: []checkmode.Type{checkmode.Default}, diff --git a/check/checklevel/checklevel.go b/check/checklevel/checklevel.go index 25ea84a9f..abc2a86b1 100644 --- a/check/checklevel/checklevel.go +++ b/check/checklevel/checklevel.go @@ -1,6 +1,8 @@ package checklevel import ( + "fmt" + "github.com/arduino/arduino-check/check/checkconfigurations" "github.com/arduino/arduino-check/configuration" "github.com/arduino/arduino-check/configuration/checkmode" @@ -11,64 +13,50 @@ type Type int // Line comments set the string for each level const ( - Pass Type = iota // pass - Info // info + Info Type = iota // info Warning // warning Error // error Notice // notice ) -func CheckLevel(checkConfiguration checkconfigurations.Type) Type { +func CheckLevel(checkConfiguration checkconfigurations.Type) (Type, error) { configurationCheckModes := configuration.CheckModes(checkConfiguration.ProjectType) for _, errorMode := range checkConfiguration.ErrorModes { if configurationCheckModes[errorMode] == true { - return Error + return Error, nil } } for _, warningMode := range checkConfiguration.WarningModes { if configurationCheckModes[warningMode] == true { - return Warning + return Warning, nil } } for _, infoMode := range checkConfiguration.InfoModes { if configurationCheckModes[infoMode] == true { - return Info - } - } - - for _, passMode := range checkConfiguration.PassModes { - if configurationCheckModes[passMode] == true { - return Pass + return Info, nil } } // Use default level for _, errorMode := range checkConfiguration.ErrorModes { if errorMode == checkmode.Default { - return Error + return Error, nil } } for _, warningMode := range checkConfiguration.WarningModes { if warningMode == checkmode.Default { - return Warning + return Warning, nil } } for _, infoMode := range checkConfiguration.InfoModes { if infoMode == checkmode.Default { - return Info - } - } - - for _, passMode := range checkConfiguration.PassModes { - if passMode == checkmode.Default { - return Pass + return Info, nil } } - // TODO: this should return an error - return Pass + return Notice, fmt.Errorf("Check %s is incorrectly configured", checkConfiguration.ID) } diff --git a/check/checklevel/type_string.go b/check/checklevel/type_string.go index 66c0b888a..89bbacf56 100644 --- a/check/checklevel/type_string.go +++ b/check/checklevel/type_string.go @@ -8,16 +8,15 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} - _ = x[Pass-0] - _ = x[Info-1] - _ = x[Warning-2] - _ = x[Error-3] - _ = x[Notice-4] + _ = x[Info-0] + _ = x[Warning-1] + _ = x[Error-2] + _ = x[Notice-3] } -const _Type_name = "passinfowarningerrornotice" +const _Type_name = "infowarningerrornotice" -var _Type_index = [...]uint8{0, 4, 8, 15, 20, 26} +var _Type_index = [...]uint8{0, 4, 11, 16, 22} func (i Type) String() string { if i < 0 || i >= Type(len(_Type_index)-1) { From cd49ac3e189fe0c0c9d21a96cdd961d9ac7aecf6 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 11:19:52 -0700 Subject: [PATCH 25/54] Add/improve code comments --- check/check.go | 4 ++ .../checkconfigurations.go | 47 ++++++++++--------- check/checkdata/checkdata.go | 11 +++++ check/checkfunctions/checkfunctions.go | 4 +- check/checkfunctions/library.go | 2 + check/checkfunctions/sketch.go | 2 + check/checklevel/checklevel.go | 5 +- check/checkresult/checkresult.go | 1 + configuration/checkmode/checkmode.go | 4 +- configuration/configuration.go | 6 +++ configuration/defaults.go | 2 + project/library/library.go | 6 ++- .../libraryproperties/libraryproperties.go | 7 +++ project/packageindex/packageindex.go | 7 +++ project/platform/platform.go | 6 +++ project/project.go | 47 +++++++++++-------- project/projecttype/projecttype.go | 2 + project/sketch/sketch.go | 6 +++ 18 files changed, 123 insertions(+), 46 deletions(-) diff --git a/check/check.go b/check/check.go index 0623a11d0..2191317c9 100644 --- a/check/check.go +++ b/check/check.go @@ -1,3 +1,4 @@ +// Package check runs checks on a project. package check import ( @@ -16,6 +17,7 @@ import ( "github.com/arduino/arduino-check/result/feedback" ) +// shouldRun returns whether a given check should be run for the given project under the current tool configuration. func shouldRun(checkConfiguration checkconfigurations.Type, currentProject project.Type) bool { configurationCheckModes := configuration.CheckModes(currentProject.SuperprojectType) @@ -52,6 +54,7 @@ func shouldRun(checkConfiguration checkconfigurations.Type, currentProject proje return false } +// 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)) @@ -62,6 +65,7 @@ func message(templateText string, checkOutput string) string { return messageBuffer.String() } +// RunChecks runs all checks for the given project and outputs the results. func RunChecks(project project.Type) { fmt.Printf("Checking %s in %s\n", project.ProjectType.String(), project.Path.String()) diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go index 5c759f4d0..13af74d93 100644 --- a/check/checkconfigurations/checkconfigurations.go +++ b/check/checkconfigurations/checkconfigurations.go @@ -1,3 +1,11 @@ +/* +Package checkconfigurations defines the configuration of each check: +- metadata +- output template +- under which conditions it's enabled +- the level of a failure +- which function implements it +*/ package checkconfigurations import ( @@ -6,29 +14,24 @@ import ( "github.com/arduino/arduino-check/project/projecttype" ) +// Type is the type for check configurations. type Type struct { - ProjectType projecttype.Type - // Arbitrary text for the log - Category string - Subcategory string - // Unique check identifier - ID string - Name string - Description string - // The warning/error message template displayed when the check fails - // The check function output will be filled into the template - MessageTemplate string - // The following fields define under which tool configuration modes the check will run - // Check is disabled when tool is in any of these modes - DisableModes []checkmode.Type - // Check is only enabled when tool is in one of these modes - EnableModes []checkmode.Type - // The following fields define the check level in each configuration mode - InfoModes []checkmode.Type - WarningModes []checkmode.Type - ErrorModes []checkmode.Type - // The function that implements the check - CheckFunction checkfunctions.Type + ProjectType projecttype.Type // The project type the check applies to. + // The following fields provide arbitrary text for the tool output associated with each check: + Category string + Subcategory string + ID string // Unique check identifier: + Name string // Short description of the check. + Description string // Supplemental information about the check. + MessageTemplate string // The warning/error message template displayed when the check fails. Will be filled by check function output. + // The following fields define under which tool configuration modes the check will run: + DisableModes []checkmode.Type // Check is disabled when tool is in any of these modes. + EnableModes []checkmode.Type // Check is only enabled when tool is in one of these modes. + // The following fields define the check level in each configuration mode: + InfoModes []checkmode.Type // Failure of the check only results in an informational message. + WarningModes []checkmode.Type // Failure of the check is considered a warning. + ErrorModes []checkmode.Type // Failure of the check is considered an error. + CheckFunction checkfunctions.Type // The function that implements the check. } // Checks is an array of structs that define the configuration of each check. diff --git a/check/checkdata/checkdata.go b/check/checkdata/checkdata.go index 1fa1b4fe8..29dc207d9 100644 --- a/check/checkdata/checkdata.go +++ b/check/checkdata/checkdata.go @@ -1,3 +1,7 @@ +/* +Package checkdata handles the collection of data specific to a project before running the checks on it. +This is for data required by multiple checks. +*/ package checkdata import ( @@ -11,34 +15,41 @@ import ( var projectType projecttype.Type +// ProjectType returns the type of the project being checked. func ProjectType() projecttype.Type { return projectType } var projectPath *paths.Path +// ProjectPath returns the path to the project being checked. func ProjectPath() *paths.Path { return projectPath } var libraryPropertiesLoadError error +// LibraryPropertiesLoadError returns the error output from loading the library.properties metadata file. func LibraryPropertiesLoadError() error { return libraryPropertiesLoadError } var libraryProperties *properties.Map +// LibraryProperties returns the data from the library.properties metadata file. func LibraryProperties() *properties.Map { return libraryProperties } var libraryPropertiesSchemaValidationResult *gojsonschema.Result +// LibraryPropertiesSchemaValidationResult returns the result of validating library.properties against the JSON schema. +// See: https://github.com/xeipuuv/gojsonschema func LibraryPropertiesSchemaValidationResult() *gojsonschema.Result { return libraryPropertiesSchemaValidationResult } +// Initialize gathers the check data for the specified project. func Initialize(project project.Type) { projectType = project.ProjectType projectPath = project.Path diff --git a/check/checkfunctions/checkfunctions.go b/check/checkfunctions/checkfunctions.go index 9154df707..42f4e2cd7 100644 --- a/check/checkfunctions/checkfunctions.go +++ b/check/checkfunctions/checkfunctions.go @@ -1,8 +1,10 @@ +// Package checkfunctions contains the functions that implement each check. package checkfunctions import ( "github.com/arduino/arduino-check/check/checkresult" ) -// output is the contextual information that will be added to the stock message +// Type is the function signature for the check functions. +// The `output` result is the contextual information that will be inserted into the check's message template. type Type func() (result checkresult.Type, output string) diff --git a/check/checkfunctions/library.go b/check/checkfunctions/library.go index ba06a80bb..edeab008c 100644 --- a/check/checkfunctions/library.go +++ b/check/checkfunctions/library.go @@ -1,5 +1,7 @@ package checkfunctions +// The check functions for libraries. + import ( "github.com/arduino/arduino-check/check/checkdata" "github.com/arduino/arduino-check/check/checkresult" diff --git a/check/checkfunctions/sketch.go b/check/checkfunctions/sketch.go index e5f5e0809..ef3436e23 100644 --- a/check/checkfunctions/sketch.go +++ b/check/checkfunctions/sketch.go @@ -1,5 +1,7 @@ package checkfunctions +// The check functions for sketches. + import ( "github.com/arduino/arduino-check/check/checkdata" "github.com/arduino/arduino-check/check/checkresult" diff --git a/check/checklevel/checklevel.go b/check/checklevel/checklevel.go index abc2a86b1..a2347a8a2 100644 --- a/check/checklevel/checklevel.go +++ b/check/checklevel/checklevel.go @@ -1,3 +1,4 @@ +// Package checklevel defines the level assigned to a check failure. package checklevel import ( @@ -8,10 +9,11 @@ import ( "github.com/arduino/arduino-check/configuration/checkmode" ) +// Type is the type for the check levels. //go:generate stringer -type=Type -linecomment type Type int -// Line comments set the string for each level +// The line comments set the string for each level. const ( Info Type = iota // info Warning // warning @@ -19,6 +21,7 @@ const ( Notice // notice ) +// CheckLevel determines the check level assigned to failure of the given check under the current tool configuration. func CheckLevel(checkConfiguration checkconfigurations.Type) (Type, error) { configurationCheckModes := configuration.CheckModes(checkConfiguration.ProjectType) for _, errorMode := range checkConfiguration.ErrorModes { diff --git a/check/checkresult/checkresult.go b/check/checkresult/checkresult.go index 7c0f29fc5..e63f80759 100644 --- a/check/checkresult/checkresult.go +++ b/check/checkresult/checkresult.go @@ -1,3 +1,4 @@ +// Package checkresult defines the possible result values returned by a check. package checkresult //go:generate stringer -type=Type -linecomment diff --git a/configuration/checkmode/checkmode.go b/configuration/checkmode/checkmode.go index dd4bc6c09..e6478944d 100644 --- a/configuration/checkmode/checkmode.go +++ b/configuration/checkmode/checkmode.go @@ -1,3 +1,4 @@ +// Package checkmode defines the tool configuration options that affect checks. package checkmode import ( @@ -5,6 +6,7 @@ import ( "github.com/sirupsen/logrus" ) +// Type is the type for check modes. type Type int //go:generate stringer -type=Type -linecomment @@ -17,10 +19,10 @@ const ( Default // default ) +// Modes merges the default check mode values for the given superproject type with any user-specified check mode settings. func Modes(defaultCheckModes map[projecttype.Type]map[Type]bool, customCheckModes map[Type]bool, superprojectType projecttype.Type) map[Type]bool { checkModes := make(map[Type]bool) - // Merge the default settings with any custom settings specified by the user for key, defaultValue := range defaultCheckModes[superprojectType] { customCheckModeValue, customCheckModeIsConfigured := customCheckModes[key] if customCheckModeIsConfigured { diff --git a/configuration/configuration.go b/configuration/configuration.go index 450950526..31a12217f 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -1,3 +1,4 @@ +// Package configuration handles the configuration of the arduino-check tool. package configuration import ( @@ -7,6 +8,7 @@ import ( "github.com/sirupsen/logrus" ) +// Initialize sets up the tool configuration according to defaults and user-specified options. func Initialize() { setDefaults() // TODO configuration according to command line input @@ -30,24 +32,28 @@ func Initialize() { var customCheckModes = make(map[checkmode.Type]bool) +// CheckModes returns the check modes configuration for the given project type. func CheckModes(superprojectType projecttype.Type) map[checkmode.Type]bool { return checkmode.Modes(defaultCheckModes, customCheckModes, superprojectType) } var superprojectType projecttype.Type +// SuperprojectType returns the superproject type filter configuration. func SuperprojectType() projecttype.Type { return superprojectType } var recursive bool +// Recursive returns the recursive project search configuration value. func Recursive() bool { return recursive } var targetPath *paths.Path +// TargetPath returns the projects search path. func TargetPath() *paths.Path { return targetPath } diff --git a/configuration/defaults.go b/configuration/defaults.go index a353cd121..535ffc44f 100644 --- a/configuration/defaults.go +++ b/configuration/defaults.go @@ -1,5 +1,7 @@ package configuration +// The default configuration settings. + import ( "github.com/arduino/arduino-check/configuration/checkmode" "github.com/arduino/arduino-check/project/projecttype" diff --git a/project/library/library.go b/project/library/library.go index e3d1d0c0e..77881f08d 100644 --- a/project/library/library.go +++ b/project/library/library.go @@ -1,3 +1,4 @@ +// Package library provides functions specific to checking Arduino libraries. package library import ( @@ -6,13 +7,14 @@ import ( var empty struct{} -// reference: https://github.com/arduino/arduino-cli/blob/0.13.0/arduino/libraries/libraries.go#L167 +// Reference: https://github.com/arduino/arduino-cli/blob/0.13.0/arduino/libraries/libraries.go#L167 var headerFileValidExtensions = map[string]struct{}{ ".h": empty, ".hpp": empty, ".hh": empty, } +// HasHeaderFileValidExtension returns whether the file at the given path has a valid library header file extension. func HasHeaderFileValidExtension(filePath *paths.Path) bool { _, hasHeaderFileValidExtension := headerFileValidExtensions[filePath.Ext()] if hasHeaderFileValidExtension { @@ -21,10 +23,12 @@ func HasHeaderFileValidExtension(filePath *paths.Path) bool { return false } +// See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-metadata var metadataFilenames = map[string]struct{}{ "library.properties": empty, } +// IsMetadataFile returns whether the file at the given path is an Arduino library metadata file. func IsMetadataFile(filePath *paths.Path) bool { _, isMetadataFile := metadataFilenames[filePath.Base()] if isMetadataFile { diff --git a/project/library/libraryproperties/libraryproperties.go b/project/library/libraryproperties/libraryproperties.go index e091f4f7f..a64b2bcd0 100644 --- a/project/library/libraryproperties/libraryproperties.go +++ b/project/library/libraryproperties/libraryproperties.go @@ -1,3 +1,4 @@ +// Package libraryproperties provides functions for working with the library.properties Arduino library metadata file. package libraryproperties import ( @@ -10,6 +11,7 @@ import ( "github.com/xeipuuv/gojsonschema" ) +// Properties parses the library.properties from the given path and returns the data. func Properties(libraryPath *paths.Path) (*properties.Map, error) { libraryProperties, err := properties.Load(libraryPath.Join("library.properties").String()) if err != nil { @@ -18,6 +20,7 @@ func Properties(libraryPath *paths.Path) (*properties.Map, error) { return libraryProperties, nil } +// Validate validates library.properties data against the JSON schema. func Validate(libraryProperties *properties.Map) *gojsonschema.Result { workingPath, _ := os.Getwd() schemaPath := paths.New(workingPath).Join("arduino-library-properties-schema.json") @@ -38,14 +41,18 @@ func Validate(libraryProperties *properties.Map) *gojsonschema.Result { return result } +// FieldMissing returns whether the given required field is missing from library.properties. func FieldMissing(fieldName string, validationResult *gojsonschema.Result) bool { return ValidationErrorMatch("required", "(root)", fieldName+" is required", validationResult) } +// FieldPatternMismatch returns whether the given field did not match the regular expression defined in the JSON schema. func FieldPatternMismatch(fieldName string, validationResult *gojsonschema.Result) bool { return ValidationErrorMatch("pattern", fieldName, "", validationResult) } +// ValidationErrorMatch returns whether the given query matches against the JSON schema validation error. +// See: https://github.com/xeipuuv/gojsonschema#working-with-errors func ValidationErrorMatch(typeQuery string, fieldQuery string, descriptionQuery string, validationResult *gojsonschema.Result) bool { if validationResult.Valid() { // No error, so nothing to match diff --git a/project/packageindex/packageindex.go b/project/packageindex/packageindex.go index 940dc7a6c..894ffbda6 100644 --- a/project/packageindex/packageindex.go +++ b/project/packageindex/packageindex.go @@ -1,3 +1,7 @@ +/* +Package packageindex provides functions specific to checking the package index files of the Arduino Boards Manager. +See: https://arduino.github.io/arduino-cli/latest/package_index_json-specification +*/ package packageindex import ( @@ -8,10 +12,12 @@ import ( var empty struct{} +// Reference: https://arduino.github.io/arduino-cli/latest/package_index_json-specification/#naming-of-the-json-index-file var validExtensions = map[string]struct{}{ ".json": empty, } +// HasValidExtension returns whether the file at the given path has a valid package index extension. func HasValidExtension(filePath *paths.Path) bool { _, hasValidExtension := validExtensions[filePath.Ext()] if hasValidExtension { @@ -27,6 +33,7 @@ var validFilenameRegex = map[bool]*regexp.Regexp{ false: regexp.MustCompile(`^package_(.+_)+index.json$`), } +// HasValidFilename returns whether the file at the given path has a valid package index filename. func HasValidFilename(filePath *paths.Path, officialCheckMode bool) bool { regex := validFilenameRegex[officialCheckMode] filename := filePath.Base() diff --git a/project/platform/platform.go b/project/platform/platform.go index 8053dd603..47c949479 100644 --- a/project/platform/platform.go +++ b/project/platform/platform.go @@ -1,3 +1,7 @@ +/* +Package packageindex provides functions specific to checking Arduino boards platforms. +See: https://arduino.github.io/arduino-cli/latest/platform-specification/ +*/ package platform import ( @@ -14,6 +18,7 @@ var configurationFilenames = map[string]struct{}{ "programmers.txt": empty, } +// IsConfigurationFile returns whether the file at the given path has a boards platform configuration file filename. func IsConfigurationFile(filePath *paths.Path) bool { _, isConfigurationFile := configurationFilenames[filePath.Base()] if isConfigurationFile { @@ -27,6 +32,7 @@ var requiredConfigurationFilenames = map[string]struct{}{ "boards.txt": empty, } +// IsRequiredConfigurationFile returns whether the file at the given path has the filename of a required boards platform configuration file. func IsRequiredConfigurationFile(filePath *paths.Path) bool { _, isRequiredConfigurationFile := requiredConfigurationFilenames[filePath.Base()] if isRequiredConfigurationFile { diff --git a/project/project.go b/project/project.go index fb3c85eba..72cb4c36f 100644 --- a/project/project.go +++ b/project/project.go @@ -1,3 +1,4 @@ +// Package project finds and classifies Arduino projects. package project import ( @@ -13,12 +14,15 @@ import ( "github.com/sirupsen/logrus" ) +// Type is the type for project definitions. type Type struct { Path *paths.Path ProjectType projecttype.Type SuperprojectType projecttype.Type } +// FindProjects searches the target path configured by the user for projects of the type configured by the user as well as the subprojects of those project. +// It returns a slice containing the definitions of each found project. func FindProjects() ([]Type, error) { targetPath := configuration.TargetPath() superprojectTypeConfiguration := configuration.SuperprojectType() @@ -26,7 +30,7 @@ func FindProjects() ([]Type, error) { var foundProjects []Type - // If targetPath is a file, targetPath itself is the project, so it's only necessary to determine/verify the type + // If targetPath is a file, targetPath itself is the project, so it's only necessary to determine/verify the type. if targetPath.IsNotDir() { logrus.Debug("Projects path is file") // The filename provides additional information about the project type. So rather than using isProject(), which doesn't make use this information, use a specialized function that does. @@ -56,6 +60,7 @@ func FindProjects() ([]Type, error) { return foundProjects, nil } +// findProjects finds projects of the given type and subprojects of those projects. It returns a slice containing the definitions of all found projects. func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursive bool) []Type { var foundProjects []Type @@ -65,19 +70,19 @@ func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursiv foundProject := Type{ Path: targetPath, ProjectType: foundProjectType, - // findSubprojects() will overwrite this with the correct value when the project is a subproject + // findSubprojects() will overwrite this with the correct value when the project is a subproject. SuperprojectType: foundProjectType, } foundProjects = append(foundProjects, foundProject) foundProjects = append(foundProjects, findSubprojects(foundProject, foundProject.ProjectType)...) - // Don't search recursively past a project + // Don't search recursively past a project. return foundProjects } if recursive { - // targetPath was not a project, so search the subfolders + // targetPath was not a project, so search the subfolders. directoryListing, _ := targetPath.ReadDir() directoryListing.FilterDirs() for _, potentialProjectDirectory := range directoryListing { @@ -88,6 +93,8 @@ func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursiv return foundProjects } +// findSubprojects finds subprojects of the given project. +// For example, the subprojects of a library are its example sketches. func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) []Type { var immediateSubprojects []Type @@ -98,7 +105,7 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ case projecttype.Library: subprojectPath := superproject.Path.Join("examples") immediateSubprojects = append(immediateSubprojects, findProjects(subprojectPath, projecttype.Sketch, true)...) - // Apparently there is some level of official support for "example" in addition to the specification-compliant "examples" + // Apparently there is some level of official support for "example" in addition to the specification-compliant "examples". // see: https://github.com/arduino/arduino-cli/blob/0.13.0/arduino/libraries/loader.go#L153 subprojectPath = superproject.Path.Join("example") immediateSubprojects = append(immediateSubprojects, findProjects(subprojectPath, projecttype.Sketch, true)...) @@ -111,11 +118,11 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ } var allSubprojects []Type - // Subprojects may have their own subprojects + // Subprojects may have their own subprojects. for _, immediateSubproject := range immediateSubprojects { - // Subprojects at all levels should have SuperprojectType set to the top level superproject's type, not the immediate parent's type + // Subprojects at all levels should have SuperprojectType set to the top level superproject's type, not the immediate parent's type. immediateSubproject.SuperprojectType = apexSuperprojectType - // Each parent project should be followed in the list by its subprojects + // Each parent project should be followed in the list by its subprojects. allSubprojects = append(allSubprojects, immediateSubproject) allSubprojects = append(allSubprojects, findSubprojects(immediateSubproject, apexSuperprojectType)...) } @@ -123,7 +130,7 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ return allSubprojects } -// isProject determines if a path contains an Arduino project, and if so which type +// isProject determines if a path contains an Arduino project, and if so which type. func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { logrus.Tracef("Checking if %s is %s", potentialProjectPath.String(), projectType.String()) if (projectType == projecttype.All || projectType == projecttype.Sketch) && isSketch(potentialProjectPath) { @@ -142,7 +149,7 @@ func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) ( return false, projecttype.Not } -// isProject determines if a file is the indicator file for an Arduino project, and if so which type +// isProject determines if a file is the indicator file for an Arduino project, and if so which type. func isProjectIndicatorFile(potentialProjectFilePath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { logrus.Tracef("Checking if %s is %s indicator file", potentialProjectFilePath.String(), projectType.String()) if (projectType == projecttype.All || projectType == projecttype.Sketch) && isSketchIndicatorFile(potentialProjectFilePath) { @@ -162,8 +169,8 @@ func isProjectIndicatorFile(potentialProjectFilePath *paths.Path, projectType pr return false, projecttype.Not } -// isSketch determines if a path is an Arduino sketch -// Note: this intentionally does not determine the validity of the sketch, only that the developer's intent was for it to be a sketch +// isSketch determines whether a path is an Arduino sketch. +// Note: this intentionally does not determine the validity of the sketch, only that the developer's intent was for it to be a sketch. func isSketch(potentialProjectPath *paths.Path) bool { directoryListing, _ := potentialProjectPath.ReadDir() directoryListing.FilterOutDirs() @@ -173,7 +180,7 @@ func isSketch(potentialProjectPath *paths.Path) bool { } } - // No file was found with a valid main sketch file extension + // No file was found with a valid main sketch file extension. return false } @@ -184,8 +191,8 @@ func isSketchIndicatorFile(filePath *paths.Path) bool { return false } -// isLibrary determines if a path is an Arduino library -// Note: this intentionally does not determine the validity of the library, only that the developer's intent was for it to be a library +// isLibrary determines if a path is an Arduino library. +// Note: this intentionally does not determine the validity of the library, only that the developer's intent was for it to be a library. func isLibrary(potentialProjectPath *paths.Path) bool { // Arduino libraries will always have one of the following files in its root folder: // - a library.properties metadata file @@ -198,7 +205,7 @@ func isLibrary(potentialProjectPath *paths.Path) bool { } } - // None of the files required for a valid Arduino library were found + // None of the files required for a valid Arduino library were found. return false } @@ -214,8 +221,8 @@ func isLibraryIndicatorFile(filePath *paths.Path) bool { return false } -// isPlatform determines if a path is an Arduino boards platform -// Note: this intentionally does not determine the validity of the platform, only that the developer's intent was for it to be a platform +// isPlatform determines if a path is an Arduino boards platform. +// Note: this intentionally does not determine the validity of the platform, only that the developer's intent was for it to be a platform. func isPlatform(potentialProjectPath *paths.Path) bool { directoryListing, _ := potentialProjectPath.ReadDir() directoryListing.FilterOutDirs() @@ -244,8 +251,8 @@ func isStrictPlatformIndicatorFile(filePath *paths.Path) bool { return false } -// isPackageIndex determines if a path contains an Arduino package index -// Note: this intentionally does not determine the validity of the package index, only that the developer's intent was for it to be a package index +// isPackageIndex determines if a path contains an Arduino package index. +// Note: this intentionally does not determine the validity of the package index, only that the developer's intent was for it to be a package index. func isPackageIndex(potentialProjectPath *paths.Path) bool { directoryListing, _ := potentialProjectPath.ReadDir() directoryListing.FilterOutDirs() diff --git a/project/projecttype/projecttype.go b/project/projecttype/projecttype.go index f65361117..a7e886cec 100644 --- a/project/projecttype/projecttype.go +++ b/project/projecttype/projecttype.go @@ -1,5 +1,7 @@ +// Package projecttype defines the Arduino project types. package projecttype +// Type is the type for Arduino project types. //go:generate stringer -type=Type -linecomment type Type int diff --git a/project/sketch/sketch.go b/project/sketch/sketch.go index 51a0ea180..ef4209785 100644 --- a/project/sketch/sketch.go +++ b/project/sketch/sketch.go @@ -1,3 +1,7 @@ +/* +Package sketch provides functions specific to checking Arduino sketches. +See: https://arduino.github.io/arduino-cli/latest/sketch-specification/ +*/ package sketch import ( @@ -5,6 +9,8 @@ import ( "github.com/arduino/go-paths-helper" ) +// HasMainFileValidExtension returns whether the file at the given path has a valid sketch main file extension. +// Sketches may contain source files with other extensions (e.g., .h, .cpp), but they are required to have at least one file with a main extension. func HasMainFileValidExtension(filePath *paths.Path) bool { _, hasMainFileValidExtension := globals.MainFileValidExtensions[filePath.Ext()] if hasMainFileValidExtension { From 952112dca8fda5a3480b648213edbce08499b1f3 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 12:11:01 -0700 Subject: [PATCH 26/54] Add TODO comment re: coniguration.init() vs configuration.Initialize() --- configuration/configuration.go | 1 + 1 file changed, 1 insertion(+) diff --git a/configuration/configuration.go b/configuration/configuration.go index 31a12217f..43741ba47 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -8,6 +8,7 @@ import ( "github.com/sirupsen/logrus" ) +// TODO: will it be possible to use init() instead? // Initialize sets up the tool configuration according to defaults and user-specified options. func Initialize() { setDefaults() From 00178a74132454937eef1798b28c9cccd517335b Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 12:15:55 -0700 Subject: [PATCH 27/54] Rename checkconfigurations.Type.Name field to checkconfigurations.Type.Brief "Name" seemed to give the wrong impression of the purpose of this field. --- check/checkconfigurations/checkconfigurations.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go index 13af74d93..0e2a53d5a 100644 --- a/check/checkconfigurations/checkconfigurations.go +++ b/check/checkconfigurations/checkconfigurations.go @@ -21,7 +21,7 @@ type Type struct { Category string Subcategory string ID string // Unique check identifier: - Name string // Short description of the check. + Brief string // Short description of the check. Description string // Supplemental information about the check. MessageTemplate string // The warning/error message template displayed when the check fails. Will be filled by check function output. // The following fields define under which tool configuration modes the check will run: @@ -41,7 +41,7 @@ var Configurations = []Type{ Category: "library.properties", Subcategory: "general", ID: "LP001", - Name: "invalid format", + Brief: "invalid format", Description: "", MessageTemplate: "library.properties has an invalid format: {{.}}", DisableModes: nil, @@ -56,7 +56,7 @@ var Configurations = []Type{ Category: "library.properties", Subcategory: "name field", ID: "LP002", - Name: "missing name field", + Brief: "missing name field", Description: "", MessageTemplate: "missing name field in library.properties", DisableModes: nil, @@ -71,7 +71,7 @@ var Configurations = []Type{ Category: "library.properties", Subcategory: "name field", ID: "LP003", - Name: "disallowed characters", + Brief: "disallowed characters", Description: "", MessageTemplate: "disallowed characters in library.properties name field. See: https://arduino.github.io/arduino-cli/latest/library-specification/#libraryproperties-file-format", DisableModes: nil, @@ -86,7 +86,7 @@ var Configurations = []Type{ Category: "library.properties", Subcategory: "version field", ID: "LP004", - Name: "missing version field", + Brief: "missing version field", Description: "", MessageTemplate: "missing version field in library.properties", DisableModes: nil, @@ -101,7 +101,7 @@ var Configurations = []Type{ Category: "structure", Subcategory: "", ID: "SS001", - Name: ".pde extension", + Brief: ".pde extension", Description: "The .pde extension is used by both Processing sketches and Arduino sketches. Processing sketches should either be in the \"data\" subfolder of the sketch or in the \"extras\" folder of the library. Arduino sketches should use the modern .ino extension", MessageTemplate: "{{.}} uses deprecated .pde file extension. Use .ino for Arduino sketches", DisableModes: nil, From c1c3b9c4db2f56b8e830f71e6921877f9d7b9866 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 12:22:02 -0700 Subject: [PATCH 28/54] Rename configuration.SuperprojectType() to configuration.SuperprojectTypeFilter() The name "SuperprojectType" is too vague and used as a project.Type field name to mean a completely different thing. --- configuration/configuration.go | 12 ++++++------ configuration/defaults.go | 2 +- project/project.go | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/configuration/configuration.go b/configuration/configuration.go index 43741ba47..95d29c5e2 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -25,9 +25,9 @@ func Initialize() { logrus.SetLevel(logrus.PanicLevel) logrus.WithFields(logrus.Fields{ - "superproject type": SuperprojectType().String(), - "recursive": Recursive(), - "projects path": TargetPath().String(), + "superproject type filter": SuperprojectTypeFilter().String(), + "recursive": Recursive(), + "projects path": TargetPath().String(), }).Debug("Configuration initialized") } @@ -38,11 +38,11 @@ func CheckModes(superprojectType projecttype.Type) map[checkmode.Type]bool { return checkmode.Modes(defaultCheckModes, customCheckModes, superprojectType) } -var superprojectType projecttype.Type +var superprojectTypeFilter projecttype.Type // SuperprojectType returns the superproject type filter configuration. -func SuperprojectType() projecttype.Type { - return superprojectType +func SuperprojectTypeFilter() projecttype.Type { + return superprojectTypeFilter } var recursive bool diff --git a/configuration/defaults.go b/configuration/defaults.go index 535ffc44f..d9b83d86f 100644 --- a/configuration/defaults.go +++ b/configuration/defaults.go @@ -8,7 +8,7 @@ import ( ) func setDefaults() { - superprojectType = projecttype.All + superprojectTypeFilter = projecttype.All recursive = true // TODO: targetPath defaults to current path } diff --git a/project/project.go b/project/project.go index 72cb4c36f..aa0a63738 100644 --- a/project/project.go +++ b/project/project.go @@ -25,7 +25,7 @@ type Type struct { // It returns a slice containing the definitions of each found project. func FindProjects() ([]Type, error) { targetPath := configuration.TargetPath() - superprojectTypeConfiguration := configuration.SuperprojectType() + superprojectTypeFilter := configuration.SuperprojectTypeFilter() recursive := configuration.Recursive() var foundProjects []Type @@ -34,7 +34,7 @@ func FindProjects() ([]Type, error) { if targetPath.IsNotDir() { logrus.Debug("Projects path is file") // The filename provides additional information about the project type. So rather than using isProject(), which doesn't make use this information, use a specialized function that does. - isProject, projectType := isProjectIndicatorFile(targetPath, superprojectTypeConfiguration) + isProject, projectType := isProjectIndicatorFile(targetPath, superprojectTypeFilter) if isProject { foundProject := Type{ Path: targetPath.Parent(), @@ -51,7 +51,7 @@ func FindProjects() ([]Type, error) { return nil, fmt.Errorf("specified path %s is not an Arduino project", targetPath.String()) } - foundProjects = append(foundProjects, findProjects(targetPath, superprojectTypeConfiguration, recursive)...) + foundProjects = append(foundProjects, findProjects(targetPath, superprojectTypeFilter, recursive)...) if foundProjects == nil { return nil, fmt.Errorf("no projects found under %s", targetPath.String()) From 63a68f555064b363c7c19070fdac91cecb444430 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 12:26:22 -0700 Subject: [PATCH 29/54] Rename project.findProjects() to project.findProjectsUnderPath() The former name was too similar to project.FindProjects(). --- project/project.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/project/project.go b/project/project.go index aa0a63738..0da6ea8db 100644 --- a/project/project.go +++ b/project/project.go @@ -51,7 +51,7 @@ func FindProjects() ([]Type, error) { return nil, fmt.Errorf("specified path %s is not an Arduino project", targetPath.String()) } - foundProjects = append(foundProjects, findProjects(targetPath, superprojectTypeFilter, recursive)...) + foundProjects = append(foundProjects, findProjectsUnderPath(targetPath, superprojectTypeFilter, recursive)...) if foundProjects == nil { return nil, fmt.Errorf("no projects found under %s", targetPath.String()) @@ -60,8 +60,8 @@ func FindProjects() ([]Type, error) { return foundProjects, nil } -// findProjects finds projects of the given type and subprojects of those projects. It returns a slice containing the definitions of all found projects. -func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursive bool) []Type { +// findProjectsUnderPath finds projects of the given type and subprojects of those projects. It returns a slice containing the definitions of all found projects. +func findProjectsUnderPath(targetPath *paths.Path, projectType projecttype.Type, recursive bool) []Type { var foundProjects []Type isProject, foundProjectType := isProject(targetPath, projectType) @@ -86,7 +86,7 @@ func findProjects(targetPath *paths.Path, projectType projecttype.Type, recursiv directoryListing, _ := targetPath.ReadDir() directoryListing.FilterDirs() for _, potentialProjectDirectory := range directoryListing { - foundProjects = append(foundProjects, findProjects(potentialProjectDirectory, projectType, recursive)...) + foundProjects = append(foundProjects, findProjectsUnderPath(potentialProjectDirectory, projectType, recursive)...) } } @@ -104,14 +104,14 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ return nil case projecttype.Library: subprojectPath := superproject.Path.Join("examples") - immediateSubprojects = append(immediateSubprojects, findProjects(subprojectPath, projecttype.Sketch, true)...) + immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Sketch, true)...) // Apparently there is some level of official support for "example" in addition to the specification-compliant "examples". // see: https://github.com/arduino/arduino-cli/blob/0.13.0/arduino/libraries/loader.go#L153 subprojectPath = superproject.Path.Join("example") - immediateSubprojects = append(immediateSubprojects, findProjects(subprojectPath, projecttype.Sketch, true)...) + immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Sketch, true)...) case projecttype.Platform: subprojectPath := superproject.Path.Join("libraries") - immediateSubprojects = append(immediateSubprojects, findProjects(subprojectPath, projecttype.Library, false)...) + immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Library, false)...) case projecttype.PackageIndex: // Platform indexes don't have subprojects return nil From 85470118e1a82869d6362fb05ab898c1822fdc36 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 12:36:11 -0700 Subject: [PATCH 30/54] Add a getter for checkconfigurations.configurations I use getters for everything else, so this provides consistency. --- check/check.go | 2 +- check/checkconfigurations/checkconfigurations.go | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/check/check.go b/check/check.go index 2191317c9..d5fc0226b 100644 --- a/check/check.go +++ b/check/check.go @@ -71,7 +71,7 @@ func RunChecks(project project.Type) { checkdata.Initialize(project) - for _, checkConfiguration := range checkconfigurations.Configurations { + for _, checkConfiguration := range checkconfigurations.Configurations() { if !shouldRun(checkConfiguration, project) { // TODO: this should only be printed to log and in verbose mode fmt.Printf("Skipping check: %s\n", checkConfiguration.ID) diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go index 0e2a53d5a..c108d86ed 100644 --- a/check/checkconfigurations/checkconfigurations.go +++ b/check/checkconfigurations/checkconfigurations.go @@ -34,8 +34,8 @@ type Type struct { CheckFunction checkfunctions.Type // The function that implements the check. } -// Checks is an array of structs that define the configuration of each check. -var Configurations = []Type{ +// configurations is an array of structs that define the configuration of each check. +var configurations = []Type{ { ProjectType: projecttype.Library, Category: "library.properties", @@ -112,3 +112,8 @@ var Configurations = []Type{ CheckFunction: checkfunctions.PdeSketchExtension, }, } + +// Configurations returns the slice of check configurations. +func Configurations() []Type { + return configurations +} From f9027f8c908ab09202e9db04f66b56599067b668 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 13:00:38 -0700 Subject: [PATCH 31/54] Improve organization of code --- check/check.go | 60 +++++++++---------- .../checkconfigurations.go | 10 ++-- check/checkdata/checkdata.go | 57 ++++-------------- check/checkdata/library.go | 41 +++++++++++++ 4 files changed, 89 insertions(+), 79 deletions(-) create mode 100644 check/checkdata/library.go diff --git a/check/check.go b/check/check.go index d5fc0226b..5fe747acd 100644 --- a/check/check.go +++ b/check/check.go @@ -17,6 +17,36 @@ import ( "github.com/arduino/arduino-check/result/feedback" ) +// RunChecks runs all checks for the given project and outputs the results. +func RunChecks(project project.Type) { + fmt.Printf("Checking %s in %s\n", project.ProjectType.String(), project.Path.String()) + + checkdata.Initialize(project) + + for _, checkConfiguration := range checkconfigurations.Configurations() { + if !shouldRun(checkConfiguration, project) { + // TODO: this should only be printed to log and in verbose mode + fmt.Printf("Skipping check: %s\n", checkConfiguration.ID) + continue + } + + fmt.Printf("Running check %s: ", checkConfiguration.ID) + result, output := checkConfiguration.CheckFunction() + fmt.Printf("%s\n", result.String()) + if result == checkresult.NotRun { + // TODO: make the check functions output an explanation for why they didn't run + fmt.Printf("%s: %s\n", checklevel.Notice, output) + } else if result != checkresult.Pass { + checkLevel, err := checklevel.CheckLevel(checkConfiguration) + if err != nil { + feedback.Errorf("Error while determining check level: %v", err) + os.Exit(1) + } + fmt.Printf("%s: %s\n", checkLevel.String(), message(checkConfiguration.MessageTemplate, output)) + } + } +} + // shouldRun returns whether a given check should be run for the given project under the current tool configuration. func shouldRun(checkConfiguration checkconfigurations.Type, currentProject project.Type) bool { configurationCheckModes := configuration.CheckModes(currentProject.SuperprojectType) @@ -64,33 +94,3 @@ func message(templateText string, checkOutput string) string { return messageBuffer.String() } - -// RunChecks runs all checks for the given project and outputs the results. -func RunChecks(project project.Type) { - fmt.Printf("Checking %s in %s\n", project.ProjectType.String(), project.Path.String()) - - checkdata.Initialize(project) - - for _, checkConfiguration := range checkconfigurations.Configurations() { - if !shouldRun(checkConfiguration, project) { - // TODO: this should only be printed to log and in verbose mode - fmt.Printf("Skipping check: %s\n", checkConfiguration.ID) - continue - } - - fmt.Printf("Running check %s: ", checkConfiguration.ID) - result, output := checkConfiguration.CheckFunction() - fmt.Printf("%s\n", result.String()) - if result == checkresult.NotRun { - // TODO: make the check functions output an explanation for why they didn't run - fmt.Printf("%s: %s\n", checklevel.Notice, output) - } else if result != checkresult.Pass { - checkLevel, err := checklevel.CheckLevel(checkConfiguration) - if err != nil { - feedback.Errorf("Error while determining check level: %v", err) - os.Exit(1) - } - fmt.Printf("%s: %s\n", checkLevel.String(), message(checkConfiguration.MessageTemplate, output)) - } - } -} diff --git a/check/checkconfigurations/checkconfigurations.go b/check/checkconfigurations/checkconfigurations.go index c108d86ed..cded58f71 100644 --- a/check/checkconfigurations/checkconfigurations.go +++ b/check/checkconfigurations/checkconfigurations.go @@ -34,6 +34,11 @@ type Type struct { CheckFunction checkfunctions.Type // The function that implements the check. } +// Configurations returns the slice of check configurations. +func Configurations() []Type { + return configurations +} + // configurations is an array of structs that define the configuration of each check. var configurations = []Type{ { @@ -112,8 +117,3 @@ var configurations = []Type{ CheckFunction: checkfunctions.PdeSketchExtension, }, } - -// Configurations returns the slice of check configurations. -func Configurations() []Type { - return configurations -} diff --git a/check/checkdata/checkdata.go b/check/checkdata/checkdata.go index 29dc207d9..aaee70a9a 100644 --- a/check/checkdata/checkdata.go +++ b/check/checkdata/checkdata.go @@ -6,13 +6,23 @@ package checkdata import ( "github.com/arduino/arduino-check/project" - "github.com/arduino/arduino-check/project/library/libraryproperties" "github.com/arduino/arduino-check/project/projecttype" "github.com/arduino/go-paths-helper" - "github.com/arduino/go-properties-orderedmap" - "github.com/xeipuuv/gojsonschema" ) +// Initialize gathers the check data for the specified project. +func Initialize(project project.Type) { + projectType = project.ProjectType + projectPath = project.Path + switch project.ProjectType { + case projecttype.Sketch: + case projecttype.Library: + InitializeForLibrary(project) + case projecttype.Platform: + case projecttype.PackageIndex: + } +} + var projectType projecttype.Type // ProjectType returns the type of the project being checked. @@ -26,44 +36,3 @@ var projectPath *paths.Path func ProjectPath() *paths.Path { return projectPath } - -var libraryPropertiesLoadError error - -// LibraryPropertiesLoadError returns the error output from loading the library.properties metadata file. -func LibraryPropertiesLoadError() error { - return libraryPropertiesLoadError -} - -var libraryProperties *properties.Map - -// LibraryProperties returns the data from the library.properties metadata file. -func LibraryProperties() *properties.Map { - return libraryProperties -} - -var libraryPropertiesSchemaValidationResult *gojsonschema.Result - -// LibraryPropertiesSchemaValidationResult returns the result of validating library.properties against the JSON schema. -// See: https://github.com/xeipuuv/gojsonschema -func LibraryPropertiesSchemaValidationResult() *gojsonschema.Result { - return libraryPropertiesSchemaValidationResult -} - -// Initialize gathers the check data for the specified project. -func Initialize(project project.Type) { - projectType = project.ProjectType - projectPath = project.Path - switch project.ProjectType { - case projecttype.Sketch: - case projecttype.Library: - libraryProperties, libraryPropertiesLoadError = libraryproperties.Properties(project.Path) - if libraryPropertiesLoadError != nil { - // TODO: can I even do this? - libraryPropertiesSchemaValidationResult = nil - } else { - libraryPropertiesSchemaValidationResult = libraryproperties.Validate(libraryProperties) - } - case projecttype.Platform: - case projecttype.PackageIndex: - } -} diff --git a/check/checkdata/library.go b/check/checkdata/library.go new file mode 100644 index 000000000..361b847e3 --- /dev/null +++ b/check/checkdata/library.go @@ -0,0 +1,41 @@ +package checkdata + +import ( + "github.com/arduino/arduino-check/project" + "github.com/arduino/arduino-check/project/library/libraryproperties" + "github.com/arduino/go-properties-orderedmap" + "github.com/xeipuuv/gojsonschema" +) + +// Initialize gathers the library check data for the specified project. +func InitializeForLibrary(project project.Type) { + libraryProperties, libraryPropertiesLoadError = libraryproperties.Properties(project.Path) + if libraryPropertiesLoadError != nil { + // TODO: can I even do this? + libraryPropertiesSchemaValidationResult = nil + } else { + libraryPropertiesSchemaValidationResult = libraryproperties.Validate(libraryProperties) + } +} + +var libraryPropertiesLoadError error + +// LibraryPropertiesLoadError returns the error output from loading the library.properties metadata file. +func LibraryPropertiesLoadError() error { + return libraryPropertiesLoadError +} + +var libraryProperties *properties.Map + +// LibraryProperties returns the data from the library.properties metadata file. +func LibraryProperties() *properties.Map { + return libraryProperties +} + +var libraryPropertiesSchemaValidationResult *gojsonschema.Result + +// LibraryPropertiesSchemaValidationResult returns the result of validating library.properties against the JSON schema. +// See: https://github.com/xeipuuv/gojsonschema +func LibraryPropertiesSchemaValidationResult() *gojsonschema.Result { + return libraryPropertiesSchemaValidationResult +} From 42039d9ea399046dcde0909966262155d43f137d Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 13:08:36 -0700 Subject: [PATCH 32/54] Return error when check run configuration is missing Checks should always be explicitly configured to run or not run in any check mode. --- check/check.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/check/check.go b/check/check.go index 5fe747acd..2ae9034f1 100644 --- a/check/check.go +++ b/check/check.go @@ -24,7 +24,13 @@ func RunChecks(project project.Type) { checkdata.Initialize(project) for _, checkConfiguration := range checkconfigurations.Configurations() { - if !shouldRun(checkConfiguration, project) { + runCheck, err := shouldRun(checkConfiguration, project) + if err != nil { + feedback.Errorf("Error while determining whether to run check: %v", err) + os.Exit(1) + } + + if !runCheck { // TODO: this should only be printed to log and in verbose mode fmt.Printf("Skipping check: %s\n", checkConfiguration.ID) continue @@ -48,40 +54,39 @@ func RunChecks(project project.Type) { } // shouldRun returns whether a given check should be run for the given project under the current tool configuration. -func shouldRun(checkConfiguration checkconfigurations.Type, currentProject project.Type) bool { +func shouldRun(checkConfiguration checkconfigurations.Type, currentProject project.Type) (bool, error) { configurationCheckModes := configuration.CheckModes(currentProject.SuperprojectType) if checkConfiguration.ProjectType != currentProject.ProjectType { - return false + return false, nil } for _, disableMode := range checkConfiguration.DisableModes { if configurationCheckModes[disableMode] == true { - return false + return false, nil } } for _, enableMode := range checkConfiguration.EnableModes { if configurationCheckModes[enableMode] == true { - return true + return true, nil } } // Use default for _, disableMode := range checkConfiguration.DisableModes { if disableMode == checkmode.Default { - return false + return false, nil } } for _, enableMode := range checkConfiguration.EnableModes { if enableMode == checkmode.Default { - return true + return true, nil } } - // TODO: this should return an error - return false + return false, fmt.Errorf("Check %s is incorrectly configured", checkConfiguration.ID) } // message fills the message template provided by the check configuration with the check output. From 658cdf60c43d55cf1dca85e7c7989ee43d66e5c7 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 19:12:10 -0700 Subject: [PATCH 33/54] Use packageindex.HasValidExtension() in package index detection code I made a dedicated function for this purpose, then forgot to use it! --- project/project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/project.go b/project/project.go index 0da6ea8db..75dc38220 100644 --- a/project/project.go +++ b/project/project.go @@ -266,7 +266,7 @@ func isPackageIndex(potentialProjectPath *paths.Path) bool { } func isPackageIndexIndicatorFile(filePath *paths.Path) bool { - if filePath.Ext() == ".json" { + if packageindex.HasValidExtension(filePath) { return true } From b1a0224e66dff2a3beddddbc1707070b09443214 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 21 Sep 2020 23:23:45 -0700 Subject: [PATCH 34/54] Define supported and valid library examples folder names in the library package --- project/library/library.go | 22 ++++++++++++++++++++++ project/project.go | 10 ++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/project/library/library.go b/project/library/library.go index 77881f08d..687777748 100644 --- a/project/library/library.go +++ b/project/library/library.go @@ -36,3 +36,25 @@ func IsMetadataFile(filePath *paths.Path) bool { } return false } + +// See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-examples +var examplesFolderValidNames = map[string]struct{}{ + "examples": empty, +} + +// Only "examples" is specification-compliant, but apparently "example" is also supported +// See: https://github.com/arduino/arduino-cli/blob/0.13.0/arduino/libraries/loader.go#L153 +var examplesFolderSupportedNames = map[string]struct{}{ + "examples": empty, + "example": empty, +} + +// ExamplesFolderNames returns a slice of supported examples folder names +func ExamplesFolderSupportedNames() []string { + folderNames := make([]string, 0, len(examplesFolderSupportedNames)) + for folderName := range examplesFolderSupportedNames { + folderNames = append(folderNames, folderName) + } + + return folderNames +} diff --git a/project/project.go b/project/project.go index 75dc38220..e0a9ccf4f 100644 --- a/project/project.go +++ b/project/project.go @@ -103,12 +103,10 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ // Sketches don't have subprojects return nil case projecttype.Library: - subprojectPath := superproject.Path.Join("examples") - immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Sketch, true)...) - // Apparently there is some level of official support for "example" in addition to the specification-compliant "examples". - // see: https://github.com/arduino/arduino-cli/blob/0.13.0/arduino/libraries/loader.go#L153 - subprojectPath = superproject.Path.Join("example") - immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Sketch, true)...) + for _, subprojectFolderName := range library.ExamplesFolderSupportedNames() { + subprojectPath := superproject.Path.Join(subprojectFolderName) + immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Sketch, true)...) + } case projecttype.Platform: subprojectPath := superproject.Path.Join("libraries") immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Library, false)...) From 598ae7f5826548453ea0b7d81dcfbc093962849c Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 22 Sep 2020 00:10:27 -0700 Subject: [PATCH 35/54] Define valid platform bundled libraries folder names in the platform package --- project/platform/platform.go | 14 ++++++++++++++ project/project.go | 6 ++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/project/platform/platform.go b/project/platform/platform.go index 47c949479..7052793a9 100644 --- a/project/platform/platform.go +++ b/project/platform/platform.go @@ -40,3 +40,17 @@ func IsRequiredConfigurationFile(filePath *paths.Path) bool { } return false } + +// See: https://arduino.github.io/arduino-cli/latest/platform-specification/#platform-bundled-libraries +var bundledLibrariesFolderNames = map[string]struct{}{ + "libraries": empty, +} + +func BundledLibrariesFolderNames() []string { + folderNames := make([]string, 0, len(bundledLibrariesFolderNames)) + for folderName := range bundledLibrariesFolderNames { + folderNames = append(folderNames, folderName) + } + + return folderNames +} diff --git a/project/project.go b/project/project.go index e0a9ccf4f..d45ba1677 100644 --- a/project/project.go +++ b/project/project.go @@ -108,8 +108,10 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Sketch, true)...) } case projecttype.Platform: - subprojectPath := superproject.Path.Join("libraries") - immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Library, false)...) + for _, subprojectFolderName := range platform.BundledLibrariesFolderNames() { + subprojectPath := superproject.Path.Join(subprojectFolderName) + immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Library, false)...) + } case projecttype.PackageIndex: // Platform indexes don't have subprojects return nil From e5133ae494c37b6dc30a52efe856f4552eb9cd96 Mon Sep 17 00:00:00 2001 From: rsora Date: Tue, 22 Sep 2020 11:14:29 +0200 Subject: [PATCH 36/54] Add go.mod files --- go.mod | 11 +++ go.sum | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..6d7d5cfd8 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/arduino/arduino-check + +go 1.14 + +require ( + github.com/arduino/arduino-cli v0.0.0-20200922073731-53e3230c4f71 + github.com/arduino/go-paths-helper v1.3.2 + github.com/arduino/go-properties-orderedmap v1.4.0 + github.com/sirupsen/logrus v1.6.0 + github.com/xeipuuv/gojsonschema v1.2.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..5d878690f --- /dev/null +++ b/go.sum @@ -0,0 +1,279 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/arduino/arduino-cli v0.0.0-20200922073731-53e3230c4f71 h1:wqYJLwv4C+lpGbtqNgGVy5MNKQx8ET+/kDukD2nE9hs= +github.com/arduino/arduino-cli v0.0.0-20200922073731-53e3230c4f71/go.mod h1:FoP2RfB4sX+XvDiLKysORud9lv8HtgYq14sNawOnx7o= +github.com/arduino/board-discovery v0.0.0-20180823133458-1ba29327fb0c/go.mod h1:HK7SpkEax/3P+0w78iRQx1sz1vCDYYw9RXwHjQTB5i8= +github.com/arduino/go-paths-helper v1.0.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= +github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= +github.com/arduino/go-paths-helper v1.3.2 h1:5U9TSKQODiwSVgTxskC0PNl0l0Vf40GUlp99Zy2SK8w= +github.com/arduino/go-paths-helper v1.3.2/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= +github.com/arduino/go-properties-orderedmap v1.3.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk= +github.com/arduino/go-properties-orderedmap v1.4.0 h1:YEbbzPqm1gXWDM/Jaq8tlvmh09z2qeHPJTUw9/VA4Dk= +github.com/arduino/go-properties-orderedmap v1.4.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk= +github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b/go.mod h1:uwGy5PpN4lqW97FiLnbcx+xx8jly5YuPMJWfVwwjJiQ= +github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b/go.mod h1:iIPnclBMYm1g32Q5kXoqng4jLhMStReIP7ZxaoUC2y8= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cmaglie/go.rice v1.0.3/go.mod h1:AF3bOWkvdOpp8/S3UL8qbQ4N7DiISIbJtj54GWFPAsc= +github.com/cmaglie/pb v1.0.27/go.mod h1:GilkKZMXYjBA4NxItWFfO+lwkp59PLHQ+IOW/b/kmZI= +github.com/codeclysm/cc v1.2.2/go.mod h1:XtW4ArCNgQwFphcRGG9+sPX5WM1J6/u0gMy5ZdV3obA= +github.com/codeclysm/extract/v3 v3.0.1/go.mod h1:NKsw+hqua9H+Rlwy/w/3Qgt9jDonYEgB6wJu+25eOKw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= +github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fluxio/iohelpers v0.0.0-20160419043813-3a4dd67a94d2/go.mod h1:c7sGIpDbBo0JZZ1tKyC1p5smWf8QcUjK4bFtZjHAecg= +github.com/fluxio/multierror v0.0.0-20160419044231-9c68d39025e5/go.mod h1:BEUDl7FG1cc76sM0J0x8dqr6RhiL4uqvk6oFkwuNyuM= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/h2non/filetype v1.0.6/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU= +github.com/h2non/filetype v1.0.8/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/imjasonmiller/godice v0.1.2/go.mod h1:8cTkdnVI+NglU2d6sv+ilYcNaJ5VSTBwvMbFULJd/QQ= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/clock v0.0.0-20180524022203-d293bb356ca4/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= +github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/retry v0.0.0-20160928201858-1998d01ba1c3/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4= +github.com/juju/testing v0.0.0-20200510222523-6c8c298c77a0/go.mod h1:hpGvhGHPVbNBraRLZEhoQwFLMrjK8PSlO4D3nDjKYXo= +github.com/juju/utils v0.0.0-20180808125547-9dfc6dbfb02b/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk= +github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leonelquinteros/gotext v1.4.0/go.mod h1:yZGXREmoGTtBvZHNcc+Yfug49G/2spuF/i/Qlsvz1Us= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/marcinbor85/gohex v0.0.0-20200531163658-baab2527a9a2/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mdlayher/genetlink v0.0.0-20190313224034-60417448a851/go.mod h1:EsbsAEUEs15qC1cosAwxgCWV0Qhd8TmkxnA9Kw1Vhl4= +github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= +github.com/mdlayher/taskstats v0.0.0-20190313225729-7cbba52ee072/go.mod h1:sGdS7A6CAETR53zkdjGkgoFlh1vSm7MtX+i8XfEsTMA= +github.com/miekg/dns v1.0.5/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228/go.mod h1:MGuVJ1+5TX1SCoO2Sx0eAnjpdRytYla2uC1YIZfkC9c= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583/go.mod h1:sFPiU/UgDcsQVu3vkqpZLCXWFwUoQRpHGu9ATihPAl0= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e/go.mod h1:tm/wZFQ8e24NYaBGIlnO2WGCAi67re4HHuOm0sftE/M= +github.com/segmentio/objconv v1.0.1/go.mod h1:auayaH5k3137Cl4SoXTgrzQcuQDmvuVtZgS0fb1Ahys= +github.com/segmentio/stats/v4 v4.5.3/go.mod h1:LsaahUJR7iiSs8mnkvQvdQ/RLHAS5adGLxuntg0ydGo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.0.1-0.20200710201246-675ae5f5a98c/go.mod h1:aeNIJzz/GSSVlS+gpCpQWZ83BKbsoW57mr90+YthtkQ= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.bug.st/cleanup v1.0.0/go.mod h1:EqVmTg2IBk4znLbPD28xne3abjsJftMdqqJEjhn70bk= +go.bug.st/downloader/v2 v2.0.1/go.mod h1:VZW2V1iGKV8rJL2ZEGIDzzBeKowYv34AedJz13RzVII= +go.bug.st/relaxed-semver v0.0.0-20190922224835-391e10178d18/go.mod h1:Cx1VqMtEhE9pIkEyUj3LVVVPkv89dgW8aCKrRPDR/uE= +go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q= +go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45/go.mod h1:dRSl/CVCTf56CkXgJMDOdSwNfo2g1orOGE/gBGdvjZw= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 8af60f36bf0f01bdd12da6ef39795c1be9356008 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 22 Sep 2020 15:35:29 -0700 Subject: [PATCH 37/54] Only print skipped check information to log This information is not normally of interest. --- check/check.go | 4 ++-- go.sum | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/check/check.go b/check/check.go index 2ae9034f1..72891c56e 100644 --- a/check/check.go +++ b/check/check.go @@ -15,6 +15,7 @@ import ( "github.com/arduino/arduino-check/configuration/checkmode" "github.com/arduino/arduino-check/project" "github.com/arduino/arduino-check/result/feedback" + "github.com/sirupsen/logrus" ) // RunChecks runs all checks for the given project and outputs the results. @@ -31,8 +32,7 @@ func RunChecks(project project.Type) { } if !runCheck { - // TODO: this should only be printed to log and in verbose mode - fmt.Printf("Skipping check: %s\n", checkConfiguration.ID) + logrus.Infof("Skipping check: %s\n", checkConfiguration.ID) continue } diff --git a/go.sum b/go.sum index 5d878690f..036d3d4ee 100644 --- a/go.sum +++ b/go.sum @@ -99,6 +99,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= From a93a6d06496ca5198a103676ab8f216c7688d1b7 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 22 Sep 2020 15:37:36 -0700 Subject: [PATCH 38/54] Add support for "json" output format --- check/check.go | 45 +++---- configuration/configuration.go | 12 ++ configuration/defaults.go | 1 + main.go | 20 +++ result/result.go | 231 +++++++++++++++++++++++++++++++++ 5 files changed, 281 insertions(+), 28 deletions(-) create mode 100644 result/result.go diff --git a/check/check.go b/check/check.go index 72891c56e..de73afaa7 100644 --- a/check/check.go +++ b/check/check.go @@ -2,18 +2,15 @@ package check import ( - "bytes" "fmt" "os" - "text/template" "github.com/arduino/arduino-check/check/checkconfigurations" "github.com/arduino/arduino-check/check/checkdata" - "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" "github.com/arduino/arduino-check/result/feedback" "github.com/sirupsen/logrus" ) @@ -36,20 +33,23 @@ func RunChecks(project project.Type) { continue } - fmt.Printf("Running check %s: ", checkConfiguration.ID) - result, output := checkConfiguration.CheckFunction() - fmt.Printf("%s\n", result.String()) - if result == checkresult.NotRun { - // TODO: make the check functions output an explanation for why they didn't run - fmt.Printf("%s: %s\n", checklevel.Notice, output) - } else if result != checkresult.Pass { - checkLevel, err := checklevel.CheckLevel(checkConfiguration) - if err != nil { - feedback.Errorf("Error while determining check level: %v", err) - os.Exit(1) - } - fmt.Printf("%s: %s\n", checkLevel.String(), message(checkConfiguration.MessageTemplate, output)) + // Output will be printed after all checks are finished when configured for "json" output format + if configuration.OutputFormat() == "text" { + fmt.Printf("Running check %s: ", checkConfiguration.ID) } + checkResult, checkOutput := checkConfiguration.CheckFunction() + reportText := result.Record(project, checkConfiguration, checkResult, checkOutput) + if configuration.OutputFormat() == "text" { + fmt.Print(reportText) + } + } + + // Checks are finished for this project, so summarize its check results in the report. + result.AddProjectSummaryReport(project) + + if configuration.OutputFormat() == "text" { + // Print the project check results summary. + fmt.Print(result.ProjectSummaryText(project)) } } @@ -88,14 +88,3 @@ func shouldRun(checkConfiguration checkconfigurations.Type, currentProject proje return false, fmt.Errorf("Check %s is incorrectly configured", checkConfiguration.ID) } - -// 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() -} diff --git a/configuration/configuration.go b/configuration/configuration.go index 95d29c5e2..1168e5fdf 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -15,6 +15,8 @@ func Initialize() { // TODO configuration according to command line input // TODO validate target path value, exit if not found // TODO support multiple paths + // TODO validate output format input + targetPath = paths.New("e:/electronics/arduino/libraries/arduino-check-test-library") // customCheckModes[checkmode.Permissive] = false @@ -22,6 +24,9 @@ func Initialize() { // customCheckModes[checkmode.LibraryManagerIndexed] = false // customCheckModes[checkmode.Official] = false // superprojectType = projecttype.All + + outputFormat = "json" + logrus.SetLevel(logrus.PanicLevel) logrus.WithFields(logrus.Fields{ @@ -52,6 +57,13 @@ func Recursive() bool { return recursive } +var outputFormat string + +// OutputFormat returns the tool output format configuration value. +func OutputFormat() string { + return outputFormat +} + var targetPath *paths.Path // TargetPath returns the projects search path. diff --git a/configuration/defaults.go b/configuration/defaults.go index d9b83d86f..88429ae5e 100644 --- a/configuration/defaults.go +++ b/configuration/defaults.go @@ -10,6 +10,7 @@ import ( func setDefaults() { superprojectTypeFilter = projecttype.All recursive = true + outputFormat = "text" // TODO: targetPath defaults to current path } diff --git a/main.go b/main.go index 0d6be2ec0..cd36b0ca3 100644 --- a/main.go +++ b/main.go @@ -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 } diff --git a/result/result.go b/result/result.go new file mode 100644 index 000000000..aaea82870 --- /dev/null +++ b/result/result.go @@ -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 { + 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() +} From 528aa98f4049393abf273ce32ec141d3f57e42fd Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 22 Sep 2020 15:48:26 -0700 Subject: [PATCH 39/54] Set exit status according to check results --- main.go | 4 +++- result/result.go | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index cd36b0ca3..68a2b216b 100644 --- a/main.go +++ b/main.go @@ -39,5 +39,7 @@ func main() { fmt.Println(result.JSONReport()) } - // TODO: set exit status according to check results + if !result.Passed() { + os.Exit(1) + } } diff --git a/result/result.go b/result/result.go index aaea82870..bb74af8a7 100644 --- a/result/result.go +++ b/result/result.go @@ -206,6 +206,11 @@ func jsonReportRaw() []byte { return reportJSON } +// Passed returns whether the checks passed cumulatively. +func Passed() bool { + return report.Summary.Pass +} + func getProjectReportIndex(projectPath *paths.Path) (bool, int) { var index int var projectReport projectReportType From 2c29553677ab749a79483d9a53bfd62919576b76 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 22 Sep 2020 16:16:02 -0700 Subject: [PATCH 40/54] Add support for report file option When a report file path is specified by the user, a JSON formatted report of the checks will be written to that path. --- configuration/configuration.go | 8 ++++++++ main.go | 5 +++++ result/result.go | 10 ++++++++++ 3 files changed, 23 insertions(+) diff --git a/configuration/configuration.go b/configuration/configuration.go index 1168e5fdf..81e6862a8 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -26,6 +26,7 @@ func Initialize() { // superprojectType = projecttype.All outputFormat = "json" + //reportFilePath = paths.New("report.json") logrus.SetLevel(logrus.PanicLevel) @@ -64,6 +65,13 @@ func OutputFormat() string { return outputFormat } +var reportFilePath *paths.Path + +// ReportFilePath returns the path to save the report file at. +func ReportFilePath() *paths.Path { + return reportFilePath +} + var targetPath *paths.Path // TargetPath returns the projects search path. diff --git a/main.go b/main.go index 68a2b216b..eae799fbb 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,11 @@ func main() { fmt.Println(result.JSONReport()) } + if configuration.ReportFilePath() != nil { + // Write report file. + result.WriteReport() + } + if !result.Passed() { os.Exit(1) } diff --git a/result/result.go b/result/result.go index bb74af8a7..27d298b72 100644 --- a/result/result.go +++ b/result/result.go @@ -206,6 +206,16 @@ func jsonReportRaw() []byte { return reportJSON } +// WriteReport writes a report for all projects to the specified file. +func WriteReport() { + // Write report file + err := configuration.ReportFilePath().WriteFile(jsonReportRaw()) + if err != nil { + feedback.Errorf("Error while writing report: %v", err) + os.Exit(1) + } +} + // Passed returns whether the checks passed cumulatively. func Passed() bool { return report.Summary.Pass From 9bde28348966b15433eb4a6392552306ddaa978d Mon Sep 17 00:00:00 2001 From: per1234 Date: Wed, 28 Oct 2020 21:27:35 -0700 Subject: [PATCH 41/54] Return boolean variable values directly from functions Since the variables already contain the value that needs to be returned, the if statement only adds unnecessary verbosity. --- project/library/library.go | 5 +---- project/packageindex/packageindex.go | 5 +---- project/platform/platform.go | 10 ++-------- project/project.go | 29 +++++----------------------- project/sketch/sketch.go | 5 +---- 5 files changed, 10 insertions(+), 44 deletions(-) diff --git a/project/library/library.go b/project/library/library.go index 687777748..e6d4bf75a 100644 --- a/project/library/library.go +++ b/project/library/library.go @@ -17,10 +17,7 @@ var headerFileValidExtensions = map[string]struct{}{ // HasHeaderFileValidExtension returns whether the file at the given path has a valid library header file extension. func HasHeaderFileValidExtension(filePath *paths.Path) bool { _, hasHeaderFileValidExtension := headerFileValidExtensions[filePath.Ext()] - if hasHeaderFileValidExtension { - return true - } - return false + return hasHeaderFileValidExtension } // See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-metadata diff --git a/project/packageindex/packageindex.go b/project/packageindex/packageindex.go index 894ffbda6..d2005b90b 100644 --- a/project/packageindex/packageindex.go +++ b/project/packageindex/packageindex.go @@ -20,10 +20,7 @@ var validExtensions = map[string]struct{}{ // HasValidExtension returns whether the file at the given path has a valid package index extension. func HasValidExtension(filePath *paths.Path) bool { _, hasValidExtension := validExtensions[filePath.Ext()] - if hasValidExtension { - return true - } - return false + return hasValidExtension } // Regular expressions for official and non-official package index filenames diff --git a/project/platform/platform.go b/project/platform/platform.go index 7052793a9..a6534ad9f 100644 --- a/project/platform/platform.go +++ b/project/platform/platform.go @@ -21,10 +21,7 @@ var configurationFilenames = map[string]struct{}{ // IsConfigurationFile returns whether the file at the given path has a boards platform configuration file filename. func IsConfigurationFile(filePath *paths.Path) bool { _, isConfigurationFile := configurationFilenames[filePath.Base()] - if isConfigurationFile { - return true - } - return false + return isConfigurationFile } var requiredConfigurationFilenames = map[string]struct{}{ @@ -35,10 +32,7 @@ var requiredConfigurationFilenames = map[string]struct{}{ // IsRequiredConfigurationFile returns whether the file at the given path has the filename of a required boards platform configuration file. func IsRequiredConfigurationFile(filePath *paths.Path) bool { _, isRequiredConfigurationFile := requiredConfigurationFilenames[filePath.Base()] - if isRequiredConfigurationFile { - return true - } - return false + return isRequiredConfigurationFile } // See: https://arduino.github.io/arduino-cli/latest/platform-specification/#platform-bundled-libraries diff --git a/project/project.go b/project/project.go index d45ba1677..17df7c3f7 100644 --- a/project/project.go +++ b/project/project.go @@ -185,10 +185,7 @@ func isSketch(potentialProjectPath *paths.Path) bool { } func isSketchIndicatorFile(filePath *paths.Path) bool { - if sketch.HasMainFileValidExtension(filePath) { - return true - } - return false + return sketch.HasMainFileValidExtension(filePath) } // isLibrary determines if a path is an Arduino library. @@ -236,19 +233,11 @@ func isPlatform(potentialProjectPath *paths.Path) bool { } func isPlatformIndicatorFile(filePath *paths.Path) bool { - if platform.IsConfigurationFile(filePath) { - return true - } - - return false + return platform.IsConfigurationFile(filePath) } func isStrictPlatformIndicatorFile(filePath *paths.Path) bool { - if platform.IsRequiredConfigurationFile(filePath) { - return true - } - - return false + return platform.IsRequiredConfigurationFile(filePath) } // isPackageIndex determines if a path contains an Arduino package index. @@ -266,17 +255,9 @@ func isPackageIndex(potentialProjectPath *paths.Path) bool { } func isPackageIndexIndicatorFile(filePath *paths.Path) bool { - if packageindex.HasValidExtension(filePath) { - return true - } - - return false + return packageindex.HasValidExtension(filePath) } func isStrictPackageIndexIndicatorFile(filePath *paths.Path) bool { - if packageindex.HasValidFilename(filePath, true) { - return true - } - - return false + return packageindex.HasValidFilename(filePath, true) } diff --git a/project/sketch/sketch.go b/project/sketch/sketch.go index ef4209785..af05a809f 100644 --- a/project/sketch/sketch.go +++ b/project/sketch/sketch.go @@ -13,8 +13,5 @@ import ( // Sketches may contain source files with other extensions (e.g., .h, .cpp), but they are required to have at least one file with a main extension. func HasMainFileValidExtension(filePath *paths.Path) bool { _, hasMainFileValidExtension := globals.MainFileValidExtensions[filePath.Ext()] - if hasMainFileValidExtension { - return true - } - return false + return hasMainFileValidExtension } From b3457a2817aaf05eb56288e69b98ee2d6df2a205 Mon Sep 17 00:00:00 2001 From: per1234 Date: Wed, 28 Oct 2020 21:32:18 -0700 Subject: [PATCH 42/54] Reduce verbosity of Boolean comparisons --- check/check.go | 4 ++-- check/checklevel/checklevel.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/check/check.go b/check/check.go index de73afaa7..d159b8c22 100644 --- a/check/check.go +++ b/check/check.go @@ -62,13 +62,13 @@ func shouldRun(checkConfiguration checkconfigurations.Type, currentProject proje } for _, disableMode := range checkConfiguration.DisableModes { - if configurationCheckModes[disableMode] == true { + if configurationCheckModes[disableMode] { return false, nil } } for _, enableMode := range checkConfiguration.EnableModes { - if configurationCheckModes[enableMode] == true { + if configurationCheckModes[enableMode] { return true, nil } } diff --git a/check/checklevel/checklevel.go b/check/checklevel/checklevel.go index a2347a8a2..8510d13fa 100644 --- a/check/checklevel/checklevel.go +++ b/check/checklevel/checklevel.go @@ -25,19 +25,19 @@ const ( func CheckLevel(checkConfiguration checkconfigurations.Type) (Type, error) { configurationCheckModes := configuration.CheckModes(checkConfiguration.ProjectType) for _, errorMode := range checkConfiguration.ErrorModes { - if configurationCheckModes[errorMode] == true { + if configurationCheckModes[errorMode] { return Error, nil } } for _, warningMode := range checkConfiguration.WarningModes { - if configurationCheckModes[warningMode] == true { + if configurationCheckModes[warningMode] { return Warning, nil } } for _, infoMode := range checkConfiguration.InfoModes { - if configurationCheckModes[infoMode] == true { + if configurationCheckModes[infoMode] { return Info, nil } } From b40200e2cdc413a548d9d4a75b7f08bf041d230c Mon Sep 17 00:00:00 2001 From: per1234 Date: Wed, 28 Oct 2020 22:43:17 -0700 Subject: [PATCH 43/54] Remove unncessary use of String() method --- check/check.go | 2 +- configuration/checkmode/checkmode.go | 2 +- configuration/configuration.go | 4 ++-- project/project.go | 28 ++++++++++++++-------------- result/result.go | 6 +++--- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/check/check.go b/check/check.go index d159b8c22..6a2a22963 100644 --- a/check/check.go +++ b/check/check.go @@ -17,7 +17,7 @@ import ( // RunChecks runs all checks for the given project and outputs the results. func RunChecks(project project.Type) { - fmt.Printf("Checking %s in %s\n", project.ProjectType.String(), project.Path.String()) + fmt.Printf("Checking %s in %s\n", project.ProjectType, project.Path) checkdata.Initialize(project) diff --git a/configuration/checkmode/checkmode.go b/configuration/checkmode/checkmode.go index e6478944d..120384b20 100644 --- a/configuration/checkmode/checkmode.go +++ b/configuration/checkmode/checkmode.go @@ -30,7 +30,7 @@ func Modes(defaultCheckModes map[projecttype.Type]map[Type]bool, customCheckMode } else { checkModes[key] = defaultValue } - logrus.Tracef("Check mode option %s set to %t\n", key.String(), checkModes[key]) + logrus.Tracef("Check mode option %s set to %t\n", key, checkModes[key]) } // This mode is always enabled diff --git a/configuration/configuration.go b/configuration/configuration.go index 81e6862a8..c7dd4cd4c 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -31,9 +31,9 @@ func Initialize() { logrus.SetLevel(logrus.PanicLevel) logrus.WithFields(logrus.Fields{ - "superproject type filter": SuperprojectTypeFilter().String(), + "superproject type filter": SuperprojectTypeFilter(), "recursive": Recursive(), - "projects path": TargetPath().String(), + "projects path": TargetPath(), }).Debug("Configuration initialized") } diff --git a/project/project.go b/project/project.go index 17df7c3f7..947b406a4 100644 --- a/project/project.go +++ b/project/project.go @@ -48,13 +48,13 @@ func FindProjects() ([]Type, error) { return foundProjects, nil } - return nil, fmt.Errorf("specified path %s is not an Arduino project", targetPath.String()) + return nil, fmt.Errorf("specified path %s is not an Arduino project", targetPath) } foundProjects = append(foundProjects, findProjectsUnderPath(targetPath, superprojectTypeFilter, recursive)...) if foundProjects == nil { - return nil, fmt.Errorf("no projects found under %s", targetPath.String()) + return nil, fmt.Errorf("no projects found under %s", targetPath) } return foundProjects, nil @@ -66,7 +66,7 @@ func findProjectsUnderPath(targetPath *paths.Path, projectType projecttype.Type, isProject, foundProjectType := isProject(targetPath, projectType) if isProject { - logrus.Tracef("%s is %s", targetPath.String(), projectType.String()) + logrus.Tracef("%s is %s", targetPath, projectType) foundProject := Type{ Path: targetPath, ProjectType: foundProjectType, @@ -132,18 +132,18 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ // isProject determines if a path contains an Arduino project, and if so which type. func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { - logrus.Tracef("Checking if %s is %s", potentialProjectPath.String(), projectType.String()) + logrus.Tracef("Checking if %s is %s", potentialProjectPath, projectType) if (projectType == projecttype.All || projectType == projecttype.Sketch) && isSketch(potentialProjectPath) { - logrus.Tracef("%s is %s", potentialProjectPath.String(), projecttype.Sketch.String()) + logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Sketch) return true, projecttype.Sketch } else if (projectType == projecttype.All || projectType == projecttype.Library) && isLibrary(potentialProjectPath) { - logrus.Tracef("%s is %s", potentialProjectPath.String(), projecttype.Library.String()) + logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Library) return true, projecttype.Library } else if (projectType == projecttype.All || projectType == projecttype.Platform) && isPlatform(potentialProjectPath) { - logrus.Tracef("%s is %s", potentialProjectPath.String(), projecttype.Platform.String()) + logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Platform) return true, projecttype.Platform } else if (projectType == projecttype.All || projectType == projecttype.PackageIndex) && isPackageIndex(potentialProjectPath) { - logrus.Tracef("%s is %s", potentialProjectPath.String(), projecttype.PackageIndex.String()) + logrus.Tracef("%s is %s", potentialProjectPath, projecttype.PackageIndex) return true, projecttype.PackageIndex } return false, projecttype.Not @@ -151,21 +151,21 @@ func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) ( // isProject determines if a file is the indicator file for an Arduino project, and if so which type. func isProjectIndicatorFile(potentialProjectFilePath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { - logrus.Tracef("Checking if %s is %s indicator file", potentialProjectFilePath.String(), projectType.String()) + logrus.Tracef("Checking if %s is %s indicator file", potentialProjectFilePath, projectType) if (projectType == projecttype.All || projectType == projecttype.Sketch) && isSketchIndicatorFile(potentialProjectFilePath) { - logrus.Tracef("%s is %s indicator file", potentialProjectFilePath.String(), projecttype.Sketch.String()) + logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Sketch) return true, projecttype.Sketch } else if (projectType == projecttype.All || projectType == projecttype.Library) && isLibraryIndicatorFile(potentialProjectFilePath) { - logrus.Tracef("%s is %s indicator file", potentialProjectFilePath.String(), projecttype.Library.String()) + logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Library) return true, projecttype.Library } else if (projectType == projecttype.All || projectType == projecttype.Platform) && isPlatformIndicatorFile(potentialProjectFilePath) { - logrus.Tracef("%s is %s indicator file", potentialProjectFilePath.String(), projecttype.Platform.String()) + logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Platform) return true, projecttype.Platform } else if (projectType == projecttype.All || projectType == projecttype.PackageIndex) && isPackageIndexIndicatorFile(potentialProjectFilePath) { - logrus.Tracef("%s is %s indicator file", potentialProjectFilePath.String(), projecttype.PackageIndex.String()) + logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.PackageIndex) return true, projecttype.PackageIndex } - logrus.Tracef("%s is not indicator file", potentialProjectFilePath.String()) + logrus.Tracef("%s is not indicator file", potentialProjectFilePath) return false, projecttype.Not } diff --git a/result/result.go b/result/result.go index 27d298b72..8a7e59818 100644 --- a/result/result.go +++ b/result/result.go @@ -83,13 +83,13 @@ func Record(checkedProject project.Type, checkConfiguration checkconfigurations. os.Exit(1) } - summaryText := fmt.Sprintf("%v\n", checkResult.String()) + summaryText := fmt.Sprintf("%s\n", checkResult) 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) + summaryText += fmt.Sprintf("%s: %s\n", checklevel.Notice, checkOutput) } else if checkResult != checkresult.Pass { - summaryText += fmt.Sprintf("%s: %s\n", checkLevel.String(), checkMessage) + summaryText += fmt.Sprintf("%s: %s\n", checkLevel, checkMessage) } checkReport := checkReportType{ From a842f069f920ea0aca77be3817478bb9b1f69df2 Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 00:45:07 -0700 Subject: [PATCH 44/54] Eliminate duplicate code in subproject discovery --- project/project.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/project/project.go b/project/project.go index 947b406a4..242d7540f 100644 --- a/project/project.go +++ b/project/project.go @@ -96,27 +96,35 @@ func findProjectsUnderPath(targetPath *paths.Path, projectType projecttype.Type, // findSubprojects finds subprojects of the given project. // For example, the subprojects of a library are its example sketches. func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) []Type { - var immediateSubprojects []Type + subprojectFolderNames := []string{} + var subProjectType projecttype.Type + var searchPathsRecursively bool + // Determine possible subproject paths switch superproject.ProjectType { case projecttype.Sketch: // Sketches don't have subprojects return nil case projecttype.Library: - for _, subprojectFolderName := range library.ExamplesFolderSupportedNames() { - subprojectPath := superproject.Path.Join(subprojectFolderName) - immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Sketch, true)...) - } + subprojectFolderNames = append(subprojectFolderNames, library.ExamplesFolderSupportedNames()...) + subProjectType = projecttype.Sketch + searchPathsRecursively = true // Examples sketches can be under nested subfolders case projecttype.Platform: - for _, subprojectFolderName := range platform.BundledLibrariesFolderNames() { - subprojectPath := superproject.Path.Join(subprojectFolderName) - immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, projecttype.Library, false)...) - } + subprojectFolderNames = append(subprojectFolderNames, platform.BundledLibrariesFolderNames()...) + subProjectType = projecttype.Library + searchPathsRecursively = false // Bundled libraries must be in the root of the libraries folder case projecttype.PackageIndex: // Platform indexes don't have subprojects return nil } + // Search the subproject paths for projects + var immediateSubprojects []Type + for _, subprojectFolderName := range subprojectFolderNames { + subprojectPath := superproject.Path.Join(subprojectFolderName) + immediateSubprojects = append(immediateSubprojects, findProjectsUnderPath(subprojectPath, subProjectType, searchPathsRecursively)...) + } + var allSubprojects []Type // Subprojects may have their own subprojects. for _, immediateSubproject := range immediateSubprojects { From 2fe0250610565ea3fc4340a28f8d3e27942405cb Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 01:00:24 -0700 Subject: [PATCH 45/54] Panic if subproject discovery was not configured for project type This would indicate a new project type was added to the tool without updating the subproject discovery code accordingly. --- project/project.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/project/project.go b/project/project.go index 242d7540f..3efe807b9 100644 --- a/project/project.go +++ b/project/project.go @@ -116,6 +116,8 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ case projecttype.PackageIndex: // Platform indexes don't have subprojects return nil + default: + panic(fmt.Sprintf("Subproject discovery not configured for project type: %s", superproject.ProjectType)) } // Search the subproject paths for projects From 918d845ce1f1337f266496f4455657a7a06d0608 Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 01:15:52 -0700 Subject: [PATCH 46/54] Refactor .pde sketch check function's output code --- check/checkfunctions/sketch.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/check/checkfunctions/sketch.go b/check/checkfunctions/sketch.go index ef3436e23..c41689308 100644 --- a/check/checkfunctions/sketch.go +++ b/check/checkfunctions/sketch.go @@ -3,6 +3,8 @@ package checkfunctions // The check functions for sketches. import ( + "strings" + "github.com/arduino/arduino-check/check/checkdata" "github.com/arduino/arduino-check/check/checkresult" ) @@ -10,19 +12,15 @@ import ( func PdeSketchExtension() (result checkresult.Type, output string) { directoryListing, _ := checkdata.ProjectPath().ReadDir() directoryListing.FilterOutDirs() - pdeSketches := "" + pdeSketches := []string{} for _, filePath := range directoryListing { if filePath.Ext() == ".pde" { - if pdeSketches == "" { - pdeSketches = filePath.Base() - } else { - pdeSketches += ", " + filePath.Base() - } + pdeSketches = append(pdeSketches, filePath.Base()) } } - if pdeSketches != "" { - return checkresult.Fail, pdeSketches + if len(pdeSketches) > 0 { + return checkresult.Fail, strings.Join(pdeSketches, ", ") } return checkresult.Pass, "" From 2517a0bccbfe6c60a61a271fa5d10cd11ea59d78 Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 01:37:58 -0700 Subject: [PATCH 47/54] Add doc comments for check functions --- check/checkfunctions/library.go | 4 ++++ check/checkfunctions/sketch.go | 1 + 2 files changed, 5 insertions(+) diff --git a/check/checkfunctions/library.go b/check/checkfunctions/library.go index edeab008c..f3419b382 100644 --- a/check/checkfunctions/library.go +++ b/check/checkfunctions/library.go @@ -8,6 +8,7 @@ import ( "github.com/arduino/arduino-check/project/library/libraryproperties" ) +// LibraryPropertiesFormat checks for invalid library.properties format. func LibraryPropertiesFormat() (result checkresult.Type, output string) { if checkdata.LibraryPropertiesLoadError() != nil { return checkresult.Fail, checkdata.LibraryPropertiesLoadError().Error() @@ -15,6 +16,7 @@ func LibraryPropertiesFormat() (result checkresult.Type, output string) { return checkresult.Pass, "" } +// LibraryPropertiesNameFieldMissing checks for missing library.properties "name" field. func LibraryPropertiesNameFieldMissing() (result checkresult.Type, output string) { if checkdata.LibraryPropertiesLoadError() != nil { return checkresult.NotRun, "" @@ -26,6 +28,7 @@ func LibraryPropertiesNameFieldMissing() (result checkresult.Type, output string return checkresult.Pass, "" } +// LibraryPropertiesNameFieldDisallowedCharacters checks for disallowed characters in the library.properties "name" field. func LibraryPropertiesNameFieldDisallowedCharacters() (result checkresult.Type, output string) { if checkdata.LibraryPropertiesLoadError() != nil { return checkresult.NotRun, "" @@ -38,6 +41,7 @@ func LibraryPropertiesNameFieldDisallowedCharacters() (result checkresult.Type, return checkresult.Pass, "" } +// LibraryPropertiesVersionFieldMissing checks for missing library.properties "version" field. func LibraryPropertiesVersionFieldMissing() (result checkresult.Type, output string) { if checkdata.LibraryPropertiesLoadError() != nil { return checkresult.NotRun, "" diff --git a/check/checkfunctions/sketch.go b/check/checkfunctions/sketch.go index c41689308..741becc43 100644 --- a/check/checkfunctions/sketch.go +++ b/check/checkfunctions/sketch.go @@ -9,6 +9,7 @@ import ( "github.com/arduino/arduino-check/check/checkresult" ) +// PdeSketchExtension checks for use of deprecated .pde sketch file extensions. func PdeSketchExtension() (result checkresult.Type, output string) { directoryListing, _ := checkdata.ProjectPath().ReadDir() directoryListing.FilterOutDirs() From 9ddc1bb41921e685732eeb34dceaaa41a91795bf Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 03:14:39 -0700 Subject: [PATCH 48/54] Add project type matcher method --- project/project.go | 16 ++++++++-------- project/projecttype/projecttype.go | 10 ++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/project/project.go b/project/project.go index 3efe807b9..e99659942 100644 --- a/project/project.go +++ b/project/project.go @@ -143,16 +143,16 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ // isProject determines if a path contains an Arduino project, and if so which type. func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { logrus.Tracef("Checking if %s is %s", potentialProjectPath, projectType) - if (projectType == projecttype.All || projectType == projecttype.Sketch) && isSketch(potentialProjectPath) { + if projectType.Matches(projecttype.Sketch) && isSketch(potentialProjectPath) { logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Sketch) return true, projecttype.Sketch - } else if (projectType == projecttype.All || projectType == projecttype.Library) && isLibrary(potentialProjectPath) { + } else if projectType.Matches(projecttype.Library) && isLibrary(potentialProjectPath) { logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Library) return true, projecttype.Library - } else if (projectType == projecttype.All || projectType == projecttype.Platform) && isPlatform(potentialProjectPath) { + } else if projectType.Matches(projecttype.Platform) && isPlatform(potentialProjectPath) { logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Platform) return true, projecttype.Platform - } else if (projectType == projecttype.All || projectType == projecttype.PackageIndex) && isPackageIndex(potentialProjectPath) { + } else if projectType.Matches(projecttype.PackageIndex) && isPackageIndex(potentialProjectPath) { logrus.Tracef("%s is %s", potentialProjectPath, projecttype.PackageIndex) return true, projecttype.PackageIndex } @@ -162,16 +162,16 @@ func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) ( // isProject determines if a file is the indicator file for an Arduino project, and if so which type. func isProjectIndicatorFile(potentialProjectFilePath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { logrus.Tracef("Checking if %s is %s indicator file", potentialProjectFilePath, projectType) - if (projectType == projecttype.All || projectType == projecttype.Sketch) && isSketchIndicatorFile(potentialProjectFilePath) { + if projectType.Matches(projecttype.Sketch) && isSketchIndicatorFile(potentialProjectFilePath) { logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Sketch) return true, projecttype.Sketch - } else if (projectType == projecttype.All || projectType == projecttype.Library) && isLibraryIndicatorFile(potentialProjectFilePath) { + } else if projectType.Matches(projecttype.Library) && isLibraryIndicatorFile(potentialProjectFilePath) { logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Library) return true, projecttype.Library - } else if (projectType == projecttype.All || projectType == projecttype.Platform) && isPlatformIndicatorFile(potentialProjectFilePath) { + } else if projectType.Matches(projecttype.Platform) && isPlatformIndicatorFile(potentialProjectFilePath) { logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Platform) return true, projecttype.Platform - } else if (projectType == projecttype.All || projectType == projecttype.PackageIndex) && isPackageIndexIndicatorFile(potentialProjectFilePath) { + } else if projectType.Matches(projecttype.PackageIndex) && isPackageIndexIndicatorFile(potentialProjectFilePath) { logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.PackageIndex) return true, projecttype.PackageIndex } diff --git a/project/projecttype/projecttype.go b/project/projecttype/projecttype.go index a7e886cec..96f300358 100644 --- a/project/projecttype/projecttype.go +++ b/project/projecttype/projecttype.go @@ -13,3 +13,13 @@ const ( All // any project type Not // N/A ) + +// Matches returns whether the receiver project type matches the argument project type +func (projectTypeA Type) Matches(projectTypeB Type) bool { + if projectTypeA == Not && projectTypeB == Not { + return true + } else if projectTypeA == Not || projectTypeB == Not { + return false + } + return (projectTypeA == All || projectTypeB == All || projectTypeA == projectTypeB) +} From e692238dba7d04047f0a0c5dcc33b4e092923fdd Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 03:27:16 -0700 Subject: [PATCH 49/54] Use more apropriate project type in log message Previously, the more general project filter type was used, but the specific project type that was detected will be more useful information. --- project/project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/project.go b/project/project.go index e99659942..2e8ae3438 100644 --- a/project/project.go +++ b/project/project.go @@ -66,7 +66,7 @@ func findProjectsUnderPath(targetPath *paths.Path, projectType projecttype.Type, isProject, foundProjectType := isProject(targetPath, projectType) if isProject { - logrus.Tracef("%s is %s", targetPath, projectType) + logrus.Tracef("%s is %s", targetPath, foundProjectType) foundProject := Type{ Path: targetPath, ProjectType: foundProjectType, From 561d88e63a5eb22544d3634693890413f6af2d49 Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 03:29:11 -0700 Subject: [PATCH 50/54] Use more appropriate variable name for project type filter The term "project type" is used both to refer to the type of the project filter, which may be "all", and to the specific project type (e.g., sketch, library, platform). This can result in some ambiguity in the code. The use of "filter" in variable names that are used exclusively for project type filters may make the code easier to follow. --- project/project.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/project/project.go b/project/project.go index 2e8ae3438..0f0b21b6b 100644 --- a/project/project.go +++ b/project/project.go @@ -61,10 +61,10 @@ func FindProjects() ([]Type, error) { } // findProjectsUnderPath finds projects of the given type and subprojects of those projects. It returns a slice containing the definitions of all found projects. -func findProjectsUnderPath(targetPath *paths.Path, projectType projecttype.Type, recursive bool) []Type { +func findProjectsUnderPath(targetPath *paths.Path, projectTypeFilter projecttype.Type, recursive bool) []Type { var foundProjects []Type - isProject, foundProjectType := isProject(targetPath, projectType) + isProject, foundProjectType := isProject(targetPath, projectTypeFilter) if isProject { logrus.Tracef("%s is %s", targetPath, foundProjectType) foundProject := Type{ @@ -86,7 +86,7 @@ func findProjectsUnderPath(targetPath *paths.Path, projectType projecttype.Type, directoryListing, _ := targetPath.ReadDir() directoryListing.FilterDirs() for _, potentialProjectDirectory := range directoryListing { - foundProjects = append(foundProjects, findProjectsUnderPath(potentialProjectDirectory, projectType, recursive)...) + foundProjects = append(foundProjects, findProjectsUnderPath(potentialProjectDirectory, projectTypeFilter, recursive)...) } } @@ -141,18 +141,18 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ } // isProject determines if a path contains an Arduino project, and if so which type. -func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { - logrus.Tracef("Checking if %s is %s", potentialProjectPath, projectType) - if projectType.Matches(projecttype.Sketch) && isSketch(potentialProjectPath) { +func isProject(potentialProjectPath *paths.Path, projectTypeFilter projecttype.Type) (bool, projecttype.Type) { + logrus.Tracef("Checking if %s is %s", potentialProjectPath, projectTypeFilter) + if projectTypeFilter.Matches(projecttype.Sketch) && isSketch(potentialProjectPath) { logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Sketch) return true, projecttype.Sketch - } else if projectType.Matches(projecttype.Library) && isLibrary(potentialProjectPath) { + } else if projectTypeFilter.Matches(projecttype.Library) && isLibrary(potentialProjectPath) { logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Library) return true, projecttype.Library - } else if projectType.Matches(projecttype.Platform) && isPlatform(potentialProjectPath) { + } else if projectTypeFilter.Matches(projecttype.Platform) && isPlatform(potentialProjectPath) { logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Platform) return true, projecttype.Platform - } else if projectType.Matches(projecttype.PackageIndex) && isPackageIndex(potentialProjectPath) { + } else if projectTypeFilter.Matches(projecttype.PackageIndex) && isPackageIndex(potentialProjectPath) { logrus.Tracef("%s is %s", potentialProjectPath, projecttype.PackageIndex) return true, projecttype.PackageIndex } @@ -160,18 +160,18 @@ func isProject(potentialProjectPath *paths.Path, projectType projecttype.Type) ( } // isProject determines if a file is the indicator file for an Arduino project, and if so which type. -func isProjectIndicatorFile(potentialProjectFilePath *paths.Path, projectType projecttype.Type) (bool, projecttype.Type) { - logrus.Tracef("Checking if %s is %s indicator file", potentialProjectFilePath, projectType) - if projectType.Matches(projecttype.Sketch) && isSketchIndicatorFile(potentialProjectFilePath) { +func isProjectIndicatorFile(potentialProjectFilePath *paths.Path, projectTypeFilter projecttype.Type) (bool, projecttype.Type) { + logrus.Tracef("Checking if %s is %s indicator file", potentialProjectFilePath, projectTypeFilter) + if projectTypeFilter.Matches(projecttype.Sketch) && isSketchIndicatorFile(potentialProjectFilePath) { logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Sketch) return true, projecttype.Sketch - } else if projectType.Matches(projecttype.Library) && isLibraryIndicatorFile(potentialProjectFilePath) { + } else if projectTypeFilter.Matches(projecttype.Library) && isLibraryIndicatorFile(potentialProjectFilePath) { logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Library) return true, projecttype.Library - } else if projectType.Matches(projecttype.Platform) && isPlatformIndicatorFile(potentialProjectFilePath) { + } else if projectTypeFilter.Matches(projecttype.Platform) && isPlatformIndicatorFile(potentialProjectFilePath) { logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Platform) return true, projecttype.Platform - } else if projectType.Matches(projecttype.PackageIndex) && isPackageIndexIndicatorFile(potentialProjectFilePath) { + } else if projectTypeFilter.Matches(projecttype.PackageIndex) && isPackageIndexIndicatorFile(potentialProjectFilePath) { logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.PackageIndex) return true, projecttype.PackageIndex } From b9f951b75d4288806ac4adb5467645df1e9533f1 Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 03:44:55 -0700 Subject: [PATCH 51/54] Refactor project identification functions Consolidate the redundant logging code in these functions. --- project/project.go | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/project/project.go b/project/project.go index 0f0b21b6b..5c4a7a112 100644 --- a/project/project.go +++ b/project/project.go @@ -143,40 +143,46 @@ func findSubprojects(superproject Type, apexSuperprojectType projecttype.Type) [ // isProject determines if a path contains an Arduino project, and if so which type. func isProject(potentialProjectPath *paths.Path, projectTypeFilter projecttype.Type) (bool, projecttype.Type) { logrus.Tracef("Checking if %s is %s", potentialProjectPath, projectTypeFilter) + + projectType := projecttype.Not if projectTypeFilter.Matches(projecttype.Sketch) && isSketch(potentialProjectPath) { - logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Sketch) - return true, projecttype.Sketch + projectType = projecttype.Sketch } else if projectTypeFilter.Matches(projecttype.Library) && isLibrary(potentialProjectPath) { - logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Library) - return true, projecttype.Library + projectType = projecttype.Library } else if projectTypeFilter.Matches(projecttype.Platform) && isPlatform(potentialProjectPath) { - logrus.Tracef("%s is %s", potentialProjectPath, projecttype.Platform) - return true, projecttype.Platform + projectType = projecttype.Platform } else if projectTypeFilter.Matches(projecttype.PackageIndex) && isPackageIndex(potentialProjectPath) { - logrus.Tracef("%s is %s", potentialProjectPath, projecttype.PackageIndex) - return true, projecttype.PackageIndex + projectType = projecttype.PackageIndex + } + + if projectType == projecttype.Not { + return false, projectType } - return false, projecttype.Not + logrus.Tracef("%s is %s", potentialProjectPath, projectType) + return true, projectType } // isProject determines if a file is the indicator file for an Arduino project, and if so which type. func isProjectIndicatorFile(potentialProjectFilePath *paths.Path, projectTypeFilter projecttype.Type) (bool, projecttype.Type) { logrus.Tracef("Checking if %s is %s indicator file", potentialProjectFilePath, projectTypeFilter) + + projectType := projecttype.Not if projectTypeFilter.Matches(projecttype.Sketch) && isSketchIndicatorFile(potentialProjectFilePath) { - logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Sketch) - return true, projecttype.Sketch + projectType = projecttype.Sketch } else if projectTypeFilter.Matches(projecttype.Library) && isLibraryIndicatorFile(potentialProjectFilePath) { - logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Library) - return true, projecttype.Library + projectType = projecttype.Library } else if projectTypeFilter.Matches(projecttype.Platform) && isPlatformIndicatorFile(potentialProjectFilePath) { - logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.Platform) - return true, projecttype.Platform + projectType = projecttype.Platform } else if projectTypeFilter.Matches(projecttype.PackageIndex) && isPackageIndexIndicatorFile(potentialProjectFilePath) { - logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projecttype.PackageIndex) - return true, projecttype.PackageIndex + projectType = projecttype.PackageIndex + } + + if projectType == projecttype.Not { + logrus.Tracef("%s is not indicator file", potentialProjectFilePath) + return false, projectType } - logrus.Tracef("%s is not indicator file", potentialProjectFilePath) - return false, projecttype.Not + logrus.Tracef("%s is %s indicator file", potentialProjectFilePath, projectType) + return true, projectType } // isSketch determines whether a path is an Arduino sketch. From fbe79ee3a436da7d9037301be7685cf0db65896e Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 08:05:38 -0700 Subject: [PATCH 52/54] Support multiple report instances Previously, there was no provision for multiple report instances. Although the current tool design has no need for multiple instances, the code now has the versatility to allow for report demands that might arise in the future. --- check/check.go | 6 +++--- main.go | 12 ++++++------ result/result.go | 40 +++++++++++++++++++++------------------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/check/check.go b/check/check.go index 6a2a22963..985e3927c 100644 --- a/check/check.go +++ b/check/check.go @@ -38,18 +38,18 @@ func RunChecks(project project.Type) { fmt.Printf("Running check %s: ", checkConfiguration.ID) } checkResult, checkOutput := checkConfiguration.CheckFunction() - reportText := result.Record(project, checkConfiguration, checkResult, checkOutput) + reportText := result.Report.Record(project, checkConfiguration, checkResult, checkOutput) if configuration.OutputFormat() == "text" { fmt.Print(reportText) } } // Checks are finished for this project, so summarize its check results in the report. - result.AddProjectSummaryReport(project) + result.Report.AddProjectSummaryReport(project) if configuration.OutputFormat() == "text" { // Print the project check results summary. - fmt.Print(result.ProjectSummaryText(project)) + fmt.Print(result.Report.ProjectSummaryText(project)) } } diff --git a/main.go b/main.go index eae799fbb..64747f5dd 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( func main() { configuration.Initialize() // Must be called after configuration.Initialize() - result.Initialize() + result.Report.Initialize() projects, err := project.FindProjects() if err != nil { @@ -27,24 +27,24 @@ func main() { } // All projects have been checked, so summarize their check results in the report. - result.AddSummaryReport() + result.Report.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()) + fmt.Print(result.Report.SummaryText()) } } else { // Print the complete JSON formatted report. - fmt.Println(result.JSONReport()) + fmt.Println(result.Report.JSONReport()) } if configuration.ReportFilePath() != nil { // Write report file. - result.WriteReport() + result.Report.WriteReport() } - if !result.Passed() { + if !result.Report.Passed() { os.Exit(1) } } diff --git a/result/result.go b/result/result.go index 8a7e59818..7143a165a 100644 --- a/result/result.go +++ b/result/result.go @@ -18,7 +18,11 @@ import ( "github.com/arduino/go-paths-helper" ) -type reportType struct { +// Report is the global instance of the check results ReportType struct +var Report ReportType + +// ReportType is the type for the check results report +type ReportType struct { Configuration toolConfigurationReportType `json:"configuration"` Projects []projectReportType `json:"projects"` Summary summaryReportType `json:"summary"` @@ -62,10 +66,8 @@ type summaryReportType struct { ErrorCount int `json:"errorCount"` } -var report reportType - // Initialize adds the tool configuration data to the report. -func Initialize() { +func (report *ReportType) Initialize() { report.Configuration = toolConfigurationReportType{ Paths: []*paths.Path{configuration.TargetPath()}, ProjectType: configuration.SuperprojectTypeFilter().String(), @@ -74,7 +76,7 @@ func Initialize() { } // 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 { +func (report *ReportType) Record(checkedProject project.Type, checkConfiguration checkconfigurations.Type, checkResult checkresult.Type, checkOutput string) string { checkMessage := message(checkConfiguration.MessageTemplate, checkOutput) checkLevel, err := checklevel.CheckLevel(checkConfiguration) @@ -103,7 +105,7 @@ func Record(checkedProject project.Type, checkConfiguration checkconfigurations. Message: checkMessage, } - reportExists, projectReportIndex := getProjectReportIndex(checkedProject.Path) + reportExists, projectReportIndex := report.getProjectReportIndex(checkedProject.Path) if !reportExists { // There is no existing report for this project. report.Projects = append( @@ -129,8 +131,8 @@ func Record(checkedProject project.Type, checkConfiguration checkconfigurations. } // 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) +func (report *ReportType) AddProjectSummaryReport(checkedProject project.Type) { + reportExists, projectReportIndex := report.getProjectReportIndex(checkedProject.Path) if !reportExists { panic(fmt.Sprintf("Unable to find report for %v when generating report summary", checkedProject.Path)) } @@ -157,8 +159,8 @@ func AddProjectSummaryReport(checkedProject project.Type) { } // ProjectSummaryText returns a text summary of the check results for the given project. -func ProjectSummaryText(checkedProject project.Type) string { - reportExists, projectReportIndex := getProjectReportIndex(checkedProject.Path) +func (report ReportType) ProjectSummaryText(checkedProject project.Type) string { + reportExists, projectReportIndex := report.getProjectReportIndex(checkedProject.Path) if !reportExists { panic(fmt.Sprintf("Unable to find report for %v when generating report summary text", checkedProject.Path)) } @@ -168,7 +170,7 @@ func ProjectSummaryText(checkedProject project.Type) string { } // AddSummaryReport summarizes the check results for all projects and adds it to the report. -func AddSummaryReport() { +func (report *ReportType) AddSummaryReport() { pass := true warningCount := 0 errorCount := 0 @@ -188,16 +190,16 @@ func AddSummaryReport() { } // SummaryText returns a text summary of the cumulative check results. -func SummaryText() string { +func (report ReportType) 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 (report ReportType) JSONReport() string { + return string(report.jsonReportRaw()) } -func jsonReportRaw() []byte { +func (report ReportType) jsonReportRaw() []byte { reportJSON, err := json.MarshalIndent(report, "", " ") if err != nil { panic(fmt.Sprintf("Error while formatting checks report: %v", err)) @@ -207,9 +209,9 @@ func jsonReportRaw() []byte { } // WriteReport writes a report for all projects to the specified file. -func WriteReport() { +func (report ReportType) WriteReport() { // Write report file - err := configuration.ReportFilePath().WriteFile(jsonReportRaw()) + err := configuration.ReportFilePath().WriteFile(report.jsonReportRaw()) if err != nil { feedback.Errorf("Error while writing report: %v", err) os.Exit(1) @@ -217,11 +219,11 @@ func WriteReport() { } // Passed returns whether the checks passed cumulatively. -func Passed() bool { +func (report ReportType) Passed() bool { return report.Summary.Pass } -func getProjectReportIndex(projectPath *paths.Path) (bool, int) { +func (report ReportType) getProjectReportIndex(projectPath *paths.Path) (bool, int) { var index int var projectReport projectReportType for index, projectReport = range report.Projects { From 6894049f1ad60d4645e978fd1d17e4b117dfabfe Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 08:28:54 -0700 Subject: [PATCH 53/54] Refactor names in the result package The change in approach in the result package resulted in some of the use of the term "report" in that package becoming less intuitive, since reports are only one use of the results. --- check/check.go | 6 ++-- main.go | 12 ++++---- result/result.go | 74 ++++++++++++++++++++++++------------------------ 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/check/check.go b/check/check.go index 985e3927c..e8abcd0de 100644 --- a/check/check.go +++ b/check/check.go @@ -38,18 +38,18 @@ func RunChecks(project project.Type) { fmt.Printf("Running check %s: ", checkConfiguration.ID) } checkResult, checkOutput := checkConfiguration.CheckFunction() - reportText := result.Report.Record(project, checkConfiguration, checkResult, checkOutput) + reportText := result.Results.Record(project, checkConfiguration, checkResult, checkOutput) if configuration.OutputFormat() == "text" { fmt.Print(reportText) } } // Checks are finished for this project, so summarize its check results in the report. - result.Report.AddProjectSummaryReport(project) + result.Results.AddProjectSummary(project) if configuration.OutputFormat() == "text" { // Print the project check results summary. - fmt.Print(result.Report.ProjectSummaryText(project)) + fmt.Print(result.Results.ProjectSummaryText(project)) } } diff --git a/main.go b/main.go index 64747f5dd..c02e0bcf5 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( func main() { configuration.Initialize() // Must be called after configuration.Initialize() - result.Report.Initialize() + result.Results.Initialize() projects, err := project.FindProjects() if err != nil { @@ -27,24 +27,24 @@ func main() { } // All projects have been checked, so summarize their check results in the report. - result.Report.AddSummaryReport() + result.Results.AddSummary() if configuration.OutputFormat() == "text" { if len(projects) > 1 { // There are multiple projects, print the summary of check results for all projects. - fmt.Print(result.Report.SummaryText()) + fmt.Print(result.Results.SummaryText()) } } else { // Print the complete JSON formatted report. - fmt.Println(result.Report.JSONReport()) + fmt.Println(result.Results.JSONReport()) } if configuration.ReportFilePath() != nil { // Write report file. - result.Report.WriteReport() + result.Results.WriteReport() } - if !result.Report.Passed() { + if !result.Results.Passed() { os.Exit(1) } } diff --git a/result/result.go b/result/result.go index 7143a165a..54d5a0ead 100644 --- a/result/result.go +++ b/result/result.go @@ -18,11 +18,11 @@ import ( "github.com/arduino/go-paths-helper" ) -// Report is the global instance of the check results ReportType struct -var Report ReportType +// Results is the global instance of the check results result.Type struct +var Results Type -// ReportType is the type for the check results report -type ReportType struct { +// Type is the type for the check results data +type Type struct { Configuration toolConfigurationReportType `json:"configuration"` Projects []projectReportType `json:"projects"` Summary summaryReportType `json:"summary"` @@ -66,9 +66,9 @@ type summaryReportType struct { ErrorCount int `json:"errorCount"` } -// Initialize adds the tool configuration data to the report. -func (report *ReportType) Initialize() { - report.Configuration = toolConfigurationReportType{ +// Initialize adds the tool configuration data to the results data. +func (results *Type) Initialize() { + results.Configuration = toolConfigurationReportType{ Paths: []*paths.Path{configuration.TargetPath()}, ProjectType: configuration.SuperprojectTypeFilter().String(), Recursive: configuration.Recursive(), @@ -76,7 +76,7 @@ func (report *ReportType) Initialize() { } // Record records the result of a check and returns a text summary for it. -func (report *ReportType) Record(checkedProject project.Type, checkConfiguration checkconfigurations.Type, checkResult checkresult.Type, checkOutput string) string { +func (results *Type) Record(checkedProject project.Type, checkConfiguration checkconfigurations.Type, checkResult checkresult.Type, checkOutput string) string { checkMessage := message(checkConfiguration.MessageTemplate, checkOutput) checkLevel, err := checklevel.CheckLevel(checkConfiguration) @@ -105,11 +105,11 @@ func (report *ReportType) Record(checkedProject project.Type, checkConfiguration Message: checkMessage, } - reportExists, projectReportIndex := report.getProjectReportIndex(checkedProject.Path) + reportExists, projectReportIndex := results.getProjectReportIndex(checkedProject.Path) if !reportExists { // There is no existing report for this project. - report.Projects = append( - report.Projects, + results.Projects = append( + results.Projects, projectReportType{ Path: checkedProject.Path, ProjectType: checkedProject.ProjectType.String(), @@ -124,15 +124,15 @@ func (report *ReportType) Record(checkedProject project.Type, checkConfiguration ) } 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) + results.Projects[projectReportIndex].Checks = append(results.Projects[projectReportIndex].Checks, checkReport) } return summaryText } -// AddProjectSummaryReport summarizes the results of all checks on the given project and adds it to the report. -func (report *ReportType) AddProjectSummaryReport(checkedProject project.Type) { - reportExists, projectReportIndex := report.getProjectReportIndex(checkedProject.Path) +// AddProjectSummary summarizes the results of all checks on the given project and adds it to the report. +func (results *Type) AddProjectSummary(checkedProject project.Type) { + reportExists, projectReportIndex := results.getProjectReportIndex(checkedProject.Path) if !reportExists { panic(fmt.Sprintf("Unable to find report for %v when generating report summary", checkedProject.Path)) } @@ -140,7 +140,7 @@ func (report *ReportType) AddProjectSummaryReport(checkedProject project.Type) { pass := true warningCount := 0 errorCount := 0 - for _, checkReport := range report.Projects[projectReportIndex].Checks { + for _, checkReport := range results.Projects[projectReportIndex].Checks { if checkReport.Result == checkresult.Fail.String() { if checkReport.Level == checklevel.Warning.String() { warningCount += 1 @@ -151,7 +151,7 @@ func (report *ReportType) AddProjectSummaryReport(checkedProject project.Type) { } } - report.Projects[projectReportIndex].Summary = summaryReportType{ + results.Projects[projectReportIndex].Summary = summaryReportType{ Pass: pass, WarningCount: warningCount, ErrorCount: errorCount, @@ -159,22 +159,22 @@ func (report *ReportType) AddProjectSummaryReport(checkedProject project.Type) { } // ProjectSummaryText returns a text summary of the check results for the given project. -func (report ReportType) ProjectSummaryText(checkedProject project.Type) string { - reportExists, projectReportIndex := report.getProjectReportIndex(checkedProject.Path) +func (results Type) ProjectSummaryText(checkedProject project.Type) string { + reportExists, projectReportIndex := results.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 + projectSummaryReport := results.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 (report *ReportType) AddSummaryReport() { +// AddSummary summarizes the check results for all projects and adds it to the report. +func (results *Type) AddSummary() { pass := true warningCount := 0 errorCount := 0 - for _, projectReport := range report.Projects { + for _, projectReport := range results.Projects { if !projectReport.Summary.Pass { pass = false } @@ -182,7 +182,7 @@ func (report *ReportType) AddSummaryReport() { errorCount += projectReport.Summary.ErrorCount } - report.Summary = summaryReportType{ + results.Summary = summaryReportType{ Pass: pass, WarningCount: warningCount, ErrorCount: errorCount, @@ -190,17 +190,17 @@ func (report *ReportType) AddSummaryReport() { } // SummaryText returns a text summary of the cumulative check results. -func (report ReportType) 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) +func (results Type) SummaryText() string { + return fmt.Sprintf("Finished checking projects. Results:\nWarning count: %v\nError count: %v\nChecks passed: %v\n", results.Summary.WarningCount, results.Summary.ErrorCount, results.Summary.Pass) } -// Report returns a JSON formatted report of checks on all projects. -func (report ReportType) JSONReport() string { - return string(report.jsonReportRaw()) +// JSONReport returns a JSON formatted report of checks on all projects. +func (results Type) JSONReport() string { + return string(results.jsonReportRaw()) } -func (report ReportType) jsonReportRaw() []byte { - reportJSON, err := json.MarshalIndent(report, "", " ") +func (results Type) jsonReportRaw() []byte { + reportJSON, err := json.MarshalIndent(results, "", " ") if err != nil { panic(fmt.Sprintf("Error while formatting checks report: %v", err)) } @@ -209,9 +209,9 @@ func (report ReportType) jsonReportRaw() []byte { } // WriteReport writes a report for all projects to the specified file. -func (report ReportType) WriteReport() { +func (results Type) WriteReport() { // Write report file - err := configuration.ReportFilePath().WriteFile(report.jsonReportRaw()) + err := configuration.ReportFilePath().WriteFile(results.jsonReportRaw()) if err != nil { feedback.Errorf("Error while writing report: %v", err) os.Exit(1) @@ -219,14 +219,14 @@ func (report ReportType) WriteReport() { } // Passed returns whether the checks passed cumulatively. -func (report ReportType) Passed() bool { - return report.Summary.Pass +func (results Type) Passed() bool { + return results.Summary.Pass } -func (report ReportType) getProjectReportIndex(projectPath *paths.Path) (bool, int) { +func (results Type) getProjectReportIndex(projectPath *paths.Path) (bool, int) { var index int var projectReport projectReportType - for index, projectReport = range report.Projects { + for index, projectReport = range results.Projects { if projectReport.Path == projectPath { return true, index } From 09bbd6ae765c752de4ed5cedbe04a8a958c24385 Mon Sep 17 00:00:00 2001 From: per1234 Date: Thu, 29 Oct 2020 11:16:28 -0700 Subject: [PATCH 54/54] Use an "enum" for the output format types --- check/check.go | 7 ++++--- configuration/configuration.go | 7 ++++--- configuration/defaults.go | 3 ++- main.go | 3 ++- result/outputformat/outputformat.go | 11 +++++++++++ result/outputformat/type_string.go | 24 ++++++++++++++++++++++++ 6 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 result/outputformat/outputformat.go create mode 100644 result/outputformat/type_string.go diff --git a/check/check.go b/check/check.go index e8abcd0de..5bf557817 100644 --- a/check/check.go +++ b/check/check.go @@ -12,6 +12,7 @@ import ( "github.com/arduino/arduino-check/project" "github.com/arduino/arduino-check/result" "github.com/arduino/arduino-check/result/feedback" + "github.com/arduino/arduino-check/result/outputformat" "github.com/sirupsen/logrus" ) @@ -34,12 +35,12 @@ func RunChecks(project project.Type) { } // Output will be printed after all checks are finished when configured for "json" output format - if configuration.OutputFormat() == "text" { + if configuration.OutputFormat() == outputformat.Text { fmt.Printf("Running check %s: ", checkConfiguration.ID) } checkResult, checkOutput := checkConfiguration.CheckFunction() reportText := result.Results.Record(project, checkConfiguration, checkResult, checkOutput) - if configuration.OutputFormat() == "text" { + if configuration.OutputFormat() == outputformat.Text { fmt.Print(reportText) } } @@ -47,7 +48,7 @@ func RunChecks(project project.Type) { // Checks are finished for this project, so summarize its check results in the report. result.Results.AddProjectSummary(project) - if configuration.OutputFormat() == "text" { + if configuration.OutputFormat() == outputformat.Text { // Print the project check results summary. fmt.Print(result.Results.ProjectSummaryText(project)) } diff --git a/configuration/configuration.go b/configuration/configuration.go index c7dd4cd4c..ba66592fe 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -4,6 +4,7 @@ package configuration import ( "github.com/arduino/arduino-check/configuration/checkmode" "github.com/arduino/arduino-check/project/projecttype" + "github.com/arduino/arduino-check/result/outputformat" "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" ) @@ -25,7 +26,7 @@ func Initialize() { // customCheckModes[checkmode.Official] = false // superprojectType = projecttype.All - outputFormat = "json" + outputFormat = outputformat.JSON //reportFilePath = paths.New("report.json") logrus.SetLevel(logrus.PanicLevel) @@ -58,10 +59,10 @@ func Recursive() bool { return recursive } -var outputFormat string +var outputFormat outputformat.Type // OutputFormat returns the tool output format configuration value. -func OutputFormat() string { +func OutputFormat() outputformat.Type { return outputFormat } diff --git a/configuration/defaults.go b/configuration/defaults.go index 88429ae5e..b17dc3f37 100644 --- a/configuration/defaults.go +++ b/configuration/defaults.go @@ -5,12 +5,13 @@ package configuration import ( "github.com/arduino/arduino-check/configuration/checkmode" "github.com/arduino/arduino-check/project/projecttype" + "github.com/arduino/arduino-check/result/outputformat" ) func setDefaults() { superprojectTypeFilter = projecttype.All recursive = true - outputFormat = "text" + outputFormat = outputformat.Text // TODO: targetPath defaults to current path } diff --git a/main.go b/main.go index c02e0bcf5..53101788e 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "github.com/arduino/arduino-check/project" "github.com/arduino/arduino-check/result" "github.com/arduino/arduino-check/result/feedback" + "github.com/arduino/arduino-check/result/outputformat" ) func main() { @@ -29,7 +30,7 @@ func main() { // All projects have been checked, so summarize their check results in the report. result.Results.AddSummary() - if configuration.OutputFormat() == "text" { + if configuration.OutputFormat() == outputformat.Text { if len(projects) > 1 { // There are multiple projects, print the summary of check results for all projects. fmt.Print(result.Results.SummaryText()) diff --git a/result/outputformat/outputformat.go b/result/outputformat/outputformat.go new file mode 100644 index 000000000..ca8cbe9ff --- /dev/null +++ b/result/outputformat/outputformat.go @@ -0,0 +1,11 @@ +// Package projecttype defines the output formats +package outputformat + +// Type is the type for output formats +//go:generate stringer -type=Type -linecomment +type Type int + +const ( + Text Type = iota // text + JSON // JSON +) diff --git a/result/outputformat/type_string.go b/result/outputformat/type_string.go new file mode 100644 index 000000000..98d3579c4 --- /dev/null +++ b/result/outputformat/type_string.go @@ -0,0 +1,24 @@ +// Code generated by "stringer -type=Type -linecomment"; DO NOT EDIT. + +package outputformat + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Text-0] + _ = x[JSON-1] +} + +const _Type_name = "textJSON" + +var _Type_index = [...]uint8{0, 4, 8} + +func (i Type) String() string { + if i < 0 || i >= Type(len(_Type_index)-1) { + return "Type(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Type_name[_Type_index[i]:_Type_index[i+1]] +}