From 47a858e736eb9258ecb7dab9b61aedef4e09e5e8 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 29 Sep 2023 10:57:17 +0200 Subject: [PATCH 1/2] Added 'upload' flags to set upload fields value --- internal/cli/upload/upload.go | 41 +++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/internal/cli/upload/upload.go b/internal/cli/upload/upload.go index aa079f98653..2ce88cfc2db 100644 --- a/internal/cli/upload/upload.go +++ b/internal/cli/upload/upload.go @@ -51,6 +51,8 @@ var ( // NewCommand created a new `upload` command func NewCommand() *cobra.Command { + var uploadFields []string + var parsedUploadFields map[string]string uploadCommand := &cobra.Command{ Use: "upload", Short: tr("Upload Arduino sketches."), @@ -59,8 +61,20 @@ func NewCommand() *cobra.Command { Args: cobra.MaximumNArgs(1), PreRun: func(cmd *cobra.Command, args []string) { arguments.CheckFlagsConflicts(cmd, "input-file", "input-dir") + if len(uploadFields) > 0 { + parsedUploadFields = map[string]string{} + for _, field := range uploadFields { + split := strings.SplitN(field, "=", 2) + if len(split) != 2 { + feedback.Fatal(tr("Invalid upload field: %s", field), feedback.ErrBadArgument) + } + parsedUploadFields[split[0]] = split[1] + } + } + }, + Run: func(cmd *cobra.Command, args []string) { + runUploadCommand(args, parsedUploadFields) }, - Run: runUploadCommand, } fqbnArg.AddToCommand(uploadCommand) @@ -73,10 +87,11 @@ func NewCommand() *cobra.Command { programmer.AddToCommand(uploadCommand) uploadCommand.Flags().BoolVar(&dryRun, "dry-run", false, tr("Do not perform the actual upload, just log out actions")) uploadCommand.Flags().MarkHidden("dry-run") + uploadCommand.Flags().StringArrayVar(&uploadFields, "upload-field", uploadFields, tr("Set a value for a field required to upload.")+" (field=value)") return uploadCommand } -func runUploadCommand(command *cobra.Command, args []string) { +func runUploadCommand(args []string, uploadFieldsArgs map[string]string) { logrus.Info("Executing `arduino-cli upload`") path := "" @@ -147,12 +162,24 @@ func runUploadCommand(command *cobra.Command, args []string) { fields := map[string]string{} if len(userFieldRes.UserFields) > 0 { - feedback.Print(tr("Uploading to specified board using %s protocol requires the following info:", port.Protocol)) - if f, err := arguments.AskForUserFields(userFieldRes.UserFields); err != nil { - msg := fmt.Sprintf("%s: %s", tr("Error getting user input"), err) - feedback.Fatal(msg, feedback.ErrGeneric) + if len(uploadFieldsArgs) > 0 { + // If the user has specified some fields via cmd-line, we don't ask for them + for _, field := range userFieldRes.UserFields { + if value, ok := uploadFieldsArgs[field.Name]; ok { + fields[field.Name] = value + } else { + feedback.Fatal(tr("Missing required upload field: %s", field.Name), feedback.ErrBadArgument) + } + } } else { - fields = f + // Otherwise prompt the user for them + feedback.Print(tr("Uploading to specified board using %s protocol requires the following info:", port.Protocol)) + if f, err := arguments.AskForUserFields(userFieldRes.UserFields); err != nil { + msg := fmt.Sprintf("%s: %s", tr("Error getting user input"), err) + feedback.Fatal(msg, feedback.ErrGeneric) + } else { + fields = f + } } } From 088850c809c4b08796851d6fc23ef7536dd90e22 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 13 Oct 2023 14:23:29 +0200 Subject: [PATCH 2/2] Introducing key/value flag type --- internal/cli/arguments/key_value.go | 81 +++++++++++++++++++++++++++++ internal/cli/upload/upload.go | 29 ++++------- 2 files changed, 91 insertions(+), 19 deletions(-) create mode 100644 internal/cli/arguments/key_value.go diff --git a/internal/cli/arguments/key_value.go b/internal/cli/arguments/key_value.go new file mode 100644 index 00000000000..1335c624640 --- /dev/null +++ b/internal/cli/arguments/key_value.go @@ -0,0 +1,81 @@ +// This file is part of arduino-cli. +// +// Copyright 2023 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-cli. +// 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 license@arduino.cc. + +package arguments + +import ( + "errors" + "fmt" + "strings" + + "github.com/spf13/cobra" +) + +// AddKeyValuePFlag adds a flag to the command that accepts a (possibly repeated) key=value pair. +func AddKeyValuePFlag(cmd *cobra.Command, field *map[string]string, name, shorthand string, value []string, usage string) { + cmd.Flags().VarP(newKVArrayValue(value, field), name, shorthand, usage) +} + +type kvArrayValue struct { + value *map[string]string + changed bool +} + +func newKVArrayValue(val []string, p *map[string]string) *kvArrayValue { + ssv := &kvArrayValue{ + value: p, + } + for _, v := range val { + ssv.Set(v) + } + ssv.changed = false + return ssv +} + +func (s *kvArrayValue) Set(arg string) error { + split := strings.SplitN(arg, "=", 2) + if len(split) != 2 { + return errors.New("required format is 'key=value'") + } + k, v := split[0], split[1] + if k == "" { + return errors.New("key cannot be empty") + } + if !s.changed { + // Remove the default value + *s.value = make(map[string]string) + s.changed = true + } + if _, ok := (*s.value)[k]; ok { + return errors.New("duplicate key: " + k) + } + (*s.value)[k] = v + return nil +} + +func (s *kvArrayValue) Type() string { + return "key=value" +} + +func (s *kvArrayValue) String() string { + if len(*s.value) == 0 { + return "" + } + res := "[" + for k, v := range *s.value { + res += fmt.Sprintf("%s=%s, ", k, v) + } + return res[:len(res)-2] + "]" +} diff --git a/internal/cli/upload/upload.go b/internal/cli/upload/upload.go index 2ce88cfc2db..b17f323dc8c 100644 --- a/internal/cli/upload/upload.go +++ b/internal/cli/upload/upload.go @@ -51,29 +51,20 @@ var ( // NewCommand created a new `upload` command func NewCommand() *cobra.Command { - var uploadFields []string - var parsedUploadFields map[string]string + uploadFields := map[string]string{} uploadCommand := &cobra.Command{ - Use: "upload", - Short: tr("Upload Arduino sketches."), - Long: tr("Upload Arduino sketches. This does NOT compile the sketch prior to upload."), - Example: " " + os.Args[0] + " upload /home/user/Arduino/MySketch", - Args: cobra.MaximumNArgs(1), + Use: "upload", + Short: tr("Upload Arduino sketches."), + Long: tr("Upload Arduino sketches. This does NOT compile the sketch prior to upload."), + Example: "" + + " " + os.Args[0] + " upload /home/user/Arduino/MySketch -p /dev/ttyACM0 -b arduino:avr:uno\n" + + " " + os.Args[0] + " upload -p 192.168.10.1 -b arduino:avr:uno --upload-field password=abc", + Args: cobra.MaximumNArgs(1), PreRun: func(cmd *cobra.Command, args []string) { arguments.CheckFlagsConflicts(cmd, "input-file", "input-dir") - if len(uploadFields) > 0 { - parsedUploadFields = map[string]string{} - for _, field := range uploadFields { - split := strings.SplitN(field, "=", 2) - if len(split) != 2 { - feedback.Fatal(tr("Invalid upload field: %s", field), feedback.ErrBadArgument) - } - parsedUploadFields[split[0]] = split[1] - } - } }, Run: func(cmd *cobra.Command, args []string) { - runUploadCommand(args, parsedUploadFields) + runUploadCommand(args, uploadFields) }, } @@ -87,7 +78,7 @@ func NewCommand() *cobra.Command { programmer.AddToCommand(uploadCommand) uploadCommand.Flags().BoolVar(&dryRun, "dry-run", false, tr("Do not perform the actual upload, just log out actions")) uploadCommand.Flags().MarkHidden("dry-run") - uploadCommand.Flags().StringArrayVar(&uploadFields, "upload-field", uploadFields, tr("Set a value for a field required to upload.")+" (field=value)") + arguments.AddKeyValuePFlag(uploadCommand, &uploadFields, "upload-field", "F", nil, tr("Set a value for a field required to upload.")) return uploadCommand }