Skip to content

Commit 6076d14

Browse files
Add first set of profile commands (#2917)
* Improved handling of ProfileLibraryReference results * Implementation of profile commands * Improved some help description * Moved args into their correct place * Added remove deps * Fixed completion in profile lib remove * Rename 'profile init' -> 'profile create' * Proper behaviour for 'profile create' command * Added profile management integration tests * Rename --dest-dir to --sketch-path * Fixed json output for 'profile set-default' command --------- Co-authored-by: Cristian Maglie <c.maglie@arduino.cc>
1 parent 6756f78 commit 6076d14

File tree

12 files changed

+866
-104
lines changed

12 files changed

+866
-104
lines changed

commands/service_profile_create.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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+
"errors"
21+
"fmt"
22+
23+
"github.com/arduino/arduino-cli/commands/cmderrors"
24+
"github.com/arduino/arduino-cli/commands/internal/instances"
25+
"github.com/arduino/arduino-cli/internal/arduino/sketch"
26+
"github.com/arduino/arduino-cli/internal/i18n"
27+
"github.com/arduino/arduino-cli/pkg/fqbn"
28+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
29+
"github.com/arduino/go-paths-helper"
30+
)
31+
32+
// ProfileCreate creates a new project file if it does not exist. If a profile name with the associated FQBN is specified,
33+
// it is added to the project.
34+
func (s *arduinoCoreServerImpl) ProfileCreate(ctx context.Context, req *rpc.ProfileCreateRequest) (*rpc.ProfileCreateResponse, error) {
35+
if req.GetProfileName() == "" {
36+
return nil, &cmderrors.MissingProfileError{}
37+
}
38+
if req.GetFqbn() == "" {
39+
return nil, &cmderrors.MissingFQBNError{}
40+
}
41+
42+
// Returns an error if the main file is missing from the sketch so there is no need to check if the path exists
43+
sk, err := sketch.New(paths.New(req.GetSketchPath()))
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
fqbn, err := fqbn.Parse(req.GetFqbn())
49+
if err != nil {
50+
return nil, &cmderrors.InvalidFQBNError{Cause: err}
51+
}
52+
53+
// Check that the profile name is unique
54+
if profile, _ := sk.GetProfile(req.ProfileName); profile != nil {
55+
return nil, &cmderrors.ProfileAlreadyExitsError{Profile: req.ProfileName}
56+
}
57+
58+
pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance())
59+
if err != nil {
60+
return nil, err
61+
}
62+
defer release()
63+
if pme.Dirty() {
64+
return nil, &cmderrors.InstanceNeedsReinitialization{}
65+
}
66+
67+
// Automatically detect the target platform if it is installed on the user's machine
68+
_, targetPlatform, _, _, _, err := pme.ResolveFQBN(fqbn)
69+
if err != nil {
70+
if targetPlatform == nil {
71+
return nil, &cmderrors.PlatformNotFoundError{
72+
Platform: fmt.Sprintf("%s:%s", fqbn.Vendor, fqbn.Architecture),
73+
Cause: errors.New(i18n.Tr("platform not installed")),
74+
}
75+
}
76+
return nil, &cmderrors.InvalidFQBNError{Cause: err}
77+
}
78+
79+
newProfile := &sketch.Profile{Name: req.GetProfileName(), FQBN: req.GetFqbn()}
80+
// TODO: what to do with the PlatformIndexURL?
81+
newProfile.Platforms = append(newProfile.Platforms, &sketch.ProfilePlatformReference{
82+
Packager: targetPlatform.Platform.Package.Name,
83+
Architecture: targetPlatform.Platform.Architecture,
84+
Version: targetPlatform.Version,
85+
})
86+
87+
sk.Project.Profiles = append(sk.Project.Profiles, newProfile)
88+
if req.DefaultProfile {
89+
sk.Project.DefaultProfile = newProfile.Name
90+
}
91+
92+
projectFilePath := sk.GetProjectPath()
93+
err = projectFilePath.WriteFile([]byte(sk.Project.AsYaml()))
94+
if err != nil {
95+
return nil, err
96+
}
97+
98+
return &rpc.ProfileCreateResponse{}, nil
99+
}

commands/service_profile_init.go

Lines changed: 0 additions & 104 deletions
This file was deleted.

internal/cli/arguments/completion.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
"github.com/arduino/arduino-cli/internal/cli/instance"
2222
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
23+
"github.com/arduino/go-paths-helper"
2324
"go.bug.st/f"
2425
)
2526

@@ -172,3 +173,19 @@ func GetAvailablePorts(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []
172173
// Transform the data structure for the completion (DetectedPort -> Port)
173174
return f.Map(list.GetPorts(), (*rpc.DetectedPort).GetPort)
174175
}
176+
177+
// GetProfileLibraries is an helper function useful to autocomplete.
178+
// It returns a list of libraries present in the specified profile.
179+
func GetProfileLibraries(ctx context.Context, srv rpc.ArduinoCoreServiceServer, sketchPath *paths.Path, profile string) []string {
180+
resp, err := srv.ProfileLibList(ctx, &rpc.ProfileLibListRequest{
181+
SketchPath: sketchPath.String(),
182+
ProfileName: profile,
183+
})
184+
if err != nil {
185+
return nil
186+
}
187+
res := f.Map(resp.GetLibraries(), func(lib *rpc.ProfileLibraryReference) string {
188+
return lib.GetIndexLibrary().GetName()
189+
})
190+
return res
191+
}

internal/cli/cli.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/arduino/arduino-cli/internal/cli/lib"
3737
"github.com/arduino/arduino-cli/internal/cli/monitor"
3838
"github.com/arduino/arduino-cli/internal/cli/outdated"
39+
"github.com/arduino/arduino-cli/internal/cli/profile"
3940
"github.com/arduino/arduino-cli/internal/cli/sketch"
4041
"github.com/arduino/arduino-cli/internal/cli/update"
4142
"github.com/arduino/arduino-cli/internal/cli/updater"
@@ -162,6 +163,7 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command {
162163
cmd.AddCommand(burnbootloader.NewCommand(srv))
163164
cmd.AddCommand(version.NewCommand(srv))
164165
cmd.AddCommand(feedback.NewCommand())
166+
cmd.AddCommand(profile.NewCommand(srv))
165167

166168
cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, i18n.Tr("Print the logs on the standard output."))
167169
cmd.Flag("verbose").Hidden = true

internal/cli/feedback/result/rpc.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,12 @@ type ProfileLibraryReference_LocalLibraryResult struct {
11301130
Path string `json:"path,omitempty"`
11311131
}
11321132

1133+
func (*ProfileLibraryReference_LocalLibraryResult) isProfileLibraryReference() {}
1134+
1135+
func (l *ProfileLibraryReference_LocalLibraryResult) String() string {
1136+
return fmt.Sprintf("lib: %s", l.Path)
1137+
}
1138+
11331139
func NewProfileLibraryReference_LocalLibraryResult(resp *rpc.ProfileLibraryReference_LocalLibrary) *ProfileLibraryReference_LocalLibraryResult {
11341140
return &ProfileLibraryReference_LocalLibraryResult{
11351141
Path: resp.GetPath(),
@@ -1142,10 +1148,49 @@ type ProfileLibraryReference_IndexLibraryResult struct {
11421148
IsDependency bool `json:"is_dependency,omitempty"`
11431149
}
11441150

1151+
func (*ProfileLibraryReference_IndexLibraryResult) isProfileLibraryReference() {}
1152+
1153+
func (l *ProfileLibraryReference_IndexLibraryResult) String() string {
1154+
if l.IsDependency {
1155+
return fmt.Sprintf("dependency: %s@%s", l.Name, l.Version)
1156+
}
1157+
return fmt.Sprintf("%s@%s", l.Name, l.Version)
1158+
}
1159+
11451160
func NewProfileLibraryReference_IndexLibraryResult(resp *rpc.ProfileLibraryReference_IndexLibrary) *ProfileLibraryReference_IndexLibraryResult {
11461161
return &ProfileLibraryReference_IndexLibraryResult{
11471162
Name: resp.GetName(),
11481163
Version: resp.GetVersion(),
11491164
IsDependency: resp.GetIsDependency(),
11501165
}
11511166
}
1167+
1168+
type ProfileLibraryReference struct {
1169+
Kind string `json:"kind,omitempty"`
1170+
Library ProfileLibraryReference_Library `json:"library,omitempty"`
1171+
}
1172+
1173+
type ProfileLibraryReference_Library interface {
1174+
isProfileLibraryReference()
1175+
fmt.Stringer
1176+
}
1177+
1178+
func NewProfileLibraryReference(resp *rpc.ProfileLibraryReference) *ProfileLibraryReference {
1179+
if lib := resp.GetIndexLibrary(); lib != nil {
1180+
return &ProfileLibraryReference{
1181+
Library: NewProfileLibraryReference_IndexLibraryResult(lib),
1182+
Kind: "index",
1183+
}
1184+
}
1185+
if lib := resp.GetLocalLibrary(); lib != nil {
1186+
return &ProfileLibraryReference{
1187+
Library: NewProfileLibraryReference_LocalLibraryResult(lib),
1188+
Kind: "local",
1189+
}
1190+
}
1191+
return nil
1192+
}
1193+
1194+
func (p *ProfileLibraryReference) String() string {
1195+
return p.Library.String()
1196+
}

internal/cli/profile/profile.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
"os"
20+
21+
"github.com/arduino/arduino-cli/internal/i18n"
22+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
23+
"github.com/spf13/cobra"
24+
)
25+
26+
func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command {
27+
profileCommand := &cobra.Command{
28+
Use: "profile",
29+
Short: i18n.Tr("Build profile operations."),
30+
Long: i18n.Tr("Build profile operations."),
31+
Example: " " + os.Args[0] + " profile init",
32+
}
33+
34+
profileCommand.AddCommand(initProfileCreateCommand(srv))
35+
profileCommand.AddCommand(initProfileLibCommand(srv))
36+
profileCommand.AddCommand(initProfileSetDefaultCommand(srv))
37+
return profileCommand
38+
}

0 commit comments

Comments
 (0)