Skip to content

Commit c35b020

Browse files
committed
Add command line interface
1 parent c474fee commit c35b020

File tree

11 files changed

+393
-67
lines changed

11 files changed

+393
-67
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# arduino-check
22

3-
`arduino-check` automatically checks for common problems in your [Arduino](https://www.arduino.cc/) projects:
3+
`arduino-check` is a command line tool that automatically checks for common problems in your
4+
[Arduino](https://www.arduino.cc/) projects:
45

56
- Sketches
67
- Libraries

cli/cli.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// This file is part of arduino-check.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-check.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to license@arduino.cc.
15+
16+
// Package cli defines the arduino-check command line interface.
17+
package cli
18+
19+
import (
20+
"github.com/arduino/arduino-check/command"
21+
"github.com/spf13/cobra"
22+
)
23+
24+
// Root creates a new arduino-check command root.
25+
func Root() *cobra.Command {
26+
rootCommand := &cobra.Command{
27+
Short: "Linter for Arduino projects.",
28+
Long: "arduino-check checks specification compliance and for other common problems with Arduino projects",
29+
DisableFlagsInUseLine: true,
30+
Use: "arduino-check [FLAG]... PROJECT_PATH\n\nRun checks on PROJECT_PATH.",
31+
Run: command.ArduinoCheck,
32+
}
33+
34+
rootCommand.PersistentFlags().String("format", "text", "The output format, can be {text|json}.")
35+
rootCommand.PersistentFlags().String("library-manager", "", "Configure the checks for libraries in the Arduino Library Manager index. Can be {submit|update|false}.\nsubmit: Also run additional checks required to pass before a library is accepted for inclusion in the index.\nupdate: Also run additional checks required to pass before new releases of a library already in the index are accepted.\nfalse: Don't run any Library Manager-specific checks.")
36+
rootCommand.PersistentFlags().String("log-format", "text", "The output format for the logs, can be {text|json}.")
37+
rootCommand.PersistentFlags().String("log-level", "panic", "Messages with this level and above will be logged. Valid levels are: trace, debug, info, warn, error, fatal, panic")
38+
rootCommand.PersistentFlags().Bool("permissive", false, "Only fail when critical issues are detected.")
39+
rootCommand.PersistentFlags().String("project-type", "all", "Only check projects of the specified type and their subprojects. Can be {sketch|library|all}.")
40+
rootCommand.PersistentFlags().Bool("recursive", true, "Search path recursively for Arduino projects to check. Can be {true|false}.")
41+
rootCommand.PersistentFlags().String("report-file", "", "Save a report on the checks to this file.")
42+
43+
return rootCommand
44+
}

command/command.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// This file is part of arduino-check.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-check.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to license@arduino.cc.
15+
16+
// Package command implements the arduino-check commands.
17+
package command
18+
19+
import (
20+
"fmt"
21+
"os"
22+
23+
"github.com/arduino/arduino-check/check"
24+
"github.com/arduino/arduino-check/configuration"
25+
"github.com/arduino/arduino-check/project"
26+
"github.com/arduino/arduino-check/result"
27+
"github.com/arduino/arduino-check/result/feedback"
28+
"github.com/arduino/arduino-check/result/outputformat"
29+
"github.com/spf13/cobra"
30+
)
31+
32+
// arduinoCheck is the root command function.
33+
func ArduinoCheck(rootCommand *cobra.Command, cliArguments []string) {
34+
if err := configuration.Initialize(rootCommand.Flags(), cliArguments); err != nil {
35+
feedback.Errorf("Configuration error: %v", err)
36+
os.Exit(1)
37+
}
38+
39+
result.Results.Initialize()
40+
41+
projects, err := project.FindProjects()
42+
if err != nil {
43+
feedback.Errorf("Error while finding projects: %v", err)
44+
os.Exit(1)
45+
}
46+
47+
for _, project := range projects {
48+
check.RunChecks(project)
49+
}
50+
51+
// All projects have been checked, so summarize their check results in the report.
52+
result.Results.AddSummary()
53+
54+
if configuration.OutputFormat() == outputformat.Text {
55+
if len(projects) > 1 {
56+
// There are multiple projects, print the summary of check results for all projects.
57+
fmt.Print(result.Results.SummaryText())
58+
}
59+
} else {
60+
// Print the complete JSON formatted report.
61+
fmt.Println(result.Results.JSONReport())
62+
}
63+
64+
if configuration.ReportFilePath() != nil {
65+
// Write report file.
66+
result.Results.WriteReport()
67+
}
68+
69+
if !result.Results.Passed() {
70+
os.Exit(1)
71+
}
72+
}

configuration/checkmode/checkmode.go

+17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
package checkmode
1818

1919
import (
20+
"fmt"
21+
"strings"
22+
2023
"github.com/arduino/arduino-check/project/projecttype"
2124
"github.com/sirupsen/logrus"
2225
)
@@ -34,6 +37,20 @@ const (
3437
Default // default
3538
)
3639

40+
// LibraryManagerModeFromString parses the --library-manager flag value and returns the corresponding check mode settings.
41+
func LibraryManagerModeFromString(libraryManagerModeString string) (bool, bool, error) {
42+
switch strings.ToLower(libraryManagerModeString) {
43+
case "submit":
44+
return true, false, nil
45+
case "update":
46+
return false, true, nil
47+
case "false":
48+
return false, false, nil
49+
default:
50+
return false, false, fmt.Errorf("No matching Library Manager mode for string %s", libraryManagerModeString)
51+
}
52+
}
53+
3754
// Modes merges the default check mode values for the given superproject type with any user-specified check mode settings.
3855
func Modes(defaultCheckModes map[projecttype.Type]map[Type]bool, customCheckModes map[Type]bool, superprojectType projecttype.Type) map[Type]bool {
3956
checkModes := make(map[Type]bool)

configuration/configuration.go

+73-19
Original file line numberDiff line numberDiff line change
@@ -17,42 +17,96 @@
1717
package configuration
1818

1919
import (
20+
"fmt"
2021
"os"
22+
"strings"
2123

2224
"github.com/arduino/arduino-check/configuration/checkmode"
2325
"github.com/arduino/arduino-check/project/projecttype"
2426
"github.com/arduino/arduino-check/result/outputformat"
2527
"github.com/arduino/go-paths-helper"
2628
"github.com/sirupsen/logrus"
29+
"github.com/spf13/pflag"
2730
)
2831

29-
// TODO: will it be possible to use init() instead?
3032
// Initialize sets up the tool configuration according to defaults and user-specified options.
31-
func Initialize() {
32-
setDefaults()
33-
// TODO configuration according to command line input
34-
// TODO validate target path value, exit if not found
35-
// TODO support multiple paths
36-
// TODO validate output format input
33+
func Initialize(flags *pflag.FlagSet, projectPaths []string) error {
34+
var err error
35+
outputFormatString, _ := flags.GetString("format")
36+
outputFormat, err = outputformat.FromString(outputFormatString)
37+
if err != nil {
38+
return fmt.Errorf("--format flag value %s not valid", outputFormatString)
39+
}
3740

38-
targetPath = paths.New("e:/electronics/arduino/libraries/arduino-check-test-library")
41+
libraryManagerModeString, _ := flags.GetString("library-manager")
42+
if libraryManagerModeString != "" {
43+
customCheckModes[checkmode.LibraryManagerSubmission], customCheckModes[checkmode.LibraryManagerIndexed], err = checkmode.LibraryManagerModeFromString(libraryManagerModeString)
44+
if err != nil {
45+
return fmt.Errorf("--library-manager flag value %s not valid", libraryManagerModeString)
46+
}
47+
}
3948

40-
// customCheckModes[checkmode.Permissive] = false
41-
// customCheckModes[checkmode.LibraryManagerSubmission] = false
42-
// customCheckModes[checkmode.LibraryManagerIndexed] = false
43-
// customCheckModes[checkmode.Official] = false
44-
// superprojectType = projecttype.All
49+
logFormatString, _ := flags.GetString("log-format")
50+
logFormat, err := logFormatFromString(logFormatString)
51+
if err != nil {
52+
return fmt.Errorf("--log-format flag value %s not valid", logFormatString)
53+
}
54+
logrus.SetFormatter(logFormat)
55+
56+
logLevelString, _ := flags.GetString("log-level")
57+
logLevel, err := logrus.ParseLevel(logLevelString)
58+
if err != nil {
59+
return fmt.Errorf("--log-level flag value %s not valid", logLevelString)
60+
}
61+
logrus.SetLevel(logLevel)
62+
63+
customCheckModes[checkmode.Permissive], _ = flags.GetBool("permissive")
4564

46-
outputFormat = outputformat.JSON
47-
//reportFilePath = paths.New("report.json")
65+
superprojectTypeFilterString, _ := flags.GetString("project-type")
66+
superprojectTypeFilter, err = projecttype.FromString(superprojectTypeFilterString)
67+
if err != nil {
68+
return fmt.Errorf("--project-type flag value %s not valid", superprojectTypeFilterString)
69+
}
70+
71+
recursive, _ = flags.GetBool("recursive")
72+
73+
// TODO: validate path
74+
reportFilePathString, _ := flags.GetString("report-file")
75+
reportFilePath = paths.New(reportFilePathString)
76+
77+
// TODO validate target path value, exit if not found
78+
// TODO support multiple paths
79+
targetPath = paths.New(projectPaths[0])
4880

49-
logrus.SetLevel(logrus.PanicLevel)
81+
// TODO: set via environment variable
82+
// customCheckModes[checkmode.Official] = false
5083

5184
logrus.WithFields(logrus.Fields{
52-
"superproject type filter": SuperprojectTypeFilter(),
53-
"recursive": Recursive(),
54-
"projects path": TargetPath(),
85+
"output format": OutputFormat(),
86+
"Library Manager submission mode": customCheckModes[checkmode.LibraryManagerSubmission],
87+
"Library Manager update mode": customCheckModes[checkmode.LibraryManagerIndexed],
88+
"log format": logFormatString,
89+
"log level": logrus.GetLevel().String(),
90+
"permissive": customCheckModes[checkmode.Permissive],
91+
"superproject type filter": SuperprojectTypeFilter(),
92+
"recursive": Recursive(),
93+
"report file": ReportFilePath(),
94+
"projects path": TargetPath(),
5595
}).Debug("Configuration initialized")
96+
97+
return nil
98+
}
99+
100+
// logFormatFromString parses the --log-format flag value and returns the corresponding log formatter.
101+
func logFormatFromString(logFormatString string) (logrus.Formatter, error) {
102+
switch strings.ToLower(logFormatString) {
103+
case "text":
104+
return &logrus.TextFormatter{}, nil
105+
case "json":
106+
return &logrus.JSONFormatter{}, nil
107+
default:
108+
return nil, fmt.Errorf("No matching log format for string %s", logFormatString)
109+
}
56110
}
57111

58112
var customCheckModes = make(map[checkmode.Type]bool)

0 commit comments

Comments
 (0)