From c537b5a5c1c3b493e6d245e23d661e41ae2e1270 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 4 Dec 2025 19:04:06 +0100 Subject: [PATCH 01/10] Improved handling of ProfileLibraryReference results --- internal/cli/feedback/result/rpc.go | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/internal/cli/feedback/result/rpc.go b/internal/cli/feedback/result/rpc.go index 9af38ca88db..31a87eb564d 100644 --- a/internal/cli/feedback/result/rpc.go +++ b/internal/cli/feedback/result/rpc.go @@ -1130,6 +1130,12 @@ type ProfileLibraryReference_LocalLibraryResult struct { Path string `json:"path,omitempty"` } +func (*ProfileLibraryReference_LocalLibraryResult) isProfileLibraryReference() {} + +func (l *ProfileLibraryReference_LocalLibraryResult) String() string { + return fmt.Sprintf("lib: %s", l.Path) +} + func NewProfileLibraryReference_LocalLibraryResult(resp *rpc.ProfileLibraryReference_LocalLibrary) *ProfileLibraryReference_LocalLibraryResult { return &ProfileLibraryReference_LocalLibraryResult{ Path: resp.GetPath(), @@ -1142,6 +1148,15 @@ type ProfileLibraryReference_IndexLibraryResult struct { IsDependency bool `json:"is_dependency,omitempty"` } +func (*ProfileLibraryReference_IndexLibraryResult) isProfileLibraryReference() {} + +func (l *ProfileLibraryReference_IndexLibraryResult) String() string { + if l.IsDependency { + return fmt.Sprintf("dependency: %s@%s", l.Name, l.Version) + } + return fmt.Sprintf("%s@%s", l.Name, l.Version) +} + func NewProfileLibraryReference_IndexLibraryResult(resp *rpc.ProfileLibraryReference_IndexLibrary) *ProfileLibraryReference_IndexLibraryResult { return &ProfileLibraryReference_IndexLibraryResult{ Name: resp.GetName(), @@ -1149,3 +1164,33 @@ func NewProfileLibraryReference_IndexLibraryResult(resp *rpc.ProfileLibraryRefer IsDependency: resp.GetIsDependency(), } } + +type ProfileLibraryReference struct { + Kind string `json:"kind,omitempty"` + Library ProfileLibraryReference_Library `json:"library,omitempty"` +} + +type ProfileLibraryReference_Library interface { + isProfileLibraryReference() + fmt.Stringer +} + +func NewProfileLibraryReference(resp *rpc.ProfileLibraryReference) *ProfileLibraryReference { + if lib := resp.GetIndexLibrary(); lib != nil { + return &ProfileLibraryReference{ + Library: NewProfileLibraryReference_IndexLibraryResult(lib), + Kind: "index", + } + } + if lib := resp.GetLocalLibrary(); lib != nil { + return &ProfileLibraryReference{ + Library: NewProfileLibraryReference_LocalLibraryResult(lib), + Kind: "local", + } + } + return nil +} + +func (p *ProfileLibraryReference) String() string { + return p.Library.String() +} From 80b0018673178f9549b9f6e84217fded47025489 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 4 Dec 2025 19:07:19 +0100 Subject: [PATCH 02/10] Implementation of profile commands --- internal/cli/cli.go | 2 + internal/cli/profile/profile.go | 46 ++++++++ internal/cli/profile/profile_init.go | 84 +++++++++++++ internal/cli/profile/profile_lib.go | 40 +++++++ internal/cli/profile/profile_lib_add.go | 124 ++++++++++++++++++++ internal/cli/profile/profile_lib_remove.go | 105 +++++++++++++++++ internal/cli/profile/profile_set-default.go | 57 +++++++++ 7 files changed, 458 insertions(+) create mode 100644 internal/cli/profile/profile.go create mode 100644 internal/cli/profile/profile_init.go create mode 100644 internal/cli/profile/profile_lib.go create mode 100644 internal/cli/profile/profile_lib_add.go create mode 100644 internal/cli/profile/profile_lib_remove.go create mode 100644 internal/cli/profile/profile_set-default.go diff --git a/internal/cli/cli.go b/internal/cli/cli.go index eb5c381abf5..11744ca5bc5 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -36,6 +36,7 @@ import ( "github.com/arduino/arduino-cli/internal/cli/lib" "github.com/arduino/arduino-cli/internal/cli/monitor" "github.com/arduino/arduino-cli/internal/cli/outdated" + "github.com/arduino/arduino-cli/internal/cli/profile" "github.com/arduino/arduino-cli/internal/cli/sketch" "github.com/arduino/arduino-cli/internal/cli/update" "github.com/arduino/arduino-cli/internal/cli/updater" @@ -162,6 +163,7 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { cmd.AddCommand(burnbootloader.NewCommand(srv)) cmd.AddCommand(version.NewCommand(srv)) cmd.AddCommand(feedback.NewCommand()) + cmd.AddCommand(profile.NewCommand(srv)) cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, i18n.Tr("Print the logs on the standard output.")) cmd.Flag("verbose").Hidden = true diff --git a/internal/cli/profile/profile.go b/internal/cli/profile/profile.go new file mode 100644 index 00000000000..a29835e4bb8 --- /dev/null +++ b/internal/cli/profile/profile.go @@ -0,0 +1,46 @@ +// This file is part of arduino-cli. +// +// Copyright 2025 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 profile + +import ( + "os" + + "github.com/arduino/arduino-cli/internal/cli/arguments" + "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/spf13/cobra" +) + +var ( + fqbnArg arguments.Fqbn // Fully Qualified Board Name, e.g.: arduino:avr:uno. + profileArg arguments.Profile // Name of the profile to add to the project +) + +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { + profileCommand := &cobra.Command{ + Use: "profile", + Short: i18n.Tr("Build profile operations."), + Long: i18n.Tr("Build profile operations."), + Example: " " + os.Args[0] + " profile init", + } + + profileCommand.AddCommand(initProfileCreateCommand(srv)) + profileCommand.AddCommand(initProfileLibCommand(srv)) + profileCommand.AddCommand(initProfileSetDefaultCommand(srv)) + // profileCommand.AddCommand(initDumpCommand(srv)) + + return profileCommand +} diff --git a/internal/cli/profile/profile_init.go b/internal/cli/profile/profile_init.go new file mode 100644 index 00000000000..97f142562dd --- /dev/null +++ b/internal/cli/profile/profile_init.go @@ -0,0 +1,84 @@ +// This file is part of arduino-cli. +// +// Copyright 2025 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 profile + +import ( + "context" + "os" + + "github.com/arduino/arduino-cli/internal/cli/arguments" + "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/cli/instance" + "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/go-paths-helper" + "github.com/spf13/cobra" +) + +func initProfileCreateCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { + var defaultProfile bool + initCommand := &cobra.Command{ + Use: "init", + Short: i18n.Tr("Create or update the sketch project file."), + Long: i18n.Tr("Create or update the sketch project file."), + Example: "" + + " # " + i18n.Tr("Creates or updates the sketch project file in the current directory.") + "\n" + + " " + os.Args[0] + " profile init\n" + + " " + os.Args[0] + " profile init --profile uno_profile -b arduino:avr:uno", + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + runInitCommand(cmd.Context(), args, srv, defaultProfile) + }, + } + fqbnArg.AddToCommand(initCommand, srv) + profileArg.AddToCommand(initCommand, srv) + initCommand.Flags().BoolVar(&defaultProfile, "default", false, i18n.Tr("Set the profile as the default one.")) + return initCommand +} + +func runInitCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, defaultProfile bool) { + path := "" + if len(args) > 0 { + path = args[0] + } + + sketchPath := arguments.InitSketchPath(path) + + inst := instance.CreateAndInit(ctx, srv) + + _, err := srv.ProfileCreate(ctx, &rpc.ProfileCreateRequest{ + Instance: inst, + SketchPath: sketchPath.String(), + ProfileName: profileArg.Get(), + Fqbn: fqbnArg.String(), + DefaultProfile: defaultProfile}) + if err != nil { + feedback.Fatal(i18n.Tr("Error initializing the project file: %v", err), feedback.ErrGeneric) + } + feedback.PrintResult(profileResult{ProjectFilePath: sketchPath.Join("sketch.yaml")}) +} + +type profileResult struct { + ProjectFilePath *paths.Path `json:"project_path"` +} + +func (ir profileResult) Data() interface{} { + return ir +} + +func (ir profileResult) String() string { + return i18n.Tr("Project file created in: %s", ir.ProjectFilePath) +} diff --git a/internal/cli/profile/profile_lib.go b/internal/cli/profile/profile_lib.go new file mode 100644 index 00000000000..97be3274355 --- /dev/null +++ b/internal/cli/profile/profile_lib.go @@ -0,0 +1,40 @@ +// This file is part of arduino-cli. +// +// Copyright 2025 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 profile + +import ( + "os" + + "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/spf13/cobra" +) + +func initProfileLibCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { + libCommand := &cobra.Command{ + Use: "lib", + Short: i18n.Tr("Commands related to build profile libraries."), + Long: i18n.Tr("Commands related to the library dependencies of build profiles."), + Example: "" + + " " + os.Args[0] + " profile lib add AudioZero -m my_profile\n" + + " " + os.Args[0] + " profile lib remove Arduino_JSON --profile my_profile\n", + } + + libCommand.AddCommand(initLibAddCommand(srv)) + libCommand.AddCommand(initLibRemoveCommand(srv)) + + return libCommand +} diff --git a/internal/cli/profile/profile_lib_add.go b/internal/cli/profile/profile_lib_add.go new file mode 100644 index 00000000000..dc2b5aeecc0 --- /dev/null +++ b/internal/cli/profile/profile_lib_add.go @@ -0,0 +1,124 @@ +// This file is part of arduino-cli. +// +// Copyright 2025 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 profile + +import ( + "context" + "fmt" + "os" + + "github.com/arduino/arduino-cli/internal/cli/arguments" + "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/cli/feedback/result" + "github.com/arduino/arduino-cli/internal/cli/instance" + "github.com/arduino/arduino-cli/internal/cli/lib" + "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/spf13/cobra" + "go.bug.st/f" +) + +func initLibAddCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { + var destDir string + var noDeps bool + var noOverwrite bool + addCommand := &cobra.Command{ + Use: fmt.Sprintf("add %s[@%s]...", i18n.Tr("LIBRARY"), i18n.Tr("VERSION_NUMBER")), + Short: i18n.Tr("Adds a library to the profile."), + Long: i18n.Tr("Adds a library to the profile."), + Example: "" + + " " + os.Args[0] + " profile lib add AudioZero -m my_profile\n" + + " " + os.Args[0] + " profile lib add Arduino_JSON@0.2.0 --profile my_profile\n", + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + runLibAddCommand(cmd.Context(), args, srv, destDir, noDeps, noOverwrite) + }, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return arguments.GetInstallableLibs(cmd.Context(), srv), cobra.ShellCompDirectiveDefault + }, + } + + addCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) + addCommand.Flags().BoolVar(&noDeps, "no-deps", false, i18n.Tr("Do not add dependencies.")) + addCommand.Flags().BoolVar(&noOverwrite, "no-overwrite", false, i18n.Tr("Do not overwrite already added libraries.")) + + profileArg.AddToCommand(addCommand, srv) + + return addCommand +} + +func runLibAddCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, destDir string, noAddDeps, noOverwrite bool) { + sketchPath := arguments.InitSketchPath(destDir) + + instance := instance.CreateAndInit(ctx, srv) + libRefs, err := lib.ParseLibraryReferenceArgsAndAdjustCase(ctx, srv, instance, args) + if err != nil { + feedback.Fatal(i18n.Tr("Arguments error: %v", err), feedback.ErrBadArgument) + } + addDeps := !noAddDeps + for _, lib := range libRefs { + resp, err := srv.ProfileLibAdd(ctx, &rpc.ProfileLibAddRequest{ + Instance: instance, + SketchPath: sketchPath.String(), + ProfileName: profileArg.Get(), + Library: &rpc.ProfileLibraryReference{ + Library: &rpc.ProfileLibraryReference_IndexLibrary_{ + IndexLibrary: &rpc.ProfileLibraryReference_IndexLibrary{ + Name: lib.Name, + Version: lib.Version, + }, + }, + }, + AddDependencies: &addDeps, + NoOverwrite: &noOverwrite, + }) + if err != nil { + feedback.Fatal(i18n.Tr("Error adding %s to the profile %s: %v", lib.Name, profileArg.Get(), err), feedback.ErrGeneric) + } + feedback.PrintResult(libAddResult{ + AddedLibraries: f.Map(resp.GetAddedLibraries(), result.NewProfileLibraryReference), + SkippedLibraries: f.Map(resp.GetSkippedLibraries(), result.NewProfileLibraryReference), + ProfileName: resp.ProfileName, + }) + } +} + +type libAddResult struct { + AddedLibraries []*result.ProfileLibraryReference `json:"added_libraries"` + SkippedLibraries []*result.ProfileLibraryReference `json:"skipped_libraries"` + ProfileName string `json:"profile_name"` +} + +func (lr libAddResult) Data() any { + return lr +} + +func (lr libAddResult) String() string { + res := "" + if len(lr.AddedLibraries) > 0 { + res += fmt.Sprintln(i18n.Tr("The following libraries were added to the profile %s:", lr.ProfileName)) + for _, l := range lr.AddedLibraries { + res += fmt.Sprintf(" - %s\n", l) + } + } + if len(lr.SkippedLibraries) > 0 { + res += fmt.Sprintln(i18n.Tr("The following libraries were already present in the profile %s and were not modified:", lr.ProfileName)) + for _, l := range lr.SkippedLibraries { + res += fmt.Sprintf(" - %s\n", l) + } + } + return res +} diff --git a/internal/cli/profile/profile_lib_remove.go b/internal/cli/profile/profile_lib_remove.go new file mode 100644 index 00000000000..e179fb50624 --- /dev/null +++ b/internal/cli/profile/profile_lib_remove.go @@ -0,0 +1,105 @@ +// This file is part of arduino-cli. +// +// Copyright 2025 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 profile + +import ( + "context" + "fmt" + "os" + + "github.com/arduino/arduino-cli/internal/cli/arguments" + "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/cli/feedback/result" + "github.com/arduino/arduino-cli/internal/cli/instance" + "github.com/arduino/arduino-cli/internal/cli/lib" + "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/spf13/cobra" + "go.bug.st/f" +) + +func initLibRemoveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { + var destDir string + + removeCommand := &cobra.Command{ + Use: fmt.Sprintf("remove %s[@%s]...", i18n.Tr("LIBRARY"), i18n.Tr("VERSION_NUMBER")), + Short: i18n.Tr("Removes a library from the profile."), + Long: i18n.Tr("Removes a library from the profile."), + Example: "" + + " " + os.Args[0] + " profile lib remove AudioZero -m my_profile\n" + + " " + os.Args[0] + " profile lib remove Arduino_JSON@0.2.0 --profile my_profile\n", + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + runLibRemoveCommand(cmd.Context(), args, srv, destDir) + }, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return arguments.GetInstallableLibs(cmd.Context(), srv), cobra.ShellCompDirectiveDefault + }, + } + + removeCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) + profileArg.AddToCommand(removeCommand, srv) + + return removeCommand +} + +func runLibRemoveCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, destDir string) { + sketchPath := arguments.InitSketchPath(destDir) + + instance := instance.CreateAndInit(ctx, srv) + libRefs, err := lib.ParseLibraryReferenceArgsAndAdjustCase(ctx, srv, instance, args) + if err != nil { + feedback.Fatal(i18n.Tr("Arguments error: %v", err), feedback.ErrBadArgument) + } + for _, lib := range libRefs { + resp, err := srv.ProfileLibRemove(ctx, &rpc.ProfileLibRemoveRequest{ + SketchPath: sketchPath.String(), + ProfileName: profileArg.Get(), + Library: &rpc.ProfileLibraryReference{ + Library: &rpc.ProfileLibraryReference_IndexLibrary_{ + IndexLibrary: &rpc.ProfileLibraryReference_IndexLibrary{ + Name: lib.Name, + Version: lib.Version, + }, + }, + }, + }) + if err != nil { + feedback.Fatal(fmt.Sprintf("%s: %v", + i18n.Tr("Error removing library %[1]s from the profile", lib.Name), err), feedback.ErrGeneric) + } + feedback.PrintResult(libRemoveResult{ + RemovedLibraries: f.Map(resp.GetRemovedLibraries(), result.NewProfileLibraryReference), + ProfileName: resp.ProfileName}) + } +} + +type libRemoveResult struct { + RemovedLibraries []*result.ProfileLibraryReference `json:"removed_libraries"` + ProfileName string `json:"profile_name"` +} + +func (lr libRemoveResult) Data() interface{} { + return lr +} + +func (lr libRemoveResult) String() string { + res := fmt.Sprintln(i18n.Tr("The following libraries were removed from the profile %s:", lr.ProfileName)) + for _, lib := range lr.RemovedLibraries { + res += fmt.Sprintf(" - %s\n", lib) + } + return res +} diff --git a/internal/cli/profile/profile_set-default.go b/internal/cli/profile/profile_set-default.go new file mode 100644 index 00000000000..e2e3079a4d6 --- /dev/null +++ b/internal/cli/profile/profile_set-default.go @@ -0,0 +1,57 @@ +// This file is part of arduino-cli. +// +// Copyright 2025 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 profile + +import ( + "context" + "os" + + "github.com/arduino/arduino-cli/internal/cli/arguments" + "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/spf13/cobra" +) + +func initProfileSetDefaultCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { + var destDir string + setDefaultCommand := &cobra.Command{ + Use: "set-default", + Short: i18n.Tr("Set the default build profile."), + Long: i18n.Tr("Set the default build profile."), + Example: "" + + " " + os.Args[0] + " profile set-default my_profile\n", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + runSetDefaultCommand(cmd.Context(), args, srv, destDir) + }, + } + + setDefaultCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) + + return setDefaultCommand +} + +func runSetDefaultCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, destDir string) { + profileName := args[0] + sketchPath := arguments.InitSketchPath(destDir) + + _, err := srv.ProfileSetDefault(ctx, &rpc.ProfileSetDefaultRequest{SketchPath: sketchPath.String(), ProfileName: profileName}) + if err != nil { + feedback.Fatal(i18n.Tr("Cannot set %s as default profile: %v", profileName, err), feedback.ErrGeneric) + } + feedback.Print(i18n.Tr("Default profile set to: %s", profileName)) +} From 08708b9219c332a29e509926643245f790b570db Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 5 Dec 2025 13:31:57 +0100 Subject: [PATCH 03/10] Improved some help description --- internal/cli/profile/profile_lib.go | 3 +-- internal/cli/profile/profile_lib_add.go | 3 +-- internal/cli/profile/profile_lib_remove.go | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/internal/cli/profile/profile_lib.go b/internal/cli/profile/profile_lib.go index 97be3274355..04371ea1dd9 100644 --- a/internal/cli/profile/profile_lib.go +++ b/internal/cli/profile/profile_lib.go @@ -26,8 +26,7 @@ import ( func initProfileLibCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { libCommand := &cobra.Command{ Use: "lib", - Short: i18n.Tr("Commands related to build profile libraries."), - Long: i18n.Tr("Commands related to the library dependencies of build profiles."), + Short: i18n.Tr("Commands to manage libraries in sketch profiles."), Example: "" + " " + os.Args[0] + " profile lib add AudioZero -m my_profile\n" + " " + os.Args[0] + " profile lib remove Arduino_JSON --profile my_profile\n", diff --git a/internal/cli/profile/profile_lib_add.go b/internal/cli/profile/profile_lib_add.go index dc2b5aeecc0..ff767f6aca6 100644 --- a/internal/cli/profile/profile_lib_add.go +++ b/internal/cli/profile/profile_lib_add.go @@ -37,8 +37,7 @@ func initLibAddCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var noOverwrite bool addCommand := &cobra.Command{ Use: fmt.Sprintf("add %s[@%s]...", i18n.Tr("LIBRARY"), i18n.Tr("VERSION_NUMBER")), - Short: i18n.Tr("Adds a library to the profile."), - Long: i18n.Tr("Adds a library to the profile."), + Short: i18n.Tr("Adds a library to a sketch profile."), Example: "" + " " + os.Args[0] + " profile lib add AudioZero -m my_profile\n" + " " + os.Args[0] + " profile lib add Arduino_JSON@0.2.0 --profile my_profile\n", diff --git a/internal/cli/profile/profile_lib_remove.go b/internal/cli/profile/profile_lib_remove.go index e179fb50624..a20db242856 100644 --- a/internal/cli/profile/profile_lib_remove.go +++ b/internal/cli/profile/profile_lib_remove.go @@ -36,8 +36,7 @@ func initLibRemoveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { removeCommand := &cobra.Command{ Use: fmt.Sprintf("remove %s[@%s]...", i18n.Tr("LIBRARY"), i18n.Tr("VERSION_NUMBER")), - Short: i18n.Tr("Removes a library from the profile."), - Long: i18n.Tr("Removes a library from the profile."), + Short: i18n.Tr("Removes a library from a sketch profile."), Example: "" + " " + os.Args[0] + " profile lib remove AudioZero -m my_profile\n" + " " + os.Args[0] + " profile lib remove Arduino_JSON@0.2.0 --profile my_profile\n", From 82c627a7137e3170332cdf1ac9b512149d3dcc7b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 4 Dec 2025 19:11:33 +0100 Subject: [PATCH 04/10] Moved args into their correct place --- internal/cli/profile/profile.go | 8 -------- internal/cli/profile/profile_init.go | 18 ++++++++++-------- internal/cli/profile/profile_lib.go | 2 -- internal/cli/profile/profile_lib_add.go | 14 ++++++-------- internal/cli/profile/profile_lib_remove.go | 12 +++++------- internal/cli/profile/profile_set-default.go | 2 -- 6 files changed, 21 insertions(+), 35 deletions(-) diff --git a/internal/cli/profile/profile.go b/internal/cli/profile/profile.go index a29835e4bb8..2542b0fe0a2 100644 --- a/internal/cli/profile/profile.go +++ b/internal/cli/profile/profile.go @@ -18,17 +18,11 @@ package profile import ( "os" - "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" ) -var ( - fqbnArg arguments.Fqbn // Fully Qualified Board Name, e.g.: arduino:avr:uno. - profileArg arguments.Profile // Name of the profile to add to the project -) - func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { profileCommand := &cobra.Command{ Use: "profile", @@ -40,7 +34,5 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { profileCommand.AddCommand(initProfileCreateCommand(srv)) profileCommand.AddCommand(initProfileLibCommand(srv)) profileCommand.AddCommand(initProfileSetDefaultCommand(srv)) - // profileCommand.AddCommand(initDumpCommand(srv)) - return profileCommand } diff --git a/internal/cli/profile/profile_init.go b/internal/cli/profile/profile_init.go index 97f142562dd..e7e2e7c3b3d 100644 --- a/internal/cli/profile/profile_init.go +++ b/internal/cli/profile/profile_init.go @@ -29,7 +29,9 @@ import ( ) func initProfileCreateCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { - var defaultProfile bool + var setAsDefault bool + var fqbnArg arguments.Fqbn + var profileArg arguments.Profile initCommand := &cobra.Command{ Use: "init", Short: i18n.Tr("Create or update the sketch project file."), @@ -40,16 +42,16 @@ func initProfileCreateCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { " " + os.Args[0] + " profile init --profile uno_profile -b arduino:avr:uno", Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runInitCommand(cmd.Context(), args, srv, defaultProfile) + runInitCommand(cmd.Context(), args, srv, profileArg.Get(), fqbnArg.String(), setAsDefault) }, } fqbnArg.AddToCommand(initCommand, srv) profileArg.AddToCommand(initCommand, srv) - initCommand.Flags().BoolVar(&defaultProfile, "default", false, i18n.Tr("Set the profile as the default one.")) + initCommand.Flags().BoolVar(&setAsDefault, "set-default", false, i18n.Tr("Set the profile as the default one.")) return initCommand } -func runInitCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, defaultProfile bool) { +func runInitCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, profile, fqbn string, setAsDefault bool) { path := "" if len(args) > 0 { path = args[0] @@ -62,9 +64,9 @@ func runInitCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServi _, err := srv.ProfileCreate(ctx, &rpc.ProfileCreateRequest{ Instance: inst, SketchPath: sketchPath.String(), - ProfileName: profileArg.Get(), - Fqbn: fqbnArg.String(), - DefaultProfile: defaultProfile}) + ProfileName: profile, + Fqbn: fqbn, + DefaultProfile: setAsDefault}) if err != nil { feedback.Fatal(i18n.Tr("Error initializing the project file: %v", err), feedback.ErrGeneric) } @@ -75,7 +77,7 @@ type profileResult struct { ProjectFilePath *paths.Path `json:"project_path"` } -func (ir profileResult) Data() interface{} { +func (ir profileResult) Data() any { return ir } diff --git a/internal/cli/profile/profile_lib.go b/internal/cli/profile/profile_lib.go index 04371ea1dd9..74e7d6ca242 100644 --- a/internal/cli/profile/profile_lib.go +++ b/internal/cli/profile/profile_lib.go @@ -31,9 +31,7 @@ func initProfileLibCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { " " + os.Args[0] + " profile lib add AudioZero -m my_profile\n" + " " + os.Args[0] + " profile lib remove Arduino_JSON --profile my_profile\n", } - libCommand.AddCommand(initLibAddCommand(srv)) libCommand.AddCommand(initLibRemoveCommand(srv)) - return libCommand } diff --git a/internal/cli/profile/profile_lib_add.go b/internal/cli/profile/profile_lib_add.go index ff767f6aca6..06f01f2d4db 100644 --- a/internal/cli/profile/profile_lib_add.go +++ b/internal/cli/profile/profile_lib_add.go @@ -35,6 +35,7 @@ func initLibAddCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var destDir string var noDeps bool var noOverwrite bool + var profileArg arguments.Profile addCommand := &cobra.Command{ Use: fmt.Sprintf("add %s[@%s]...", i18n.Tr("LIBRARY"), i18n.Tr("VERSION_NUMBER")), Short: i18n.Tr("Adds a library to a sketch profile."), @@ -43,23 +44,20 @@ func initLibAddCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { " " + os.Args[0] + " profile lib add Arduino_JSON@0.2.0 --profile my_profile\n", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runLibAddCommand(cmd.Context(), args, srv, destDir, noDeps, noOverwrite) + runLibAddCommand(cmd.Context(), args, srv, profileArg.Get(), destDir, noDeps, noOverwrite) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return arguments.GetInstallableLibs(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } - + profileArg.AddToCommand(addCommand, srv) addCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) addCommand.Flags().BoolVar(&noDeps, "no-deps", false, i18n.Tr("Do not add dependencies.")) addCommand.Flags().BoolVar(&noOverwrite, "no-overwrite", false, i18n.Tr("Do not overwrite already added libraries.")) - - profileArg.AddToCommand(addCommand, srv) - return addCommand } -func runLibAddCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, destDir string, noAddDeps, noOverwrite bool) { +func runLibAddCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, profile, destDir string, noAddDeps, noOverwrite bool) { sketchPath := arguments.InitSketchPath(destDir) instance := instance.CreateAndInit(ctx, srv) @@ -72,7 +70,7 @@ func runLibAddCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreSer resp, err := srv.ProfileLibAdd(ctx, &rpc.ProfileLibAddRequest{ Instance: instance, SketchPath: sketchPath.String(), - ProfileName: profileArg.Get(), + ProfileName: profile, Library: &rpc.ProfileLibraryReference{ Library: &rpc.ProfileLibraryReference_IndexLibrary_{ IndexLibrary: &rpc.ProfileLibraryReference_IndexLibrary{ @@ -85,7 +83,7 @@ func runLibAddCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreSer NoOverwrite: &noOverwrite, }) if err != nil { - feedback.Fatal(i18n.Tr("Error adding %s to the profile %s: %v", lib.Name, profileArg.Get(), err), feedback.ErrGeneric) + feedback.Fatal(i18n.Tr("Error adding %s to the profile %s: %v", lib.Name, profile, err), feedback.ErrGeneric) } feedback.PrintResult(libAddResult{ AddedLibraries: f.Map(resp.GetAddedLibraries(), result.NewProfileLibraryReference), diff --git a/internal/cli/profile/profile_lib_remove.go b/internal/cli/profile/profile_lib_remove.go index a20db242856..3699b65de1d 100644 --- a/internal/cli/profile/profile_lib_remove.go +++ b/internal/cli/profile/profile_lib_remove.go @@ -33,7 +33,7 @@ import ( func initLibRemoveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var destDir string - + var profileArg arguments.Profile removeCommand := &cobra.Command{ Use: fmt.Sprintf("remove %s[@%s]...", i18n.Tr("LIBRARY"), i18n.Tr("VERSION_NUMBER")), Short: i18n.Tr("Removes a library from a sketch profile."), @@ -42,20 +42,18 @@ func initLibRemoveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { " " + os.Args[0] + " profile lib remove Arduino_JSON@0.2.0 --profile my_profile\n", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runLibRemoveCommand(cmd.Context(), args, srv, destDir) + runLibRemoveCommand(cmd.Context(), srv, args, profileArg.Get(), destDir) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return arguments.GetInstallableLibs(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } - - removeCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) profileArg.AddToCommand(removeCommand, srv) - + removeCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) return removeCommand } -func runLibRemoveCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, destDir string) { +func runLibRemoveCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, profile, destDir string) { sketchPath := arguments.InitSketchPath(destDir) instance := instance.CreateAndInit(ctx, srv) @@ -66,7 +64,7 @@ func runLibRemoveCommand(ctx context.Context, args []string, srv rpc.ArduinoCore for _, lib := range libRefs { resp, err := srv.ProfileLibRemove(ctx, &rpc.ProfileLibRemoveRequest{ SketchPath: sketchPath.String(), - ProfileName: profileArg.Get(), + ProfileName: profile, Library: &rpc.ProfileLibraryReference{ Library: &rpc.ProfileLibraryReference_IndexLibrary_{ IndexLibrary: &rpc.ProfileLibraryReference_IndexLibrary{ diff --git a/internal/cli/profile/profile_set-default.go b/internal/cli/profile/profile_set-default.go index e2e3079a4d6..6e5034d9cef 100644 --- a/internal/cli/profile/profile_set-default.go +++ b/internal/cli/profile/profile_set-default.go @@ -39,9 +39,7 @@ func initProfileSetDefaultCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Comma runSetDefaultCommand(cmd.Context(), args, srv, destDir) }, } - setDefaultCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) - return setDefaultCommand } From 861d87720782a962a293bbfd115b06f230919436 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 5 Dec 2025 15:05:34 +0100 Subject: [PATCH 05/10] Added remove deps --- internal/cli/profile/profile_lib_remove.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/internal/cli/profile/profile_lib_remove.go b/internal/cli/profile/profile_lib_remove.go index 3699b65de1d..08ea0c3b725 100644 --- a/internal/cli/profile/profile_lib_remove.go +++ b/internal/cli/profile/profile_lib_remove.go @@ -34,15 +34,16 @@ import ( func initLibRemoveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var destDir string var profileArg arguments.Profile + var noDeps bool removeCommand := &cobra.Command{ Use: fmt.Sprintf("remove %s[@%s]...", i18n.Tr("LIBRARY"), i18n.Tr("VERSION_NUMBER")), - Short: i18n.Tr("Removes a library from a sketch profile."), + Short: i18n.Tr("Removes a library from a sketch profile with its dependencies if no longer needed."), Example: "" + " " + os.Args[0] + " profile lib remove AudioZero -m my_profile\n" + " " + os.Args[0] + " profile lib remove Arduino_JSON@0.2.0 --profile my_profile\n", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runLibRemoveCommand(cmd.Context(), srv, args, profileArg.Get(), destDir) + runLibRemoveCommand(cmd.Context(), srv, args, profileArg.Get(), destDir, noDeps) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return arguments.GetInstallableLibs(cmd.Context(), srv), cobra.ShellCompDirectiveDefault @@ -50,10 +51,11 @@ func initLibRemoveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { } profileArg.AddToCommand(removeCommand, srv) removeCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) + removeCommand.Flags().BoolVar(&noDeps, "no-deps", false, i18n.Tr("Do not remove unused dependencies.")) return removeCommand } -func runLibRemoveCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, profile, destDir string) { +func runLibRemoveCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, profile, destDir string, noDeps bool) { sketchPath := arguments.InitSketchPath(destDir) instance := instance.CreateAndInit(ctx, srv) @@ -61,8 +63,10 @@ func runLibRemoveCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, if err != nil { feedback.Fatal(i18n.Tr("Arguments error: %v", err), feedback.ErrBadArgument) } + removeDeps := !noDeps for _, lib := range libRefs { resp, err := srv.ProfileLibRemove(ctx, &rpc.ProfileLibRemoveRequest{ + Instance: instance, SketchPath: sketchPath.String(), ProfileName: profile, Library: &rpc.ProfileLibraryReference{ @@ -73,6 +77,7 @@ func runLibRemoveCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, }, }, }, + RemoveDependencies: &removeDeps, }) if err != nil { feedback.Fatal(fmt.Sprintf("%s: %v", From d4324660903370eb5133b18856910047b2360992 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 5 Dec 2025 15:55:44 +0100 Subject: [PATCH 06/10] Fixed completion in profile lib remove --- internal/cli/arguments/completion.go | 17 +++++++++++++++++ internal/cli/profile/profile_lib_remove.go | 5 ++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/internal/cli/arguments/completion.go b/internal/cli/arguments/completion.go index 9b6678fe9ac..902b26ffb73 100644 --- a/internal/cli/arguments/completion.go +++ b/internal/cli/arguments/completion.go @@ -20,6 +20,7 @@ import ( "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/go-paths-helper" "go.bug.st/f" ) @@ -172,3 +173,19 @@ func GetAvailablePorts(ctx context.Context, srv rpc.ArduinoCoreServiceServer) [] // Transform the data structure for the completion (DetectedPort -> Port) return f.Map(list.GetPorts(), (*rpc.DetectedPort).GetPort) } + +// GetProfileLibraries is an helper function useful to autocomplete. +// It returns a list of libraries present in the specified profile. +func GetProfileLibraries(ctx context.Context, srv rpc.ArduinoCoreServiceServer, sketchPath *paths.Path, profile string) []string { + resp, err := srv.ProfileLibList(ctx, &rpc.ProfileLibListRequest{ + SketchPath: sketchPath.String(), + ProfileName: profile, + }) + if err != nil { + return nil + } + res := f.Map(resp.GetLibraries(), func(lib *rpc.ProfileLibraryReference) string { + return lib.GetIndexLibrary().GetName() + }) + return res +} diff --git a/internal/cli/profile/profile_lib_remove.go b/internal/cli/profile/profile_lib_remove.go index 08ea0c3b725..2b1536ae4c5 100644 --- a/internal/cli/profile/profile_lib_remove.go +++ b/internal/cli/profile/profile_lib_remove.go @@ -46,7 +46,10 @@ func initLibRemoveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { runLibRemoveCommand(cmd.Context(), srv, args, profileArg.Get(), destDir, noDeps) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return arguments.GetInstallableLibs(cmd.Context(), srv), cobra.ShellCompDirectiveDefault + ctx := cmd.Context() + sketchPath := arguments.InitSketchPath(destDir) + completions := arguments.GetProfileLibraries(ctx, srv, sketchPath, profileArg.Get()) + return completions, cobra.ShellCompDirectiveNoFileComp }, } profileArg.AddToCommand(removeCommand, srv) From 99c8c886d252f16bf081723096560d978a2364f0 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 5 Dec 2025 15:57:37 +0100 Subject: [PATCH 07/10] Rename 'profile init' -> 'profile create' --- ...file_init.go => service_profile_create.go} | 0 .../{profile_init.go => profile_create.go} | 23 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) rename commands/{service_profile_init.go => service_profile_create.go} (100%) rename internal/cli/profile/{profile_init.go => profile_create.go} (75%) diff --git a/commands/service_profile_init.go b/commands/service_profile_create.go similarity index 100% rename from commands/service_profile_init.go rename to commands/service_profile_create.go diff --git a/internal/cli/profile/profile_init.go b/internal/cli/profile/profile_create.go similarity index 75% rename from internal/cli/profile/profile_init.go rename to internal/cli/profile/profile_create.go index e7e2e7c3b3d..173db9d8db6 100644 --- a/internal/cli/profile/profile_init.go +++ b/internal/cli/profile/profile_create.go @@ -32,26 +32,25 @@ func initProfileCreateCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var setAsDefault bool var fqbnArg arguments.Fqbn var profileArg arguments.Profile - initCommand := &cobra.Command{ - Use: "init", - Short: i18n.Tr("Create or update the sketch project file."), - Long: i18n.Tr("Create or update the sketch project file."), + createCommand := &cobra.Command{ + Use: "create", + Short: i18n.Tr("Create or update a profile in the sketch project file."), Example: "" + " # " + i18n.Tr("Creates or updates the sketch project file in the current directory.") + "\n" + - " " + os.Args[0] + " profile init\n" + - " " + os.Args[0] + " profile init --profile uno_profile -b arduino:avr:uno", + " " + os.Args[0] + " profile create\n" + + " " + os.Args[0] + " profile create --profile uno_profile -b arduino:avr:uno", Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runInitCommand(cmd.Context(), args, srv, profileArg.Get(), fqbnArg.String(), setAsDefault) + runProfileCreateCommand(cmd.Context(), args, srv, profileArg.Get(), fqbnArg.String(), setAsDefault) }, } - fqbnArg.AddToCommand(initCommand, srv) - profileArg.AddToCommand(initCommand, srv) - initCommand.Flags().BoolVar(&setAsDefault, "set-default", false, i18n.Tr("Set the profile as the default one.")) - return initCommand + fqbnArg.AddToCommand(createCommand, srv) + profileArg.AddToCommand(createCommand, srv) + createCommand.Flags().BoolVar(&setAsDefault, "set-default", false, i18n.Tr("Set the profile as the default one.")) + return createCommand } -func runInitCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, profile, fqbn string, setAsDefault bool) { +func runProfileCreateCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, profile, fqbn string, setAsDefault bool) { path := "" if len(args) > 0 { path = args[0] From a929e026d4743dd64fdd72c61e780b079c643053 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 5 Dec 2025 16:09:57 +0100 Subject: [PATCH 08/10] Proper behaviour for 'profile create' command --- commands/service_profile_create.go | 97 ++++++++++++-------------- internal/cli/profile/profile_create.go | 7 +- 2 files changed, 50 insertions(+), 54 deletions(-) diff --git a/commands/service_profile_create.go b/commands/service_profile_create.go index ae3d9e9f6e1..321081cd27e 100644 --- a/commands/service_profile_create.go +++ b/commands/service_profile_create.go @@ -32,72 +32,67 @@ import ( // ProfileCreate creates a new project file if it does not exist. If a profile name with the associated FQBN is specified, // it is added to the project. func (s *arduinoCoreServerImpl) ProfileCreate(ctx context.Context, req *rpc.ProfileCreateRequest) (*rpc.ProfileCreateResponse, error) { + if req.GetProfileName() == "" { + return nil, &cmderrors.MissingProfileError{} + } + if req.GetFqbn() == "" { + return nil, &cmderrors.MissingFQBNError{} + } + // Returns an error if the main file is missing from the sketch so there is no need to check if the path exists sk, err := sketch.New(paths.New(req.GetSketchPath())) if err != nil { return nil, err } - projectFilePath := sk.GetProjectPath() - if !projectFilePath.Exist() { - err := projectFilePath.WriteFile([]byte("profiles: {}\n")) - if err != nil { - return nil, err - } + fqbn, err := fqbn.Parse(req.GetFqbn()) + if err != nil { + return nil, &cmderrors.InvalidFQBNError{Cause: err} } - if req.GetProfileName() != "" { - if req.GetFqbn() == "" { - return nil, &cmderrors.MissingFQBNError{} - } - fqbn, err := fqbn.Parse(req.GetFqbn()) - if err != nil { - return nil, &cmderrors.InvalidFQBNError{Cause: err} - } - - // Check that the profile name is unique - if profile, _ := sk.GetProfile(req.ProfileName); profile != nil { - return nil, &cmderrors.ProfileAlreadyExitsError{Profile: req.ProfileName} - } + // Check that the profile name is unique + if profile, _ := sk.GetProfile(req.ProfileName); profile != nil { + return nil, &cmderrors.ProfileAlreadyExitsError{Profile: req.ProfileName} + } - pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) - if err != nil { - return nil, err - } - defer release() - if pme.Dirty() { - return nil, &cmderrors.InstanceNeedsReinitialization{} - } + pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) + if err != nil { + return nil, err + } + defer release() + if pme.Dirty() { + return nil, &cmderrors.InstanceNeedsReinitialization{} + } - // Automatically detect the target platform if it is installed on the user's machine - _, targetPlatform, _, _, _, err := pme.ResolveFQBN(fqbn) - if err != nil { - if targetPlatform == nil { - return nil, &cmderrors.PlatformNotFoundError{ - Platform: fmt.Sprintf("%s:%s", fqbn.Vendor, fqbn.Architecture), - Cause: errors.New(i18n.Tr("platform not installed")), - } + // Automatically detect the target platform if it is installed on the user's machine + _, targetPlatform, _, _, _, err := pme.ResolveFQBN(fqbn) + if err != nil { + if targetPlatform == nil { + return nil, &cmderrors.PlatformNotFoundError{ + Platform: fmt.Sprintf("%s:%s", fqbn.Vendor, fqbn.Architecture), + Cause: errors.New(i18n.Tr("platform not installed")), } - return nil, &cmderrors.InvalidFQBNError{Cause: err} } + return nil, &cmderrors.InvalidFQBNError{Cause: err} + } - newProfile := &sketch.Profile{Name: req.GetProfileName(), FQBN: req.GetFqbn()} - // TODO: what to do with the PlatformIndexURL? - newProfile.Platforms = append(newProfile.Platforms, &sketch.ProfilePlatformReference{ - Packager: targetPlatform.Platform.Package.Name, - Architecture: targetPlatform.Platform.Architecture, - Version: targetPlatform.Version, - }) + newProfile := &sketch.Profile{Name: req.GetProfileName(), FQBN: req.GetFqbn()} + // TODO: what to do with the PlatformIndexURL? + newProfile.Platforms = append(newProfile.Platforms, &sketch.ProfilePlatformReference{ + Packager: targetPlatform.Platform.Package.Name, + Architecture: targetPlatform.Platform.Architecture, + Version: targetPlatform.Version, + }) - sk.Project.Profiles = append(sk.Project.Profiles, newProfile) - if req.DefaultProfile { - sk.Project.DefaultProfile = newProfile.Name - } + sk.Project.Profiles = append(sk.Project.Profiles, newProfile) + if req.DefaultProfile { + sk.Project.DefaultProfile = newProfile.Name + } - err = projectFilePath.WriteFile([]byte(sk.Project.AsYaml())) - if err != nil { - return nil, err - } + projectFilePath := sk.GetProjectPath() + err = projectFilePath.WriteFile([]byte(sk.Project.AsYaml())) + if err != nil { + return nil, err } return &rpc.ProfileCreateResponse{}, nil diff --git a/internal/cli/profile/profile_create.go b/internal/cli/profile/profile_create.go index 173db9d8db6..3457f0818af 100644 --- a/internal/cli/profile/profile_create.go +++ b/internal/cli/profile/profile_create.go @@ -33,12 +33,11 @@ func initProfileCreateCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var fqbnArg arguments.Fqbn var profileArg arguments.Profile createCommand := &cobra.Command{ - Use: "create", + Use: "create --profile --fqbn [flags] []", Short: i18n.Tr("Create or update a profile in the sketch project file."), Example: "" + " # " + i18n.Tr("Creates or updates the sketch project file in the current directory.") + "\n" + - " " + os.Args[0] + " profile create\n" + - " " + os.Args[0] + " profile create --profile uno_profile -b arduino:avr:uno", + " " + os.Args[0] + " profile create -m uno -b arduino:avr:uno", Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { runProfileCreateCommand(cmd.Context(), args, srv, profileArg.Get(), fqbnArg.String(), setAsDefault) @@ -46,6 +45,8 @@ func initProfileCreateCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { } fqbnArg.AddToCommand(createCommand, srv) profileArg.AddToCommand(createCommand, srv) + createCommand.MarkFlagRequired("profile") + createCommand.MarkFlagRequired("fqbn") createCommand.Flags().BoolVar(&setAsDefault, "set-default", false, i18n.Tr("Set the profile as the default one.")) return createCommand } From 71b0902c379d87fc9eddfacbe0c175a5f243eead Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 5 Dec 2025 15:13:35 +0100 Subject: [PATCH 09/10] Added profile management integration tests --- internal/cli/profile/profile_lib_add.go | 2 +- .../integrationtest/profiles/profiles_test.go | 241 ++++++++++++++++++ 2 files changed, 242 insertions(+), 1 deletion(-) diff --git a/internal/cli/profile/profile_lib_add.go b/internal/cli/profile/profile_lib_add.go index 06f01f2d4db..1307d209cf6 100644 --- a/internal/cli/profile/profile_lib_add.go +++ b/internal/cli/profile/profile_lib_add.go @@ -83,7 +83,7 @@ func runLibAddCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreSer NoOverwrite: &noOverwrite, }) if err != nil { - feedback.Fatal(i18n.Tr("Error adding %s to the profile %s: %v", lib.Name, profile, err), feedback.ErrGeneric) + feedback.Fatal(i18n.Tr("Error adding %s: %v", lib.Name, err), feedback.ErrGeneric) } feedback.PrintResult(libAddResult{ AddedLibraries: f.Map(resp.GetAddedLibraries(), result.NewProfileLibraryReference), diff --git a/internal/integrationtest/profiles/profiles_test.go b/internal/integrationtest/profiles/profiles_test.go index aafd914db2c..814cd2c1f06 100644 --- a/internal/integrationtest/profiles/profiles_test.go +++ b/internal/integrationtest/profiles/profiles_test.go @@ -19,6 +19,7 @@ import ( "testing" "github.com/arduino/arduino-cli/internal/integrationtest" + "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" "go.bug.st/testifyjson/requirejson" ) @@ -150,3 +151,243 @@ func TestCompileWithDefaultProfile(t *testing.T) { jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:nano" ]`) } } + +func createTempSketch(t *testing.T, cli *integrationtest.ArduinoCLI, sketchName string) *paths.Path { + sketchDir := cli.SketchbookDir().Join(sketchName) + t.Cleanup(func() { require.NoError(t, sketchDir.RemoveAll()) }) + _, _, err := cli.Run("sketch", "new", sketchDir.String()) + require.NoError(t, err) + return sketchDir +} + +func TestProfileCreate(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + // Init the environment explicitly + _, _, err := cli.Run("core", "update-index") + require.NoError(t, err) + _, _, err = cli.Run("core", "install", "arduino:avr@1.8.6") + require.NoError(t, err) + + t.Run("WithInvalidSketchDir", func(t *testing.T) { + invalidSketchDir := cli.SketchbookDir().Join("tempSketch") + + _, stderr, err := cli.Run("profile", "create", invalidSketchDir.String(), "-m", "test", "-b", "arduino:avr:uno") + require.Error(t, err) + require.Contains(t, string(stderr), "no such file or directory") + + require.NoError(t, invalidSketchDir.MkdirAll()) + t.Cleanup(func() { require.NoError(t, invalidSketchDir.RemoveAll()) }) + + _, stderr, err = cli.Run("profile", "create", invalidSketchDir.String(), "-m", "test", "-b", "arduino:avr:uno") + require.Error(t, err) + require.Contains(t, string(stderr), "main file missing from sketch") + }) + + t.Run("WithNotInstalledPlatform", func(t *testing.T) { + sketchDir := createTempSketch(t, cli, "TestSketch") + _, stderr, err := cli.Run("profile", "create", sketchDir.String(), "-m", "uno", "-b", "arduino:samd:zero") + require.Error(t, err) + require.Contains(t, string(stderr), "platform not installed") + }) + + t.Run("WithoutSketchYAML", func(t *testing.T) { + sketchDir := createTempSketch(t, cli, "TestSketch") + projectFile := sketchDir.Join("sketch.yaml") + + stdout, _, err := cli.Run("profile", "create", sketchDir.String(), "-m", "test", "-b", "arduino:avr:uno") + require.NoError(t, err) + require.Contains(t, string(stdout), "Project file created in: "+projectFile.String()) + require.FileExists(t, projectFile.String()) + fileContent, err := projectFile.ReadFile() + require.NoError(t, err) + require.Contains(t, string(fileContent), "profiles:\n test:\n") + + t.Run("AddNewProfile", func(t *testing.T) { + // Add a new profile + _, _, err := cli.Run("profile", "create", sketchDir.String(), "-m", "uno", "-b", "arduino:avr:uno") + require.NoError(t, err) + fileContent, err := projectFile.ReadFile() + require.NoError(t, err) + require.Contains(t, string(fileContent), " uno:\n fqbn: arduino:avr:uno\n platforms:\n - platform: arduino:avr (1.8.6)\n") + require.NotContains(t, string(fileContent), "default_profile: uno") + }) + + t.Run("AddAndSetDefaultProfile", func(t *testing.T) { + // Add a new profile and set it as default + _, _, err := cli.Run("profile", "create", sketchDir.String(), "-m", "leonardo", "-b", "arduino:avr:leonardo", "--set-default") + require.NoError(t, err) + fileContent, err := projectFile.ReadFile() + require.NoError(t, err) + require.Contains(t, string(fileContent), " leonardo:\n fqbn: arduino:avr:leonardo\n platforms:\n - platform: arduino:avr (1.8.6)\n") + require.Contains(t, string(fileContent), "default_profile: leonardo") + }) + + t.Run("WrongFQBN", func(t *testing.T) { + // Adding a profile with an incorrect FQBN should return an error + _, stderr, err := cli.Run("profile", "create", sketchDir.String(), "-m", "wrong_fqbn", "-b", "foo:bar") + require.Error(t, err) + require.Contains(t, string(stderr), "Invalid FQBN") + }) + + t.Run("MissingFQBN", func(t *testing.T) { + // Add a profile with no FQBN should return an error + _, _, err := cli.Run("profile", "create", sketchDir.String(), "-m", "Uno") + require.Error(t, err) + }) + + t.Run("AlreadyExistingProfile", func(t *testing.T) { + // Adding a profile with a name that already exists should return an error + _, stderr, err := cli.Run("profile", "create", sketchDir.String(), "-m", "uno", "-b", "arduino:avr:uno") + require.Error(t, err) + require.Contains(t, string(stderr), "Profile 'uno' already exists") + }) + }) +} + +func TestProfileLib(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + // Init the environment explicitly + _, _, err := cli.Run("core", "update-index") + require.NoError(t, err) + _, _, err = cli.Run("core", "install", "arduino:avr") + require.NoError(t, err) + + t.Run("AddLibToDefaultProfile", func(t *testing.T) { + sk := createTempSketch(t, cli, "AddLibSketch") + + _, _, err = cli.Run("profile", "create", sk.String(), "-m", "uno", "-b", "arduino:avr:uno", "--set-default") + require.NoError(t, err) + + out, _, err := cli.Run("profile", "lib", "add", "Arduino_Modulino@0.7.0", "--dest-dir", sk.String(), "--json") + require.NoError(t, err) + requirejson.Parse(t, out).Query(".added_libraries").MustContain(` + [ + { "kind": "index", + "library": {"name": "Arduino_Modulino", "version": "0.7.0" } + } + ]`) + + fileContent, err := sk.Join("sketch.yaml").ReadFile() + require.NoError(t, err) + require.Contains(t, string(fileContent), " - Arduino_Modulino (0.7.0)\n") + // dependency added as well + require.Contains(t, string(fileContent), " - dependency: Arduino_LSM6DSOX (") + + t.Run("ChangeLibVersionToDefaultProfile", func(t *testing.T) { + out, _, err := cli.Run("profile", "lib", "add", "Arduino_Modulino@0.6.0", "--dest-dir", sk.String(), "--json") + require.NoError(t, err) + outjson := requirejson.Parse(t, out) + outjson.Query(".added_libraries").MustContain(` + [ + { "kind": "index", + "library": {"name": "Arduino_Modulino", "version": "0.6.0"} + } + ]`) + outjson.Query(".skipped_libraries").MustContain(` + [ + { "kind": "index", + "library": {"name":"Arduino_LSM6DSOX"} + } + ]`) + + fileContent, err := sk.Join("sketch.yaml").ReadFile() + require.NoError(t, err) + require.Contains(t, string(fileContent), " - Arduino_Modulino (0.6.0)\n") + }) + + t.Run("RemoveLibFromDefaultProfile", func(t *testing.T) { + _, _, err = cli.Run("profile", "lib", "remove", "Arduino_Modulino", "--dest-dir", sk.String()) + require.NoError(t, err) + fileContent, err := sk.Join("sketch.yaml").ReadFile() + require.NoError(t, err) + require.NotContains(t, string(fileContent), "Arduino_Modulino") + // dependency removed as well + require.NotContains(t, string(fileContent), "Arduino_LSM6DSOX") + }) + + t.Run("AddInexistentLibToDefaultProfile", func(t *testing.T) { + _, stderr, err := cli.Run("profile", "lib", "add", "foobar123", "--dest-dir", sk.String()) + require.Error(t, err) + require.Equal(t, "Error adding foobar123: Library 'foobar123@latest' not found\n", string(stderr)) + }) + + t.Run("RemoveLibNotInProfile", func(t *testing.T) { + _, stderr, err := cli.Run("profile", "lib", "remove", "Arduino_JSON", "--dest-dir", sk.String()) + require.Error(t, err) + require.Equal(t, "Error removing library Arduino_JSON from the profile: could not remove library: Library 'Arduino_JSON' not found\n", string(stderr)) + }) + }) + +} + +func TestProfileLibAddRemoveFromSpecificProfile(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + // Init the environment explicitly + _, _, err := cli.Run("core", "update-index") + require.NoError(t, err) + _, _, err = cli.Run("core", "install", "arduino:avr") + require.NoError(t, err) + sk := createTempSketch(t, cli, "Simple") + + _, _, err = cli.Run("profile", "create", sk.String(), "-m", "uno", "-b", "arduino:avr:uno") + require.NoError(t, err) + // Add a second profile + _, _, err = cli.Run("profile", "create", sk.String(), "-m", "my_profile", "-b", "arduino:avr:uno") + require.NoError(t, err) + + // Add library to a specific profile + _, _, err = cli.Run("profile", "lib", "add", "Arduino_Modulino@0.7.0", "-m", "my_profile", "--dest-dir", sk.String(), "--no-deps") + require.NoError(t, err) + fileContent, err := sk.Join("sketch.yaml").ReadFile() + require.NoError(t, err) + require.Contains(t, string(fileContent), " my_profile:\n fqbn: arduino:avr:uno\n platforms:\n - platform: arduino:avr (1.8.6)\n libraries:\n - Arduino_Modulino (0.7.0)\n") + + // Remove library from a specific profile + _, _, err = cli.Run("profile", "lib", "remove", "Arduino_Modulino", "-m", "my_profile", "--dest-dir", sk.String()) + require.NoError(t, err) + fileContent, err = sk.Join("sketch.yaml").ReadFile() + require.NoError(t, err) + require.NotContains(t, string(fileContent), "Arduino_Modulino") +} + +func TestProfileSetDefault(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + // Init the environment explicitly + _, _, err := cli.Run("core", "update-index") + require.NoError(t, err) + _, _, err = cli.Run("core", "install", "arduino:avr") + require.NoError(t, err) + sk := createTempSketch(t, cli, "Simple") + + // Create two profiles and set both as default (the second one should override the first) + _, _, err = cli.Run("profile", "create", sk.String(), "-m", "my_profile", "-b", "arduino:avr:uno", "--set-default") + require.NoError(t, err) + _, _, err = cli.Run("profile", "create", sk.String(), "-m", "uno", "-b", "arduino:avr:uno", "--set-default") + require.NoError(t, err) + + fileContent, err := sk.Join("sketch.yaml").ReadFileAsLines() + require.NoError(t, err) + require.Contains(t, fileContent, "default_profile: uno") + require.NotContains(t, fileContent, "default_profile: my_profile") + + // Change default profile + _, _, err = cli.Run("profile", "set-default", "my_profile", "--dest-dir", sk.String()) + require.NoError(t, err) + fileContent, err = sk.Join("sketch.yaml").ReadFileAsLines() + require.NoError(t, err) + require.NotContains(t, fileContent, "default_profile: uno") + require.Contains(t, fileContent, "default_profile: my_profile") + + // Changing to an inexistent profile returns an error + _, stderr, err := cli.Run("profile", "set-default", "inexistent_profile", "--dest-dir", sk.String()) + require.Error(t, err) + require.Equal(t, "Cannot set inexistent_profile as default profile: Profile 'inexistent_profile' not found\n", string(stderr)) +} From 290e0e150a9db36824c431a189502c868ed5374e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 5 Dec 2025 18:03:59 +0100 Subject: [PATCH 10/10] Rename --dest-dir to --sketch-path --- internal/cli/profile/profile_lib_add.go | 10 +++++----- internal/cli/profile/profile_lib_remove.go | 12 ++++++------ internal/cli/profile/profile_set-default.go | 10 +++++----- .../integrationtest/profiles/profiles_test.go | 18 +++++++++--------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/internal/cli/profile/profile_lib_add.go b/internal/cli/profile/profile_lib_add.go index 1307d209cf6..5687dfc0a87 100644 --- a/internal/cli/profile/profile_lib_add.go +++ b/internal/cli/profile/profile_lib_add.go @@ -32,7 +32,7 @@ import ( ) func initLibAddCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { - var destDir string + var sketchDir string var noDeps bool var noOverwrite bool var profileArg arguments.Profile @@ -44,21 +44,21 @@ func initLibAddCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { " " + os.Args[0] + " profile lib add Arduino_JSON@0.2.0 --profile my_profile\n", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runLibAddCommand(cmd.Context(), args, srv, profileArg.Get(), destDir, noDeps, noOverwrite) + runLibAddCommand(cmd.Context(), args, srv, profileArg.Get(), sketchDir, noDeps, noOverwrite) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return arguments.GetInstallableLibs(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } profileArg.AddToCommand(addCommand, srv) - addCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) + addCommand.Flags().StringVar(&sketchDir, "sketch-path", "", i18n.Tr("Location of the sketch.")) addCommand.Flags().BoolVar(&noDeps, "no-deps", false, i18n.Tr("Do not add dependencies.")) addCommand.Flags().BoolVar(&noOverwrite, "no-overwrite", false, i18n.Tr("Do not overwrite already added libraries.")) return addCommand } -func runLibAddCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, profile, destDir string, noAddDeps, noOverwrite bool) { - sketchPath := arguments.InitSketchPath(destDir) +func runLibAddCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, profile, sketchDir string, noAddDeps, noOverwrite bool) { + sketchPath := arguments.InitSketchPath(sketchDir) instance := instance.CreateAndInit(ctx, srv) libRefs, err := lib.ParseLibraryReferenceArgsAndAdjustCase(ctx, srv, instance, args) diff --git a/internal/cli/profile/profile_lib_remove.go b/internal/cli/profile/profile_lib_remove.go index 2b1536ae4c5..82cf41c8c71 100644 --- a/internal/cli/profile/profile_lib_remove.go +++ b/internal/cli/profile/profile_lib_remove.go @@ -32,7 +32,7 @@ import ( ) func initLibRemoveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { - var destDir string + var sketchDir string var profileArg arguments.Profile var noDeps bool removeCommand := &cobra.Command{ @@ -43,23 +43,23 @@ func initLibRemoveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { " " + os.Args[0] + " profile lib remove Arduino_JSON@0.2.0 --profile my_profile\n", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runLibRemoveCommand(cmd.Context(), srv, args, profileArg.Get(), destDir, noDeps) + runLibRemoveCommand(cmd.Context(), srv, args, profileArg.Get(), sketchDir, noDeps) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ctx := cmd.Context() - sketchPath := arguments.InitSketchPath(destDir) + sketchPath := arguments.InitSketchPath(sketchDir) completions := arguments.GetProfileLibraries(ctx, srv, sketchPath, profileArg.Get()) return completions, cobra.ShellCompDirectiveNoFileComp }, } profileArg.AddToCommand(removeCommand, srv) - removeCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) + removeCommand.Flags().StringVar(&sketchDir, "sketch-path", "", i18n.Tr("Location of the sketch.")) removeCommand.Flags().BoolVar(&noDeps, "no-deps", false, i18n.Tr("Do not remove unused dependencies.")) return removeCommand } -func runLibRemoveCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, profile, destDir string, noDeps bool) { - sketchPath := arguments.InitSketchPath(destDir) +func runLibRemoveCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, profile, sketchDir string, noDeps bool) { + sketchPath := arguments.InitSketchPath(sketchDir) instance := instance.CreateAndInit(ctx, srv) libRefs, err := lib.ParseLibraryReferenceArgsAndAdjustCase(ctx, srv, instance, args) diff --git a/internal/cli/profile/profile_set-default.go b/internal/cli/profile/profile_set-default.go index 6e5034d9cef..e78ec48bd1b 100644 --- a/internal/cli/profile/profile_set-default.go +++ b/internal/cli/profile/profile_set-default.go @@ -27,7 +27,7 @@ import ( ) func initProfileSetDefaultCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { - var destDir string + var sketchDir string setDefaultCommand := &cobra.Command{ Use: "set-default", Short: i18n.Tr("Set the default build profile."), @@ -36,16 +36,16 @@ func initProfileSetDefaultCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Comma " " + os.Args[0] + " profile set-default my_profile\n", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - runSetDefaultCommand(cmd.Context(), args, srv, destDir) + runSetDefaultCommand(cmd.Context(), args, srv, sketchDir) }, } - setDefaultCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the sketch project file.")) + setDefaultCommand.Flags().StringVar(&sketchDir, "sketch-path", "", i18n.Tr("Location of the sketch.")) return setDefaultCommand } -func runSetDefaultCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, destDir string) { +func runSetDefaultCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, sketchDir string) { profileName := args[0] - sketchPath := arguments.InitSketchPath(destDir) + sketchPath := arguments.InitSketchPath(sketchDir) _, err := srv.ProfileSetDefault(ctx, &rpc.ProfileSetDefaultRequest{SketchPath: sketchPath.String(), ProfileName: profileName}) if err != nil { diff --git a/internal/integrationtest/profiles/profiles_test.go b/internal/integrationtest/profiles/profiles_test.go index 814cd2c1f06..6a9c8deb1fa 100644 --- a/internal/integrationtest/profiles/profiles_test.go +++ b/internal/integrationtest/profiles/profiles_test.go @@ -262,7 +262,7 @@ func TestProfileLib(t *testing.T) { _, _, err = cli.Run("profile", "create", sk.String(), "-m", "uno", "-b", "arduino:avr:uno", "--set-default") require.NoError(t, err) - out, _, err := cli.Run("profile", "lib", "add", "Arduino_Modulino@0.7.0", "--dest-dir", sk.String(), "--json") + out, _, err := cli.Run("profile", "lib", "add", "Arduino_Modulino@0.7.0", "--sketch-path", sk.String(), "--json") require.NoError(t, err) requirejson.Parse(t, out).Query(".added_libraries").MustContain(` [ @@ -278,7 +278,7 @@ func TestProfileLib(t *testing.T) { require.Contains(t, string(fileContent), " - dependency: Arduino_LSM6DSOX (") t.Run("ChangeLibVersionToDefaultProfile", func(t *testing.T) { - out, _, err := cli.Run("profile", "lib", "add", "Arduino_Modulino@0.6.0", "--dest-dir", sk.String(), "--json") + out, _, err := cli.Run("profile", "lib", "add", "Arduino_Modulino@0.6.0", "--sketch-path", sk.String(), "--json") require.NoError(t, err) outjson := requirejson.Parse(t, out) outjson.Query(".added_libraries").MustContain(` @@ -300,7 +300,7 @@ func TestProfileLib(t *testing.T) { }) t.Run("RemoveLibFromDefaultProfile", func(t *testing.T) { - _, _, err = cli.Run("profile", "lib", "remove", "Arduino_Modulino", "--dest-dir", sk.String()) + _, _, err = cli.Run("profile", "lib", "remove", "Arduino_Modulino", "--sketch-path", sk.String()) require.NoError(t, err) fileContent, err := sk.Join("sketch.yaml").ReadFile() require.NoError(t, err) @@ -310,13 +310,13 @@ func TestProfileLib(t *testing.T) { }) t.Run("AddInexistentLibToDefaultProfile", func(t *testing.T) { - _, stderr, err := cli.Run("profile", "lib", "add", "foobar123", "--dest-dir", sk.String()) + _, stderr, err := cli.Run("profile", "lib", "add", "foobar123", "--sketch-path", sk.String()) require.Error(t, err) require.Equal(t, "Error adding foobar123: Library 'foobar123@latest' not found\n", string(stderr)) }) t.Run("RemoveLibNotInProfile", func(t *testing.T) { - _, stderr, err := cli.Run("profile", "lib", "remove", "Arduino_JSON", "--dest-dir", sk.String()) + _, stderr, err := cli.Run("profile", "lib", "remove", "Arduino_JSON", "--sketch-path", sk.String()) require.Error(t, err) require.Equal(t, "Error removing library Arduino_JSON from the profile: could not remove library: Library 'Arduino_JSON' not found\n", string(stderr)) }) @@ -342,14 +342,14 @@ func TestProfileLibAddRemoveFromSpecificProfile(t *testing.T) { require.NoError(t, err) // Add library to a specific profile - _, _, err = cli.Run("profile", "lib", "add", "Arduino_Modulino@0.7.0", "-m", "my_profile", "--dest-dir", sk.String(), "--no-deps") + _, _, err = cli.Run("profile", "lib", "add", "Arduino_Modulino@0.7.0", "-m", "my_profile", "--sketch-path", sk.String(), "--no-deps") require.NoError(t, err) fileContent, err := sk.Join("sketch.yaml").ReadFile() require.NoError(t, err) require.Contains(t, string(fileContent), " my_profile:\n fqbn: arduino:avr:uno\n platforms:\n - platform: arduino:avr (1.8.6)\n libraries:\n - Arduino_Modulino (0.7.0)\n") // Remove library from a specific profile - _, _, err = cli.Run("profile", "lib", "remove", "Arduino_Modulino", "-m", "my_profile", "--dest-dir", sk.String()) + _, _, err = cli.Run("profile", "lib", "remove", "Arduino_Modulino", "-m", "my_profile", "--sketch-path", sk.String()) require.NoError(t, err) fileContent, err = sk.Join("sketch.yaml").ReadFile() require.NoError(t, err) @@ -379,7 +379,7 @@ func TestProfileSetDefault(t *testing.T) { require.NotContains(t, fileContent, "default_profile: my_profile") // Change default profile - _, _, err = cli.Run("profile", "set-default", "my_profile", "--dest-dir", sk.String()) + _, _, err = cli.Run("profile", "set-default", "my_profile", "--sketch-path", sk.String()) require.NoError(t, err) fileContent, err = sk.Join("sketch.yaml").ReadFileAsLines() require.NoError(t, err) @@ -387,7 +387,7 @@ func TestProfileSetDefault(t *testing.T) { require.Contains(t, fileContent, "default_profile: my_profile") // Changing to an inexistent profile returns an error - _, stderr, err := cli.Run("profile", "set-default", "inexistent_profile", "--dest-dir", sk.String()) + _, stderr, err := cli.Run("profile", "set-default", "inexistent_profile", "--sketch-path", sk.String()) require.Error(t, err) require.Equal(t, "Cannot set inexistent_profile as default profile: Profile 'inexistent_profile' not found\n", string(stderr)) }