Skip to content

Add checks #92

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
/check/checkdata/schema/testdata/invalid-schema.json
/check/checkdata/testdata/packageindexes/invalid-JSON/package_foo_index.json
/check/checkfunctions/testdata/packageindexes/invalid-JSON/package_foo_index.json
/check/checkfunctions/testdata/sketches/InvalidJSONMetadataFile/sketch.json
180 changes: 180 additions & 0 deletions check/checkconfigurations/checkconfigurations.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,21 @@ var configurations = []Type{
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.LibraryPropertiesVersionFieldNonSemver,
},
{
ProjectType: projecttype.Library,
Category: "library.properties",
Subcategory: "version field",
ID: "",
Brief: "tag mismatch",
Description: "The Library Manager indexer will reject any tag that has a library.properties version equal to a previous tag in the index.",
MessageTemplate: "The latest Git tag appears to be greater than the library.properties version value: {{.}}. You must update the version value before making the tag.",
DisableModes: []checkmode.Type{checkmode.Default},
EnableModes: []checkmode.Type{checkmode.LibraryManagerIndexed},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Default},
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.LibraryPropertiesVersionFieldBehindTag,
},
{
ProjectType: projecttype.Library,
Category: "library.properties",
Expand Down Expand Up @@ -971,6 +986,21 @@ var configurations = []Type{
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.MissingReadme,
},
{
ProjectType: projecttype.Library,
Category: "structure",
Subcategory: "",
ID: "",
Brief: "no license file",
Description: "",
MessageTemplate: "No license file found. See: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/licensing-a-repository#detecting-a-license",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Default},
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.MissingLicenseFile,
},
{
ProjectType: projecttype.Sketch,
Category: "structure",
Expand Down Expand Up @@ -1016,6 +1046,21 @@ var configurations = []Type{
ErrorModes: []checkmode.Type{checkmode.Default},
CheckFunction: checkfunctions.IncorrectLibrarySrcFolderNameCase,
},
{
ProjectType: projecttype.Library,
Category: "structure",
Subcategory: "",
ID: "",
Brief: "no examples",
Description: "",
MessageTemplate: "No example sketches found. Please provide examples. See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-examples",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Default},
ErrorModes: nil,
CheckFunction: checkfunctions.MissingExamples,
},
{
ProjectType: projecttype.Library,
Category: "structure",
Expand Down Expand Up @@ -1091,6 +1136,21 @@ var configurations = []Type{
ErrorModes: []checkmode.Type{checkmode.Default},
CheckFunction: checkfunctions.RecursiveLibraryWithUtilityFolder,
},
{
ProjectType: projecttype.Library,
Category: "code",
Subcategory: "",
ID: "",
Brief: "incorrect Arduino.h case",
Description: "This causes compilation failure on filename case-sensitive OS (e.g., Linux).",
MessageTemplate: "Incorrect of Arduino.h filename case detected in #include directive: {{.}}",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Default},
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.IncorrectArduinoDotHFileNameCase,
},
{
ProjectType: projecttype.Sketch,
Category: "structure",
Expand Down Expand Up @@ -1151,6 +1211,81 @@ var configurations = []Type{
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.MissingReadme,
},
{
ProjectType: projecttype.Sketch,
Category: "structure",
Subcategory: "",
ID: "",
Brief: "no license file",
Description: "",
MessageTemplate: "No license file found. See: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/licensing-a-repository#detecting-a-license",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Default},
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.MissingLicenseFile,
},
{
ProjectType: projecttype.Sketch,
Category: "metadata",
Subcategory: "sketch.json",
ID: "",
Brief: "invalid sketch.json JSON format",
Description: "",
MessageTemplate: "sketch.json is not a valid JSON document. See: https://arduino.github.io/arduino-cli/latest/sketch-specification/#metadata",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Permissive},
ErrorModes: []checkmode.Type{checkmode.Default},
CheckFunction: checkfunctions.SketchDotJSONJSONFormat,
},
{
ProjectType: projecttype.Sketch,
Category: "metadata",
Subcategory: "sketch.json",
ID: "",
Brief: "invalid sketch.json data format",
Description: "",
MessageTemplate: "sketch.json has an invalid data format: {{.}}. See: https://arduino.github.io/arduino-cli/latest/sketch-specification/#metadata",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Permissive},
ErrorModes: []checkmode.Type{checkmode.Default},
CheckFunction: checkfunctions.SketchDotJSONFormat,
},
{
ProjectType: projecttype.Sketch,
Category: "structure",
Subcategory: "",
ID: "",
Brief: "name mismatch",
Description: "",
MessageTemplate: "Sketch file/folder name mismatch. The primary sketch file name must match the folder: {{.}}. See: https://arduino.github.io/arduino-cli/latest/sketch-specification/#primary-sketch-file",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Permissive},
ErrorModes: []checkmode.Type{checkmode.Default},
CheckFunction: checkfunctions.SketchNameMismatch,
},
{
ProjectType: projecttype.Sketch,
Category: "code",
Subcategory: "",
ID: "",
Brief: "incorrect Arduino.h case",
Description: "This causes compilation failure on filename case-sensitive OS (e.g., Linux).",
MessageTemplate: "Incorrect of Arduino.h filename case detected in #include directive: {{.}}",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Default},
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.IncorrectArduinoDotHFileNameCase,
},
{
ProjectType: projecttype.Platform,
Category: "configuration files",
Expand Down Expand Up @@ -1181,6 +1316,51 @@ var configurations = []Type{
ErrorModes: []checkmode.Type{checkmode.Default},
CheckFunction: checkfunctions.BoardsTxtFormat,
},
{
ProjectType: projecttype.Platform,
Category: "structure",
Subcategory: "",
ID: "",
Brief: "no readme",
Description: "",
MessageTemplate: "No readme found. Please document your library. See: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/about-readmes",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Default},
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.MissingReadme,
},
{
ProjectType: projecttype.Platform,
Category: "structure",
Subcategory: "",
ID: "",
Brief: "no license file",
Description: "",
MessageTemplate: "No license file found. See: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/licensing-a-repository#detecting-a-license",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Default},
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.MissingLicenseFile,
},
{
ProjectType: projecttype.Platform,
Category: "code",
Subcategory: "",
ID: "",
Brief: "incorrect Arduino.h case",
Description: "This causes compilation failure on filename case-sensitive OS (e.g., Linux).",
MessageTemplate: "Incorrect of Arduino.h filename case detected in #include directive: {{.}}",
DisableModes: nil,
EnableModes: []checkmode.Type{checkmode.Default},
InfoModes: nil,
WarningModes: []checkmode.Type{checkmode.Default},
ErrorModes: []checkmode.Type{checkmode.Strict},
CheckFunction: checkfunctions.IncorrectArduinoDotHFileNameCase,
},
{
ProjectType: projecttype.PackageIndex,
Category: "data",
Expand Down
1 change: 1 addition & 0 deletions check/checkdata/checkdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func Initialize(project project.Type, schemasPath *paths.Path) {
projectPath = project.Path
switch project.ProjectType {
case projecttype.Sketch:
InitializeForSketch(project)
case projecttype.Library:
InitializeForLibrary(project, schemasPath)
case projecttype.Platform:
Expand Down
60 changes: 60 additions & 0 deletions check/checkdata/sketch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// This file is part of arduino-check.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-check.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].

package checkdata

import (
"github.com/arduino/arduino-check/project"
"github.com/arduino/arduino-cli/arduino/sketches"
)

// InitializeForSketch gathers the check data for the specified sketch project.
func InitializeForSketch(project project.Type) {
loadedSketch, sketchLoadError = sketches.NewSketchFromPath(ProjectPath())

metadataSketchObject := &sketches.Sketch{
Name: ProjectPath().Base(),
FullPath: ProjectPath(),
}
metadataLoadError = metadataSketchObject.ImportMetadata()
}

var sketchLoadError error

// SketchLoadError returns the error output from Arduino CLI loading the sketch.
func SketchLoadError() error {
return sketchLoadError
}

var loadedSketch *sketches.Sketch

// Sketch returns the sketch object generated by Arduino CLI.
func Sketch() *sketches.Sketch {
return loadedSketch
}

var metadataLoadError error

// MetadataLoadError returns the error produced during load of the sketch metadata.
func MetadataLoadError() error {
return metadataLoadError
}

var metadataSketchObject *sketches.Sketch

// Metadata returns the metadata object produced by Arduino CLI.
func Metadata() *sketches.Metadata {
return metadataSketchObject.Metadata
}
64 changes: 58 additions & 6 deletions check/checkfunctions/checkfunctions.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ package checkfunctions

import (
"encoding/json"
"fmt"
"regexp"
"strings"

"github.com/arduino/arduino-check/check/checkdata"
"github.com/arduino/arduino-check/check/checkresult"
"github.com/arduino/arduino-check/project/sketch"
"github.com/arduino/go-paths-helper"
)

Expand Down Expand Up @@ -77,25 +79,75 @@ func MissingReadme() (result checkresult.Type, output string) {
readmeRegexp := regexp.MustCompile(`(?i)^readme\.(markdown)|(mdown)|(mkdn)|(md)|(textile)|(rdoc)|(org)|(creole)|(mediawiki)|(wiki)|(rst)|(asciidoc)|(adoc)|(asc)|(pod)|(txt)$`)

// https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/about-readmes#about-readmes
if pathContainsReadme(checkdata.ProjectPath(), readmeRegexp) ||
(checkdata.ProjectPath().Join("docs").Exist() && pathContainsReadme(checkdata.ProjectPath().Join("docs"), readmeRegexp)) ||
(checkdata.ProjectPath().Join(".github").Exist() && pathContainsReadme(checkdata.ProjectPath().Join(".github"), readmeRegexp)) {
if pathContainsRegexpMatch(checkdata.ProjectPath(), readmeRegexp) ||
(checkdata.ProjectPath().Join("docs").Exist() && pathContainsRegexpMatch(checkdata.ProjectPath().Join("docs"), readmeRegexp)) ||
(checkdata.ProjectPath().Join(".github").Exist() && pathContainsRegexpMatch(checkdata.ProjectPath().Join(".github"), readmeRegexp)) {
return checkresult.Pass, ""
}

return checkresult.Fail, ""
}

// pathContainsReadme checks if the provided path contains a readme file recognized by GitHub.
func pathContainsReadme(path *paths.Path, readmeRegexp *regexp.Regexp) bool {
// MissingLicenseFile checks if the project has a license file that will be recognized by GitHub.
func MissingLicenseFile() (result checkresult.Type, output string) {
if checkdata.ProjectType() != checkdata.SuperProjectType() {
return checkresult.NotRun, "License file not required for subprojects"
}

// https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/licensing-a-repository#detecting-a-license
// https://github.com/licensee/licensee/blob/master/docs/what-we-look-at.md#detecting-the-license-file
// Should be `(?i)^(((un)?licen[sc]e)|(copy(ing|right))|(ofl)|(patents))(\.(?!spdx|header|gemspec).+)?$` but regexp package doesn't support negative lookahead, so only using "preferred extensions".
// github.com/dlclark/regexp2 does support negative lookahead, but I'd prefer to stick with the standard package.
licenseRegexp := regexp.MustCompile(`(?i)^(((un)?licen[sc]e)|(copy(ing|right))|(ofl)|(patents))(\.((md)|(markdown)|(txt)|(html)))?$`)

// License file must be in root of repo
if pathContainsRegexpMatch(checkdata.ProjectPath(), licenseRegexp) {
return checkresult.Pass, ""
}

return checkresult.Fail, ""
}

// IncorrectArduinoDotHFileNameCase checks for incorrect file name case of Arduino.h in #include directives.
func IncorrectArduinoDotHFileNameCase() (result checkresult.Type, output string) {
incorrectCaseRegexp := regexp.MustCompile(`^\s*#\s*include\s*["<](a((?i)rduino)|(ARDUINO))\.[hH][">]`)

directoryListing, err := checkdata.ProjectPath().ReadDirRecursive()
if err != nil {
panic(err)
}
directoryListing.FilterOutDirs()

for _, file := range directoryListing {
if !sketch.HasSupportedExtension(file) { // Won't catch all possible files, but good enough.
continue
}

lines, err := file.ReadFileAsLines()
if err != nil {
panic(err)
}

for lineNumber, line := range lines {
if incorrectCaseRegexp.MatchString(line) {
return checkresult.Fail, fmt.Sprintf("%s:%v: %s", file, lineNumber+1, line)
}
}
}

return checkresult.Pass, ""
}

// pathContainsRegexpMatch checks if the provided path contains a file name matching the given regular expression.
func pathContainsRegexpMatch(path *paths.Path, pathRegexp *regexp.Regexp) bool {
listing, err := path.ReadDir()
if err != nil {
panic(err)
}
listing.FilterOutDirs()

for _, file := range listing {
if readmeRegexp.MatchString(file.Base()) {
if pathRegexp.MatchString(file.Base()) {
return true
}
}
Expand Down
Loading