Skip to content

Commit 95acd1f

Browse files
authored
Merge pull request #18 from per1234/lint
Use Arduino Lint to validate releases
2 parents c395aaf + db141b3 commit 95acd1f

38 files changed

+1030
-166
lines changed

Diff for: .github/workflows/test-go.yml

+14
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ jobs:
6262
with:
6363
go-version: "1.14"
6464

65+
- name: Install Arduino Lint
66+
run: |
67+
ARDUINO_LINT_INSTALLATION_PATH="${{ runner.temp }}/arduino-lint"
68+
mkdir --parents "$ARDUINO_LINT_INSTALLATION_PATH"
69+
curl \
70+
-fsSL \
71+
https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/install.sh \
72+
| \
73+
BINDIR="$ARDUINO_LINT_INSTALLATION_PATH" \
74+
sh
75+
76+
# Add installation folder to path to path
77+
echo "$ARDUINO_LINT_INSTALLATION_PATH" >> "$GITHUB_PATH"
78+
6579
- name: Install Taskfile
6680
uses: arduino/actions/setup-taskfile@master
6781
with:

Diff for: libraries/lint.go

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// This file is part of libraries-repository-engine.
2+
//
3+
// Copyright 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
// You can be released from the requirements of the above licenses by purchasing
19+
// a commercial license. Buying such a license is mandatory if you want to
20+
// modify or otherwise use the software for commercial activities involving the
21+
// Arduino software without disclosing the source code of your own applications.
22+
// To purchase a commercial license, send an email to [email protected].
23+
24+
package libraries
25+
26+
import (
27+
"encoding/json"
28+
"fmt"
29+
"io/ioutil"
30+
"os"
31+
"os/exec"
32+
"path/filepath"
33+
)
34+
35+
var empty struct{}
36+
37+
var officialTypes = map[string]struct{}{
38+
"Arduino": empty,
39+
}
40+
41+
func official(metadata *Repo) bool {
42+
for _, libraryType := range metadata.Types {
43+
_, isOfficial := officialTypes[libraryType]
44+
if isOfficial {
45+
return true
46+
}
47+
}
48+
return false
49+
}
50+
51+
// RunArduinoLint runs Arduino Lint on the library and returns the report in the event of error or warnings.
52+
func RunArduinoLint(folder string, metadata *Repo) ([]byte, error) {
53+
JSONReportFolder, err := ioutil.TempDir("", "arduino-lint-report-")
54+
if err != nil {
55+
panic(err)
56+
}
57+
JSONReportPath := filepath.Join(JSONReportFolder, "report.json")
58+
defer os.RemoveAll(JSONReportPath)
59+
60+
// See: https://arduino.github.io/arduino-lint/latest/commands/arduino-lint/
61+
cmd := exec.Command(
62+
"arduino-lint",
63+
"--compliance=permissive",
64+
"--format=text",
65+
"--project-type=library",
66+
"--recursive=false",
67+
"--report-file="+JSONReportPath,
68+
folder,
69+
)
70+
// See: https://arduino.github.io/arduino-lint/latest/#environment-variables
71+
cmd.Env = modifyEnv(os.Environ(), "ARDUINO_LINT_LIBRARY_MANAGER_INDEXING", "true")
72+
cmd.Env = modifyEnv(cmd.Env, "ARDUINO_LINT_OFFICIAL", fmt.Sprintf("%t", official(metadata)))
73+
74+
textReport, lintErr := cmd.CombinedOutput()
75+
if lintErr != nil {
76+
return textReport, lintErr
77+
}
78+
79+
// Read report.
80+
rawJSONReport, err := ioutil.ReadFile(JSONReportPath)
81+
if err != nil {
82+
panic(err)
83+
}
84+
var JSONReport map[string]interface{}
85+
if err := json.Unmarshal(rawJSONReport, &JSONReport); err != nil {
86+
panic(err)
87+
}
88+
89+
// Check warning count.
90+
reportSummary := JSONReport["summary"].(map[string]interface{})
91+
warningCount := reportSummary["warningCount"].(float64)
92+
93+
// Report should be displayed when there are warnings.
94+
if warningCount > 0 {
95+
return textReport, lintErr
96+
}
97+
98+
// No warnings.
99+
return nil, nil
100+
}

Diff for: libraries/lint_test.go

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// This file is part of libraries-repository-engine.
2+
//
3+
// Copyright 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
// You can be released from the requirements of the above licenses by purchasing
19+
// a commercial license. Buying such a license is mandatory if you want to
20+
// modify or otherwise use the software for commercial activities involving the
21+
// Arduino software without disclosing the source code of your own applications.
22+
// To purchase a commercial license, send an email to [email protected].
23+
24+
package libraries
25+
26+
import (
27+
"os"
28+
"path/filepath"
29+
"regexp"
30+
"testing"
31+
32+
"github.com/stretchr/testify/assert"
33+
)
34+
35+
var testDataPath string
36+
37+
func init() {
38+
workingDirectory, err := os.Getwd()
39+
if err != nil {
40+
panic(err)
41+
}
42+
testDataPath = filepath.Join(workingDirectory, "testdata")
43+
}
44+
45+
func TestRunArduinoLint(t *testing.T) {
46+
testTables := []struct {
47+
testName string
48+
folder string
49+
official bool
50+
reportRegexp string
51+
errorAssertion assert.ErrorAssertionFunc
52+
}{
53+
{
54+
"update",
55+
"Arduino_MKRRGB",
56+
true,
57+
"^$",
58+
assert.NoError,
59+
},
60+
{
61+
"official",
62+
"Arduino_TestOff",
63+
true,
64+
"^$",
65+
assert.NoError,
66+
},
67+
{
68+
"unofficial",
69+
"Arduino_Test3rd",
70+
false,
71+
"LP012",
72+
assert.NoError,
73+
},
74+
{
75+
"error",
76+
"Arduino_TestErr",
77+
true,
78+
"LS006",
79+
assert.Error,
80+
},
81+
{
82+
"warning",
83+
"Arduino_TestWarn",
84+
true,
85+
"LP015",
86+
assert.NoError,
87+
},
88+
{
89+
"pass",
90+
"Arduino_TestPass",
91+
true,
92+
"^$",
93+
assert.NoError,
94+
},
95+
}
96+
97+
for _, testTable := range testTables {
98+
var metadata Repo
99+
if testTable.official {
100+
metadata.Types = []string{"Arduino"}
101+
} else {
102+
metadata.Types = []string{"Contributed"}
103+
}
104+
report, err := RunArduinoLint(filepath.Join(testDataPath, "libraries", testTable.folder), &metadata)
105+
assert.Regexp(t, regexp.MustCompile(testTable.reportRegexp), string(report), testTable.testName)
106+
testTable.errorAssertion(t, err, testTable.testName)
107+
}
108+
}

Diff for: libraries/metadata/metadata.go

-105
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ import (
4040
"context"
4141
"encoding/base64"
4242
"errors"
43-
"regexp"
44-
"strings"
4543

4644
"github.com/google/go-github/github"
4745
ini "github.com/vaughan0/go-ini"
@@ -64,109 +62,6 @@ type LibraryMetadata struct {
6462
Depends string
6563
}
6664

67-
const categoryUcategorized string = "Uncategorized"
68-
69-
var validCategories = []string{
70-
"Display",
71-
"Communication",
72-
"Signal Input/Output",
73-
"Sensors",
74-
"Device Control",
75-
"Timing",
76-
"Data Storage",
77-
"Data Processing",
78-
"Other",
79-
categoryUcategorized,
80-
}
81-
82-
// IsValidCategory checks if category is a valid category
83-
func IsValidCategory(category string) bool {
84-
for _, c := range validCategories {
85-
if category == c {
86-
return true
87-
}
88-
}
89-
return false
90-
}
91-
92-
// Validate checks LibraryMetadata for errors, returns an array of the errors found
93-
func (library *LibraryMetadata) Validate() []error {
94-
var errorsAccumulator []error
95-
96-
// Check lib name
97-
if !IsValidLibraryName(library.Name) {
98-
errorsAccumulator = append(errorsAccumulator, errors.New("Invalid 'name' field: "+library.Name))
99-
}
100-
101-
// Check author and maintainer existence
102-
if library.Author == "" {
103-
errorsAccumulator = append(errorsAccumulator, errors.New("'author' field must be defined"))
104-
}
105-
if library.Maintainer == "" {
106-
library.Maintainer = library.Author
107-
}
108-
109-
// Check sentence and paragraph and url existence
110-
if library.Sentence == "" || library.URL == "" {
111-
errorsAccumulator = append(errorsAccumulator, errors.New("'sentence' and 'url' fields must be defined"))
112-
}
113-
114-
newVersion, err := VersionToSemverCompliant(library.Version)
115-
if err != nil {
116-
errorsAccumulator = append(errorsAccumulator, err)
117-
}
118-
library.Version = newVersion
119-
120-
// Check if the category is valid and set to "Uncategorized" if not
121-
if !IsValidCategory(library.Category) {
122-
library.Category = categoryUcategorized
123-
}
124-
125-
// Check if 'depends' field is correctly written
126-
if !IsValidDependency(library.Depends) {
127-
errorsAccumulator = append(errorsAccumulator, errors.New("Invalid 'depends' field: "+library.Depends))
128-
}
129-
return errorsAccumulator
130-
}
131-
132-
// IsValidLibraryName checks if a string is a valid library name
133-
func IsValidLibraryName(name string) bool {
134-
if len(name) == 0 {
135-
return false
136-
}
137-
if name[0] == '-' || name[0] == '_' || name[0] == ' ' {
138-
return false
139-
}
140-
for _, char := range name {
141-
if !strings.Contains("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-. ", string(char)) {
142-
return false
143-
}
144-
}
145-
return true
146-
}
147-
148-
var re = regexp.MustCompile("^([a-zA-Z0-9](?:[a-zA-Z0-9._\\- ]*[a-zA-Z0-9])?) *(?: \\(([^()]*)\\))?$")
149-
150-
// IsValidDependency checks if the `depends` field of library.properties is correctly formatted
151-
func IsValidDependency(depends string) bool {
152-
// TODO: merge this method with db.ExtractDependenciesList
153-
depends = strings.TrimSpace(depends)
154-
if depends == "" {
155-
return true
156-
}
157-
for _, dep := range strings.Split(depends, ",") {
158-
dep = strings.TrimSpace(dep)
159-
if dep == "" {
160-
return false
161-
}
162-
matches := re.FindAllStringSubmatch(dep, -1)
163-
if matches == nil {
164-
return false
165-
}
166-
}
167-
return true
168-
}
169-
17065
// ParsePullRequest makes a LibraryMetadata by reading library.properties from a github.PullRequest
17166
func ParsePullRequest(gh *github.Client, pull *github.PullRequest) (*LibraryMetadata, error) {
17267
head := *pull.Head

Diff for: libraries/metadata/metadata_test.go

-45
This file was deleted.

0 commit comments

Comments
 (0)