Skip to content

Commit d3d7a59

Browse files
Add profile lib add command
It adds one or multiple libraries to the specified profile.
1 parent c282cc6 commit d3d7a59

File tree

8 files changed

+701
-230
lines changed

8 files changed

+701
-230
lines changed

commands/cmderrors/cmderrors.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ func (e *PlatformLoadingError) Unwrap() error {
435435
return e.Cause
436436
}
437437

438-
// LibraryNotFoundError is returned when a platform is not found
438+
// LibraryNotFoundError is returned when a library is not found
439439
type LibraryNotFoundError struct {
440440
Library string
441441
Cause error
@@ -883,3 +883,15 @@ func (e *InstanceNeedsReinitialization) GRPCStatus() *status.Status {
883883
WithDetails(&rpc.InstanceNeedsReinitializationError{})
884884
return st
885885
}
886+
887+
// MissingProfileError is returned when the Profile is mandatory and not specified
888+
type MissingProfileError struct{}
889+
890+
func (e *MissingProfileError) Error() string {
891+
return i18n.Tr("Missing Profile name")
892+
}
893+
894+
// GRPCStatus converts the error into a *status.Status
895+
func (e *MissingProfileError) GRPCStatus() *status.Status {
896+
return status.New(codes.InvalidArgument, e.Error())
897+
}

commands/service_profile_lib_add.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2025 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to license@arduino.cc.
15+
16+
package commands
17+
18+
import (
19+
"context"
20+
21+
"github.com/arduino/arduino-cli/commands/cmderrors"
22+
"github.com/arduino/arduino-cli/commands/internal/instances"
23+
"github.com/arduino/arduino-cli/internal/arduino/sketch"
24+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
25+
paths "github.com/arduino/go-paths-helper"
26+
)
27+
28+
func (s *arduinoCoreServerImpl) ProfileLibAdd(ctx context.Context, req *rpc.ProfileLibAddRequest) (*rpc.ProfileLibAddResponse, error) {
29+
sketchPath := paths.New(req.GetSketchPath())
30+
projectFilePath, err := sketchPath.Join("sketch.yaml").Abs()
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
if req.GetProfileName() == "" {
36+
return nil, &cmderrors.MissingProfileError{}
37+
}
38+
39+
// Returns an error if the main file is missing from the sketch so there is no need to check if the path exists
40+
sk, err := sketch.New(sketchPath)
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
profile, err := sk.GetProfile(req.ProfileName)
46+
if err != nil {
47+
return nil, err
48+
}
49+
50+
// Obtain the library index from the manager
51+
li, err := instances.GetLibrariesIndex(req.GetInstance())
52+
if err != nil {
53+
return nil, err
54+
}
55+
version, err := parseVersion(req.LibVersion)
56+
if err != nil {
57+
return nil, err
58+
}
59+
libRelease, err := li.FindRelease(req.GetLibName(), version)
60+
if err != nil {
61+
return nil, err
62+
}
63+
64+
// If the library has been already added to the profile, just update the version
65+
if lib, _ := profile.GetLibrary(req.LibName); lib != nil {
66+
lib.Version = libRelease.GetVersion()
67+
} else {
68+
profile.Libraries = append(profile.Libraries, &sketch.ProfileLibraryReference{
69+
Library: req.GetLibName(),
70+
Version: libRelease.GetVersion(),
71+
})
72+
}
73+
74+
err = projectFilePath.WriteFile([]byte(sk.Project.AsYaml()))
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
return &rpc.ProfileLibAddResponse{LibName: req.LibName, LibVersion: libRelease.GetVersion().String()}, nil
80+
}

internal/arduino/sketch/profiles.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"regexp"
2525
"strings"
2626

27+
"github.com/arduino/arduino-cli/commands/cmderrors"
2728
"github.com/arduino/arduino-cli/internal/arduino/utils"
2829
"github.com/arduino/arduino-cli/internal/i18n"
2930
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
@@ -119,6 +120,16 @@ type Profile struct {
119120
Libraries ProfileRequiredLibraries `yaml:"libraries"`
120121
}
121122

123+
// GetLibrary returns the requested library or an error if not found
124+
func (p *Profile) GetLibrary(libraryName string) (*ProfileLibraryReference, error) {
125+
for _, l := range p.Libraries {
126+
if l.Library == libraryName {
127+
return l, nil
128+
}
129+
}
130+
return nil, &cmderrors.LibraryNotFoundError{Library: libraryName}
131+
}
132+
122133
// ToRpc converts this Profile to an rpc.SketchProfile
123134
func (p *Profile) ToRpc() *rpc.SketchProfile {
124135
var portConfig *rpc.MonitorPortConfiguration

internal/cli/profile/lib.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2025 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to license@arduino.cc.
15+
16+
package profile
17+
18+
import (
19+
"context"
20+
"fmt"
21+
"os"
22+
23+
"github.com/arduino/arduino-cli/internal/cli/arguments"
24+
"github.com/arduino/arduino-cli/internal/cli/feedback"
25+
"github.com/arduino/arduino-cli/internal/cli/instance"
26+
"github.com/arduino/arduino-cli/internal/cli/lib"
27+
"github.com/arduino/arduino-cli/internal/i18n"
28+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
29+
"github.com/spf13/cobra"
30+
)
31+
32+
func initLibCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command {
33+
libCommand := &cobra.Command{
34+
Use: "lib",
35+
Short: i18n.Tr("Profile commands about libraries."),
36+
Long: i18n.Tr("Profile commands about libraries."),
37+
Example: "" +
38+
" " + os.Args[0] + " profile lib add AudioZero -m my_profile\n" +
39+
" " + os.Args[0] + " profile lib remove Arduino_JSON --profile my_profile\n",
40+
}
41+
42+
libCommand.AddCommand(initLibAddCommand(srv))
43+
44+
return libCommand
45+
}
46+
47+
func initLibAddCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command {
48+
var destDir string
49+
50+
addCommand := &cobra.Command{
51+
Use: fmt.Sprintf("add %s[@%s]...", i18n.Tr("LIBRARY"), i18n.Tr("VERSION_NUMBER")),
52+
Short: i18n.Tr("Adds a library to the profile."),
53+
Long: i18n.Tr("Adds a library to the profile."),
54+
Example: "" +
55+
" " + os.Args[0] + " profile lib add AudioZero -m my_profile\n" +
56+
" " + os.Args[0] + " profile lib add Arduino_JSON@0.2.0 --profile my_profile\n",
57+
Args: cobra.MinimumNArgs(1),
58+
Run: func(cmd *cobra.Command, args []string) {
59+
runLibAddCommand(cmd.Context(), args, srv, destDir)
60+
},
61+
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
62+
return arguments.GetInstallableLibs(cmd.Context(), srv), cobra.ShellCompDirectiveDefault
63+
},
64+
}
65+
66+
addCommand.Flags().StringVar(&destDir, "dest-dir", "", i18n.Tr("Location of the project file."))
67+
profileArg.AddToCommand(addCommand, srv)
68+
69+
return addCommand
70+
}
71+
72+
func runLibAddCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer, destDir string) {
73+
sketchPath := arguments.InitSketchPath(destDir)
74+
75+
instance := instance.CreateAndInit(ctx, srv)
76+
libRefs, err := lib.ParseLibraryReferenceArgsAndAdjustCase(ctx, srv, instance, args)
77+
if err != nil {
78+
feedback.Fatal(i18n.Tr("Arguments error: %v", err), feedback.ErrBadArgument)
79+
}
80+
for _, lib := range libRefs {
81+
resp, err := srv.ProfileLibAdd(ctx, &rpc.ProfileLibAddRequest{
82+
Instance: instance,
83+
SketchPath: sketchPath.String(),
84+
ProfileName: profileArg.Get(),
85+
LibName: lib.Name,
86+
LibVersion: lib.Version,
87+
})
88+
if err != nil {
89+
feedback.Fatal(i18n.Tr("Error adding %s to the profile %s: %v", lib.Name, profileArg.Get(), err), feedback.ErrGeneric)
90+
}
91+
feedback.PrintResult(libResult{LibName: resp.GetLibName(), LibVersion: resp.GetLibVersion(), ProfileName: profileArg.Get()})
92+
}
93+
}
94+
95+
type libResult struct {
96+
LibName string `json:"library_name"`
97+
LibVersion string `json:"library_version"`
98+
ProfileName string `json:"profile_name"`
99+
}
100+
101+
func (lr libResult) Data() interface{} {
102+
return lr
103+
}
104+
105+
func (lr libResult) String() string {
106+
return i18n.Tr("Profile %s: %s@%s added succesfully", lr.ProfileName, lr.LibName, lr.LibVersion)
107+
}

internal/cli/profile/profile.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command {
3838
}
3939

4040
profileCommand.AddCommand(initInitCommand(srv))
41+
profileCommand.AddCommand(initLibCommand(srv))
4142

4243
return profileCommand
4344
}

0 commit comments

Comments
 (0)