Skip to content

Add first set of profile commands #2917

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Prev Previous commit
Next Next commit
Add profile lib remove command
It removes a library from the specified profile.
  • Loading branch information
MatteoPologruto committed May 22, 2025
commit f98bf7dd930e02440a943847c71ef89415bbde7e
2 changes: 1 addition & 1 deletion commands/service_profile_lib_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (s *arduinoCoreServerImpl) ProfileLibAdd(ctx context.Context, req *rpc.Prof
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will improve usability if this operation also adds all the dependencies of that library; otherwise, the user will need to enumerate all of them one by one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concern I have with this proposal is that the dependencies specified in the library metadata are not necessarily required to compile a specific sketch.

Dependencies of a library may be required only for specific use cases that are not applicable to a given sketch project.

The library author may declare dependencies because they are a dependency of one of the library's example sketches, even though they are not a dependency of the library itself.

In this case, the unnecessary dependencies would pollute the environment generated by the build profile, which could result in problems caused by library discovery results other than what was intended by the user.

So I would recommend adding a flag that allows the user to control this behavior, similar to what was done for lib install:

https://arduino.github.io/arduino-cli/dev/commands/arduino-cli_lib_install/#options

--no-deps                  Do not install dependencies.


// If the library has been already added to the profile, just update the version
if lib, _ := profile.GetLibrary(req.LibName); lib != nil {
if lib, _ := profile.GetLibrary(req.LibName, false); lib != nil {
lib.Version = libRelease.GetVersion()
} else {
profile.Libraries = append(profile.Libraries, &sketch.ProfileLibraryReference{
Expand Down
60 changes: 60 additions & 0 deletions commands/service_profile_lib_remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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 commands

import (
"context"

"github.com/arduino/arduino-cli/commands/cmderrors"
"github.com/arduino/arduino-cli/internal/arduino/sketch"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
paths "github.com/arduino/go-paths-helper"
)

func (s *arduinoCoreServerImpl) ProfileLibRemove(ctx context.Context, req *rpc.ProfileLibRemoveRequest) (*rpc.ProfileLibRemoveResponse, error) {
sketchPath := paths.New(req.GetSketchPath())
projectFilePath, err := sketchPath.Join("sketch.yaml").Abs()
if err != nil {
return nil, err
}

if req.GetProfileName() == "" {
return nil, &cmderrors.MissingProfileError{}
}

// 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(sketchPath)
if err != nil {
return nil, err
}

profile, err := sk.GetProfile(req.ProfileName)
if err != nil {
return nil, err
}

lib, err := profile.GetLibrary(req.LibName, true)
if err != nil {
return nil, err
}

err = projectFilePath.WriteFile([]byte(sk.Project.AsYaml()))
if err != nil {
return nil, err
}

return &rpc.ProfileLibRemoveResponse{LibName: lib.Library, LibVersion: lib.Version.String()}, nil
}
8 changes: 6 additions & 2 deletions internal/arduino/sketch/profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"net/url"
"regexp"
"slices"
"strings"

"github.com/arduino/arduino-cli/commands/cmderrors"
Expand Down Expand Up @@ -121,9 +122,12 @@ type Profile struct {
}

// GetLibrary returns the requested library or an error if not found
func (p *Profile) GetLibrary(libraryName string) (*ProfileLibraryReference, error) {
for _, l := range p.Libraries {
func (p *Profile) GetLibrary(libraryName string, toDelete bool) (*ProfileLibraryReference, error) {
for i, l := range p.Libraries {
if l.Library == libraryName {
if toDelete {
p.Libraries = slices.Delete(p.Libraries, i, i+1)
}
return l, nil
}
}
Expand Down
71 changes: 66 additions & 5 deletions internal/cli/profile/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func initLibCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command {
}

libCommand.AddCommand(initLibAddCommand(srv))
libCommand.AddCommand(initLibRemoveCommand(srv))

return libCommand
}
Expand Down Expand Up @@ -88,20 +89,80 @@ func runLibAddCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreSer
if err != nil {
feedback.Fatal(i18n.Tr("Error adding %s to the profile %s: %v", lib.Name, profileArg.Get(), err), feedback.ErrGeneric)
}
feedback.PrintResult(libResult{LibName: resp.GetLibName(), LibVersion: resp.GetLibVersion(), ProfileName: profileArg.Get()})
feedback.PrintResult(libAddResult{LibName: resp.GetLibName(), LibVersion: resp.GetLibVersion(), ProfileName: profileArg.Get()})
}
}

type libResult struct {
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 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(),
LibName: lib.Name,
})
if err != nil {
feedback.Fatal(i18n.Tr("Error removing %s from the profile %s: %v", lib.Name, profileArg.Get(), err), feedback.ErrGeneric)
}
feedback.PrintResult(libRemoveResult{LibName: resp.GetLibName(), LibVersion: resp.GetLibVersion(), ProfileName: profileArg.Get()})
}
}

type libAddResult struct {
LibName string `json:"library_name"`
LibVersion string `json:"library_version"`
ProfileName string `json:"profile_name"`
}

func (lr libAddResult) Data() interface{} {
return lr
}

func (lr libAddResult) String() string {
return i18n.Tr("Profile %s: %s@%s added successfully", lr.ProfileName, lr.LibName, lr.LibVersion)
}

type libRemoveResult struct {
LibName string `json:"library_name"`
LibVersion string `json:"library_version"`
ProfileName string `json:"profile_name"`
}

func (lr libResult) Data() interface{} {
func (lr libRemoveResult) Data() interface{} {
return lr
}

func (lr libResult) String() string {
return i18n.Tr("Profile %s: %s@%s added succesfully", lr.ProfileName, lr.LibName, lr.LibVersion)
func (lr libRemoveResult) String() string {
return i18n.Tr("Profile %s: %s@%s removed successfully", lr.ProfileName, lr.LibName, lr.LibVersion)
}
Loading