diff --git a/.github/workflows/check-go-task.yml b/.github/workflows/check-go-task.yml index 4efd2a0b26f..16b758f4304 100644 --- a/.github/workflows/check-go-task.yml +++ b/.github/workflows/check-go-task.yml @@ -114,9 +114,9 @@ jobs: version: 3.x - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v7 with: - version: v1.64.5 + version: v2.0.2 - name: Check style env: diff --git a/.golangci.yml b/.golangci.yml index 0890432757a..774ff649564 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,11 +1,11 @@ +version: "2" run: - timeout: 10m go: "1.24" tests: true linters: # Disable all linters. - disable-all: true + default: none # Enable specific linter enable: # Nice to have @@ -13,173 +13,168 @@ linters: #- errcheck #- gocritic #- thelper - - errorlint - - dupword - copyloopvar + - dupword + - errorlint - forbidigo - - gofmt - - goimports - gosec - - gosimple - govet - ineffassign - misspell - revive - staticcheck - - tenv - - typecheck - unconvert # We must disable this one because there is no support 'optional' protobuf fields yet: https://github.com/arduino/arduino-cli/pull/2570 #- protogetter -linters-settings: - govet: - # Enable analyzers by name. - # Run `GL_DEBUG=govet golangci-lint run --enable=govet` to see default, all available analyzers, and enabled analyzers. - enable: - - appends - - asmdecl - - assign - - atomic - - atomicalign - - bools - - buildtag - - cgocall - - composites - - copylocks - - deepequalerrors - - defers - - directive - - errorsas - #- fieldalignment - - findcall - - framepointer - - httpresponse - - ifaceassert - - loopclosure - - lostcancel - - nilfunc - - nilness - - printf - - reflectvaluecompare - #- shadow - - shift - - sigchanyzer - - slog - - sortslice - - stdmethods - - stringintconv - - structtag - - testinggoroutine - - tests - - unmarshal - - unreachable - - unsafeptr - - unusedresult - - unusedwrite - - forbidigo: - forbid: - - p: ^(fmt\.Print(|f|ln)|print|println)$ - msg: in cli package use `feedback.*` instead - - p: (os\.(Stdout|Stderr|Stdin))(# )? - msg: in cli package use `feedback.*` instead - analyze-types: true - - revive: - confidence: 0.8 + settings: + errorlint: + errorf: false + asserts: false + comparison: true + forbidigo: + forbid: + - pattern: ^(fmt\.Print(|f|ln)|print|println)$ + msg: in cli package use `feedback.*` instead + - pattern: (os\.(Stdout|Stderr|Stdin))(# )? + msg: in cli package use `feedback.*` instead + analyze-types: true + govet: + enable: + - appends + - asmdecl + - assign + - atomic + - atomicalign + - bools + - buildtag + - cgocall + - composites + - copylocks + - deepequalerrors + - defers + - directive + - errorsas + - findcall + - framepointer + - httpresponse + - ifaceassert + - loopclosure + - lostcancel + - nilfunc + - nilness + - printf + - reflectvaluecompare + - shift + - sigchanyzer + - slog + - sortslice + - stdmethods + - stringintconv + - structtag + - testinggoroutine + - tests + - unmarshal + - unreachable + - unsafeptr + - unusedresult + - unusedwrite + revive: + confidence: 0.8 + rules: + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: empty-block + - name: error-naming + - name: error-strings + - name: errorf + - name: exported + - name: increment-decrement + - name: package-comments + - name: range + - name: receiver-naming + - name: redefines-builtin-id + - name: superfluous-else + - name: time-naming + - name: unreachable-code + - name: var-declaration + - name: defer + - name: atomic + - name: waitgroup-by-value + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling rules: - #- name: error-return - #- name: unused-parameter - #- name: var-naming - - name: blank-imports - - name: context-as-argument - - name: context-keys-type - - name: dot-imports - - name: empty-block - - name: error-naming - - name: error-strings - - name: errorf - - name: exported - - name: increment-decrement - #- name: indent-error-flow - - name: package-comments - - name: range - - name: receiver-naming - - name: redefines-builtin-id - - name: superfluous-else - - name: time-naming - - name: unreachable-code - - name: var-declaration - - name: defer - - name: atomic - - name: waitgroup-by-value - - errorlint: - # Check for plain error comparisons. - comparison: true - - # We might evalute to allow the asserts and errofs in the future - # Do not check for plain type assertions and type switches. - asserts: false - # Do not check whether fmt.Errorf uses the %w verb for formatting errors. - errorf: false - + - linters: + - errcheck + - gosec + path: _test\.go + - linters: + - gosec + text: G401 + - linters: + - gosec + text: G501 + - linters: + - gosec + path: internal/integrationtest/ + text: G112 + - linters: + - gosec + path: executils/process.go + text: G204 + - linters: + - staticcheck + path: commands/lib/search.go + text: SA1019 + - linters: + - revive + path: arduino/libraries/loader.go + text: empty-block + - linters: + - revive + path: arduino/serialutils/serialutils.go + text: empty-block + - linters: + - revive + path: arduino/resources/download.go + text: empty-block + - linters: + - revive + path: arduino/builder/internal/progress/progress_test.go + text: empty-block + - linters: + - revive + path: internal/algorithms/channels.go + text: empty-block + - linters: + - forbidigo + path-except: internal/cli/ + - linters: + - forbidigo + path: internal/cli/.*_test.go + - linters: + - forbidigo + path: internal/cli/feedback/ + paths: + - third_party$ + - builtin$ + - examples$ issues: - # Fix found issues (if it's supported by the linter). fix: false - # List of regexps of issue texts to exclude. - # - # But independently of this option we use default exclude patterns, - # it can be disabled by `exclude-use-default: false`. - # To list all excluded by default patterns execute `golangci-lint run --help` - # - # Default: https://golangci-lint.run/usage/false-positives/#default-exclusions - exclude-rules: - # Exclude some linters from running on tests files. - - path: _test\.go - linters: [gosec, errcheck] - # G401: Use of weak cryptographic primitive - - linters: [gosec] - text: "G401" - # G501: Blocklisted import crypto/md5: weak cryptographic primitive - - linters: [gosec] - text: "G501" - # G112: Potential Slowloris Attack because ReadHeaderTimeout is not configured in the http.Server - - linters: [gosec] - path: internal/integrationtest/ - text: "G112" - # G204: Subprocess launched with a potential tainted input or cmd arguments - - linters: [gosec] - path: executils/process.go - text: "G204" - # SA1019: req.GetQuery is deprecated: Marked as deprecated in cc/arduino/cli/commands/v1/lib.proto. - - linters: [staticcheck] - path: commands/lib/search.go - text: "SA1019" - - # Ignore revive emptyblock - - linters: [revive] - path: arduino/libraries/loader.go - text: "empty-block" - - linters: [revive] - path: arduino/serialutils/serialutils.go - text: "empty-block" - - linters: [revive] - path: arduino/resources/download.go - text: "empty-block" - - linters: [revive] - path: arduino/builder/internal/progress/progress_test.go - text: "empty-block" - - linters: [revive] - path: internal/algorithms/channels.go - text: "empty-block" - - # Run linters only on specific path - - path-except: internal/cli/ - linters: - - forbidigo - - path: internal/cli/.*_test.go - linters: [forbidigo] - - path: internal/cli/feedback/ - linters: [forbidigo] +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/commands/service_compile.go b/commands/service_compile.go index 7a98759ebf4..ea36641c5e3 100644 --- a/commands/service_compile.go +++ b/commands/service_compile.go @@ -159,7 +159,7 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu signProp := boardBuildProperties.ContainsKey("build.keys.sign_key") encryptProp := boardBuildProperties.ContainsKey("build.keys.encrypt_key") // we verify that all the properties for the secure boot keys are defined or none of them is defined. - if !(keychainProp == signProp && signProp == encryptProp) { + if keychainProp != signProp || signProp != encryptProp { return errors.New(i18n.Tr("Firmware encryption/signing requires all the following properties to be defined: %s", "build.keys.keychain, build.keys.sign_key, build.keys.encrypt_key")) } @@ -245,7 +245,7 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu Message: &rpc.CompileResponse_Progress{Progress: p}, }) } - var verbosity logger.Verbosity = logger.VerbosityNormal + var verbosity = logger.VerbosityNormal if req.GetQuiet() { verbosity = logger.VerbosityQuiet } @@ -353,10 +353,10 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu // select the core name in case of "package:core" format normalizedFQBN, err := pme.NormalizeFQBN(fqbn) if err != nil { - outStream.Write([]byte(fmt.Sprintf("Could not normalize FQBN: %s\n", err))) + fmt.Fprintf(outStream, "Could not normalize FQBN: %s\n", err) normalizedFQBN = fqbn } - outStream.Write([]byte(fmt.Sprintf("FQBN: %s\n", normalizedFQBN))) + fmt.Fprintf(outStream, "FQBN: %s\n", normalizedFQBN) core = core[strings.Index(core, ":")+1:] outStream.Write([]byte(i18n.Tr("Using board '%[1]s' from platform in folder: %[2]s", targetBoard.BoardID, targetPlatform.InstallDir) + "\n")) outStream.Write([]byte(i18n.Tr("Using core '%[1]s' from platform in folder: %[2]s", core, buildPlatform.InstallDir) + "\n")) diff --git a/commands/service_upload.go b/commands/service_upload.go index 93a508be337..10f759998d7 100644 --- a/commands/service_upload.go +++ b/commands/service_upload.go @@ -505,20 +505,20 @@ func (s *arduinoCoreServerImpl) runProgramAction(ctx context.Context, pme *packa // if touch is requested but port is not specified, print a warning if touch && portToTouch == "" { - outStream.Write([]byte(fmt.Sprintln(i18n.Tr("Skipping 1200-bps touch reset: no serial port selected!")))) + fmt.Fprintln(outStream, i18n.Tr("Skipping 1200-bps touch reset: no serial port selected!")) } cb := &serialutils.ResetProgressCallbacks{ TouchingPort: func(portAddress string) { logrus.WithField("phase", "board reset").Infof("Performing 1200-bps touch reset on serial port %s", portAddress) if verbose { - outStream.Write([]byte(fmt.Sprintln(i18n.Tr("Performing 1200-bps touch reset on serial port %s", portAddress)))) + fmt.Fprintln(outStream, i18n.Tr("Performing 1200-bps touch reset on serial port %s", portAddress)) } }, WaitingForNewSerial: func() { logrus.WithField("phase", "board reset").Info("Waiting for upload port...") if verbose { - outStream.Write([]byte(fmt.Sprintln(i18n.Tr("Waiting for upload port...")))) + fmt.Fprintln(outStream, i18n.Tr("Waiting for upload port...")) } }, BootloaderPortFound: func(portAddress string) { @@ -529,9 +529,9 @@ func (s *arduinoCoreServerImpl) runProgramAction(ctx context.Context, pme *packa } if verbose { if portAddress != "" { - outStream.Write([]byte(fmt.Sprintln(i18n.Tr("Upload port found on %s", portAddress)))) + fmt.Fprintln(outStream, i18n.Tr("Upload port found on %s", portAddress)) } else { - outStream.Write([]byte(fmt.Sprintln(i18n.Tr("No upload port found, using %s as fallback", actualPort.Address)))) + fmt.Fprintln(outStream, i18n.Tr("No upload port found, using %s as fallback", actualPort.Address)) } } }, @@ -541,7 +541,7 @@ func (s *arduinoCoreServerImpl) runProgramAction(ctx context.Context, pme *packa } if newPortAddress, err := serialutils.Reset(portToTouch, wait, dryRun, nil, cb); err != nil { - errStream.Write([]byte(fmt.Sprintln(i18n.Tr("Cannot perform port reset: %s", err)))) + fmt.Fprintln(errStream, i18n.Tr("Cannot perform port reset: %s", err)) } else { if newPortAddress != "" { actualPort.Address = newPortAddress @@ -728,7 +728,7 @@ func runTool(ctx context.Context, recipeID string, props *properties.Map, outStr // Run Tool logrus.WithField("phase", "upload").Tracef("Executing upload tool: %s", cmdLine) if verbose { - outStream.Write([]byte(fmt.Sprintln(cmdLine))) + fmt.Fprintln(outStream, cmdLine) } if dryRun { return nil diff --git a/internal/arduino/builder/internal/preprocessor/ctags.go b/internal/arduino/builder/internal/preprocessor/ctags.go index c77d22e783b..ae87e35805d 100644 --- a/internal/arduino/builder/internal/preprocessor/ctags.go +++ b/internal/arduino/builder/internal/preprocessor/ctags.go @@ -65,9 +65,9 @@ func PreprocessSketchWithCtags( } // Do not bail out if we are generating the compile commands database - stderr.WriteString(fmt.Sprintf("%s: %s", + fmt.Fprintf(stderr, "%s: %s", i18n.Tr("An error occurred adding prototypes"), - i18n.Tr("the compilation database may be incomplete or inaccurate"))) + i18n.Tr("the compilation database may be incomplete or inaccurate")) if err := sourceFile.CopyTo(ctagsTarget); err != nil { return &Result{args: result.Args(), stdout: stdout.Bytes(), stderr: stderr.Bytes()}, err } diff --git a/internal/arduino/builder/internal/utils/utils.go b/internal/arduino/builder/internal/utils/utils.go index 4b4d5b79416..1c3304d39d0 100644 --- a/internal/arduino/builder/internal/utils/utils.go +++ b/internal/arduino/builder/internal/utils/utils.go @@ -82,7 +82,7 @@ func ObjFileIsUpToDate(sourceFile, objectFile, dependencyFile *paths.Path) (bool } checkDepFile := func(depFile string) (bool, error) { - rows := strings.Split(strings.Replace(depFile, "\r\n", "\n", -1), "\n") + rows := strings.Split(strings.ReplaceAll(depFile, "\r\n", "\n"), "\n") rows = f.Map(rows, removeEndingBackSlash) rows = f.Map(rows, strings.TrimSpace) rows = f.Map(rows, unescapeDep) diff --git a/internal/arduino/cores/packagemanager/loader.go b/internal/arduino/cores/packagemanager/loader.go index 14e1d6df912..2effccb825d 100644 --- a/internal/arduino/cores/packagemanager/loader.go +++ b/internal/arduino/cores/packagemanager/loader.go @@ -364,10 +364,7 @@ func convertLegacyPlatformToPluggableDiscovery(platform *cores.PlatformRelease) // Add identification properties for network protocol i := 0 - for { - if !board.Properties.ContainsKey(fmt.Sprintf("upload_port.%d.vid", i)) { - break - } + for board.Properties.ContainsKey(fmt.Sprintf("upload_port.%d.vid", i)) { i++ } board.Properties.Set(fmt.Sprintf("upload_port.%d.board", i), board.BoardID) diff --git a/internal/cli/lib/check_deps.go b/internal/cli/lib/check_deps.go index 48f57291d63..154edd146b4 100644 --- a/internal/cli/lib/check_deps.go +++ b/internal/cli/lib/check_deps.go @@ -114,13 +114,14 @@ func outputDep(dep *result.LibraryDependencyStatus) string { green := color.New(color.FgGreen) red := color.New(color.FgRed) yellow := color.New(color.FgYellow) - if dep.VersionInstalled == "" { + switch dep.VersionInstalled { + case "": res += i18n.Tr("%s must be installed.", red.Sprintf("✕ %s %s", dep.Name, dep.VersionRequired)) - } else if dep.VersionInstalled == dep.VersionRequired { + case dep.VersionRequired: res += i18n.Tr("%s is already installed.", green.Sprintf("✓ %s %s", dep.Name, dep.VersionRequired)) - } else { + default: res += i18n.Tr("%[1]s is required but %[2]s is currently installed.", yellow.Sprintf("✕ %s %s", dep.Name, dep.VersionRequired), yellow.Sprintf("%s", dep.VersionInstalled)) diff --git a/internal/mock_serial_monitor/main.go b/internal/mock_serial_monitor/main.go index f13745e1458..60302af7cd2 100644 --- a/internal/mock_serial_monitor/main.go +++ b/internal/mock_serial_monitor/main.go @@ -143,8 +143,7 @@ func (d *SerialMonitor) Open(boardPort string) (io.ReadWriter, error) { d.mockedSerialPort.Write([]byte("Tmpfile: " + d.muxFile.String() + "\n")) } for parameter, descriptor := range d.serialSettings.ConfigurationParameter { - d.mockedSerialPort.Write([]byte( - fmt.Sprintf("Configuration %s = %s\n", parameter, descriptor.Selected))) + fmt.Fprintf(d.mockedSerialPort, "Configuration %s = %s\n", parameter, descriptor.Selected) } for { n, err := d.mockedSerialPort.Read(buff)