Skip to content

Commit 333f550

Browse files
committed
Merge branch 'profile-git-libraries' into grpc-profiles-functions
2 parents 27fe68b + 8949023 commit 333f550

File tree

10 files changed

+231
-45
lines changed

10 files changed

+231
-45
lines changed

.github/workflows/check-markdown-task.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ jobs:
7878
uses: actions/checkout@v5
7979

8080
- name: Setup Node.js
81-
uses: actions/setup-node@v5
81+
uses: actions/setup-node@v6
8282
with:
8383
node-version: ${{ env.NODE_VERSION }}
8484

@@ -104,7 +104,7 @@ jobs:
104104
uses: actions/checkout@v5
105105

106106
- name: Setup Node.js
107-
uses: actions/setup-node@v5
107+
uses: actions/setup-node@v6
108108
with:
109109
node-version: ${{ env.NODE_VERSION }}
110110

commands/instances.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,13 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor
155155

156156
// Try to extract profile if specified
157157
var profile *sketch.Profile
158+
var profileSketchFullPath *paths.Path
158159
if req.GetProfile() != "" {
159160
sk, err := sketch.New(paths.New(req.GetSketchPath()))
160161
if err != nil {
161162
return &cmderrors.InvalidArgumentError{Cause: err}
162163
}
164+
profileSketchFullPath = sk.FullPath
163165
p, err := sk.GetProfile(req.GetProfile())
164166
if err != nil {
165167
return err
@@ -371,7 +373,7 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor
371373
if libraryRef.InstallDir != nil {
372374
libDir := libraryRef.InstallDir
373375
if !libDir.IsAbs() {
374-
libDir = paths.New(req.GetSketchPath()).JoinPath(libraryRef.InstallDir)
376+
libDir = profileSketchFullPath.JoinPath(libraryRef.InstallDir)
375377
}
376378
if !libDir.IsDir() {
377379
return &cmderrors.InvalidArgumentError{
@@ -386,6 +388,40 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor
386388
continue
387389
}
388390

391+
if libraryRef.GitURL != nil {
392+
uid := libraryRef.InternalUniqueIdentifier()
393+
libRoot := s.settings.ProfilesCacheDir().Join(uid)
394+
libDir := libRoot.Join(libraryRef.Library)
395+
396+
if !libDir.IsDir() {
397+
// Clone repo and install
398+
tmpDir, err := librariesmanager.CloneLibraryGitRepository(ctx, libraryRef.GitURL.String())
399+
if err != nil {
400+
taskCallback(&rpc.TaskProgress{Name: i18n.Tr("Error downloading library %s", libraryRef)})
401+
e := &cmderrors.FailedLibraryInstallError{Cause: err}
402+
responseError(e.GRPCStatus())
403+
continue
404+
}
405+
406+
// Install library into profile cache
407+
copyErr := tmpDir.CopyDirTo(libDir)
408+
_ = tmpDir.RemoveAll()
409+
if copyErr != nil {
410+
taskCallback(&rpc.TaskProgress{Name: i18n.Tr("Error installing library %s", libraryRef)})
411+
e := &cmderrors.FailedLibraryInstallError{Cause: fmt.Errorf("copying library to profile cache: %w", err)}
412+
responseError(e.GRPCStatus())
413+
continue
414+
}
415+
}
416+
417+
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
418+
Path: libDir,
419+
Location: libraries.Profile,
420+
IsSingleLibrary: true,
421+
})
422+
continue
423+
}
424+
389425
uid := libraryRef.InternalUniqueIdentifier()
390426
libRoot := s.settings.ProfilesCacheDir().Join(uid)
391427
libDir := libRoot.Join(libraryRef.Library)

commands/service_library_install.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,8 @@ func (s *arduinoCoreServerImpl) GitLibraryInstall(req *rpc.GitLibraryInstallRequ
263263
lmi, release := lm.NewInstaller()
264264
defer release()
265265

266-
// TODO: pass context
267-
// ctx := stream.Context()
268-
if err := lmi.InstallGitLib(req.GetUrl(), req.GetOverwrite()); err != nil {
266+
ctx := stream.Context()
267+
if err := lmi.InstallGitLib(ctx, req.GetUrl(), req.GetOverwrite()); err != nil {
269268
return &cmderrors.FailedLibraryInstallError{Cause: err}
270269
}
271270
taskCB(&rpc.TaskProgress{Message: i18n.Tr("Library installed"), Completed: true})

docs/CONTRIBUTING.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ First of all, thanks for contributing! This document provides some basic guideli
44

55
There are several ways you can get involved:
66

7-
| Type of contribution | Contribution method |
8-
| ------------------------------------------------- | ------------------------------------------------------- |
9-
| - Support request<br/>- Question<br/>- Discussion | Post on the [Arduino Forum][forum] |
10-
| - Bug report<br/>- Feature request | Issue report (read the [issue guidelines][issues]) |
11-
| Testing | Try out the [nightly build][nightly] |
12-
| - Bug fix<br/>- Enhancement | Pull Request (read the [pull request guidelines][prs]) |
13-
| Translations for Arduino CLI | Use the [transifex][translate] platform |
14-
| Monetary | - [Donate][donate]<br/>- [Buy official products][store] |
7+
| Type of contribution | Contribution method |
8+
| ------------------------------------------------- | ------------------------------------------------------ |
9+
| - Support request<br/>- Question<br/>- Discussion | Post on the [Arduino Forum][forum] |
10+
| - Bug report<br/>- Feature request | Issue report (read the [issue guidelines][issues]) |
11+
| Testing | Try out the [nightly build][nightly] |
12+
| - Bug fix<br/>- Enhancement | Pull Request (read the [pull request guidelines][prs]) |
13+
| Translations for Arduino CLI | Use the [transifex][translate] platform |
14+
| Monetary | [Buy official products][store] |
1515

1616
## Issue Reports
1717

@@ -342,7 +342,6 @@ If your PR doesn't need to be included in the changelog, please start the commit
342342
[nightly]: https://arduino.github.io/arduino-cli/latest/installation/#nightly-builds
343343
[prs]: #pull-requests
344344
[translate]: https://www.transifex.com/arduino-1/arduino-cli/
345-
[donate]: https://www.arduino.cc/en/Main/Contribute
346345
[store]: https://store.arduino.cc
347346
[issue-tracker]: https://github.com/arduino/arduino-cli/issues?q=
348347
[reactions]: https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments

internal/arduino/libraries/librariesmanager/install.go

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -200,54 +200,64 @@ func (lmi *Installer) InstallZipLib(ctx context.Context, archivePath *paths.Path
200200
}
201201

202202
// InstallGitLib installs a library hosted on a git repository on the specified path.
203-
func (lmi *Installer) InstallGitLib(argURL string, overwrite bool) error {
204-
libraryName, gitURL, ref, err := parseGitArgURL(argURL)
203+
func (lmi *Installer) InstallGitLib(ctx context.Context, argURL string, overwrite bool) error {
204+
tmpInstallPath, err := CloneLibraryGitRepository(ctx, argURL)
205205
if err != nil {
206206
return err
207207
}
208+
defer tmpInstallPath.RemoveAll()
209+
210+
// Install extracted library in the destination directory
211+
if err := lmi.importLibraryFromDirectory(tmpInstallPath, overwrite); err != nil {
212+
return errors.New(i18n.Tr("moving extracted archive to destination dir: %s", err))
213+
}
214+
215+
return nil
216+
}
217+
218+
// CloneLibraryGitRepository clones a git repository containing a library
219+
// into a temporary directory and returns the path to the cloned library.
220+
func CloneLibraryGitRepository(ctx context.Context, argURL string) (*paths.Path, error) {
221+
libraryName, gitURL, ref, err := parseGitArgURL(argURL)
222+
if err != nil {
223+
return nil, err
224+
}
208225

209226
// Clone library in a temporary directory
210227
tmp, err := paths.MkTempDir("", "")
211228
if err != nil {
212-
return err
229+
return nil, err
213230
}
214-
defer tmp.RemoveAll()
215231
tmpInstallPath := tmp.Join(libraryName)
216232

217-
if _, err := git.PlainClone(tmpInstallPath.String(), false, &git.CloneOptions{
233+
if _, err := git.PlainCloneContext(ctx, tmpInstallPath.String(), false, &git.CloneOptions{
218234
URL: gitURL,
219235
ReferenceName: plumbing.ReferenceName(ref),
220236
}); err != nil {
221237
if err.Error() != "reference not found" {
222-
return err
238+
return nil, err
223239
}
224240

225241
// We did not find the requested reference, let's do a PlainClone and use
226242
// "ResolveRevision" to find and checkout the requested revision
227-
if repo, err := git.PlainClone(tmpInstallPath.String(), false, &git.CloneOptions{
243+
if repo, err := git.PlainCloneContext(ctx, tmpInstallPath.String(), false, &git.CloneOptions{
228244
URL: gitURL,
229245
}); err != nil {
230-
return err
246+
return nil, err
231247
} else if h, err := repo.ResolveRevision(plumbing.Revision(ref)); err != nil {
232-
return err
248+
return nil, err
233249
} else if w, err := repo.Worktree(); err != nil {
234-
return err
250+
return nil, err
235251
} else if err := w.Checkout(&git.CheckoutOptions{
236252
Force: true, // workaround for: https://github.com/go-git/go-git/issues/1411
237253
Hash: plumbing.NewHash(h.String())}); err != nil {
238-
return err
254+
return nil, err
239255
}
240256
}
241257

242258
// We don't want the installed library to be a git repository thus we delete this folder
243259
tmpInstallPath.Join(".git").RemoveAll()
244-
245-
// Install extracted library in the destination directory
246-
if err := lmi.importLibraryFromDirectory(tmpInstallPath, overwrite); err != nil {
247-
return errors.New(i18n.Tr("moving extracted archive to destination dir: %s", err))
248-
}
249-
250-
return nil
260+
return tmpInstallPath, nil
251261
}
252262

253263
// parseGitArgURL tries to recover a library name from a git URL.

internal/arduino/sketch/profiles.go

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"errors"
2222
"fmt"
2323
"net/url"
24+
"path/filepath"
2425
"regexp"
2526
"slices"
2627
"strings"
@@ -349,23 +350,39 @@ func (p *ProfilePlatformReference) UnmarshalYAML(unmarshal func(interface{}) err
349350
// ProfileLibraryReference is a reference to a library
350351
type ProfileLibraryReference struct {
351352
Library string
352-
InstallDir *paths.Path
353353
Version *semver.Version
354+
InstallDir *paths.Path
355+
GitURL *url.URL
354356
}
355357

356358
// UnmarshalYAML decodes a ProfileLibraryReference from YAML source.
357359
func (l *ProfileLibraryReference) UnmarshalYAML(unmarshal func(interface{}) error) error {
358360
var dataMap map[string]any
359361
if err := unmarshal(&dataMap); err == nil {
360-
if installDir, ok := dataMap["dir"]; !ok {
361-
return errors.New(i18n.Tr("invalid library reference: %s", dataMap))
362-
} else if installDir, ok := installDir.(string); !ok {
363-
return fmt.Errorf("%s: %s", i18n.Tr("invalid library reference: %s"), dataMap)
364-
} else {
365-
l.InstallDir = paths.New(installDir)
366-
l.Library = l.InstallDir.Base()
367-
return nil
362+
if installDir, ok := dataMap["dir"]; ok {
363+
if installDir, ok := installDir.(string); !ok {
364+
return fmt.Errorf("%s: %s", i18n.Tr("invalid library reference: %s"), dataMap)
365+
} else {
366+
l.InstallDir = paths.New(installDir)
367+
l.Library = l.InstallDir.Base()
368+
return nil
369+
}
370+
}
371+
if gitUrl, ok := dataMap["git"]; ok {
372+
if gitUrlStr, ok := gitUrl.(string); !ok {
373+
return fmt.Errorf("%s: %s", i18n.Tr("invalid git library reference: %s"), dataMap)
374+
} else if parsedUrl, err := url.Parse(gitUrlStr); err != nil {
375+
return fmt.Errorf("%s: %w", i18n.Tr("invalid git library URL:"), err)
376+
} else {
377+
l.GitURL = parsedUrl
378+
if l.Library = filepath.Base(parsedUrl.Path); l.Library == "" {
379+
l.Library = "lib"
380+
}
381+
l.Library = strings.TrimSuffix(l.Library, ".git")
382+
return nil
383+
}
368384
}
385+
return errors.New(i18n.Tr("invalid library reference: %s", dataMap))
369386
}
370387

371388
var data string
@@ -388,12 +405,18 @@ func (l *ProfileLibraryReference) AsYaml() string {
388405
if l.InstallDir != nil {
389406
return fmt.Sprintf(" - dir: %s\n", l.InstallDir)
390407
}
408+
if l.GitURL != nil {
409+
return fmt.Sprintf(" - git: %s\n", l.GitURL)
410+
}
391411
return fmt.Sprintf(" - %s (%s)\n", l.Library, l.Version)
392412
}
393413

394414
func (l *ProfileLibraryReference) String() string {
395415
if l.InstallDir != nil {
396-
return fmt.Sprintf("%s@dir:%s", l.Library, l.InstallDir)
416+
return "@dir:" + l.InstallDir.String()
417+
}
418+
if l.GitURL != nil {
419+
return "@git:" + l.GitURL.String()
397420
}
398421
if l.Version == nil {
399422
return l.Library
@@ -467,6 +490,16 @@ func FromRpcProfileLibraryReference(l *rpc.SketchProfileLibraryReference) (*Prof
467490
func (l *ProfileLibraryReference) InternalUniqueIdentifier() string {
468491
f.Assert(l.InstallDir == nil,
469492
"InternalUniqueIdentifier should not be called for library references with an install directory")
493+
494+
if l.GitURL != nil {
495+
id := "git-" + utils.SanitizeName(l.GitURL.Host+l.GitURL.Path+"#"+l.GitURL.Fragment)
496+
if len(id) > 50 {
497+
id = id[:50]
498+
}
499+
h := sha256.Sum256([]byte(l.GitURL.String()))
500+
return id + "-" + hex.EncodeToString(h[:])[:8]
501+
}
502+
470503
id := l.String()
471504
h := sha256.Sum256([]byte(id))
472505
res := fmt.Sprintf("%s_%s", id, hex.EncodeToString(h[:])[:16])

internal/arduino/sketch/profiles_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,26 @@ func TestProjectFileLoading(t *testing.T) {
5353
require.Error(t, err)
5454
}
5555
}
56+
57+
func TestProjectFileLibraries(t *testing.T) {
58+
sketchProj := paths.New("testdata", "profiles", "profile_with_libraries.yml")
59+
proj, err := LoadProjectFile(sketchProj)
60+
require.NoError(t, err)
61+
require.Len(t, proj.Profiles, 1)
62+
prof := proj.Profiles[0]
63+
require.Len(t, prof.Libraries, 5)
64+
require.Equal(t, "FlashStorage@1.2.3", prof.Libraries[0].String())
65+
require.Equal(t, "@dir:/path/to/system/lib", prof.Libraries[1].String())
66+
require.Equal(t, "@dir:path/to/sketch/lib", prof.Libraries[2].String())
67+
require.Equal(t, "@git:https://github.com/username/HelloWorld.git#v2.13", prof.Libraries[3].String())
68+
require.Equal(t, "@git:https://github.com/username/HelloWorld.git#v2.14", prof.Libraries[4].String())
69+
require.Equal(t, "FlashStorage_1.2.3_e525d7c96b27788f", prof.Libraries[0].InternalUniqueIdentifier())
70+
require.Panics(t, func() { prof.Libraries[1].InternalUniqueIdentifier() })
71+
require.Panics(t, func() { prof.Libraries[2].InternalUniqueIdentifier() })
72+
require.Equal(t, "git-github.com_username_HelloWorld.git_v2.13-0c146203", prof.Libraries[3].InternalUniqueIdentifier())
73+
require.Equal(t, "git-github.com_username_HelloWorld.git_v2.14-49f5df7f", prof.Libraries[4].InternalUniqueIdentifier())
74+
75+
orig, err := sketchProj.ReadFile()
76+
require.NoError(t, err)
77+
require.Equal(t, string(orig), proj.AsYaml())
78+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
profiles:
2+
giga:
3+
fqbn: arduino:mbed_giga:giga
4+
platforms:
5+
- platform: arduino:mbed_giga (4.3.1)
6+
libraries:
7+
- FlashStorage (1.2.3)
8+
- dir: /path/to/system/lib
9+
- dir: path/to/sketch/lib
10+
- git: https://github.com/username/HelloWorld.git#v2.13
11+
- git: https://github.com/username/HelloWorld.git#v2.14
12+
13+
default_profile: giga_any

internal/cli/compile/compile.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,9 @@ func runCompileCommand(cmd *cobra.Command, args []string, srv rpc.ArduinoCoreSer
332332
libDir := paths.New(lib.GetInstallDir())
333333
// If the library is installed in the sketch path, we want to output the relative path
334334
// to the sketch path, so that the sketch is portable.
335-
if ok, err := libDir.IsInsideDir(sketchPath); err == nil && ok {
336-
if ref, err := libDir.RelFrom(sketchPath); err == nil {
335+
sketchDir := paths.New(sk.LocationPath)
336+
if ok, err := libDir.IsInsideDir(sketchDir); err == nil && ok {
337+
if ref, err := libDir.RelFrom(sketchDir); err == nil {
337338
libDir = paths.New(filepath.ToSlash(ref.String()))
338339
}
339340
}

0 commit comments

Comments
 (0)