Skip to content

Commit 8f5d38b

Browse files
author
Vinay Lanka
authored
Add flags to install libraries from zip or git url (#648)
* Add zip library imports * Add git library imports * Add tests for lib install with flags
1 parent 17d24eb commit 8f5d38b

File tree

19 files changed

+895
-242
lines changed

19 files changed

+895
-242
lines changed

Diff for: arduino/libraries/librariesmanager/install.go

+44
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@
1616
package librariesmanager
1717

1818
import (
19+
"context"
1920
"errors"
2021
"fmt"
22+
"os"
23+
"strings"
2124

2225
"github.com/arduino/arduino-cli/arduino/libraries"
2326
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
2427
"github.com/arduino/arduino-cli/arduino/utils"
2528
paths "github.com/arduino/go-paths-helper"
29+
"github.com/codeclysm/extract/v3"
30+
"gopkg.in/src-d/go-git.v4"
2631
)
2732

2833
var (
@@ -85,3 +90,42 @@ func (lm *LibrariesManager) Uninstall(lib *libraries.Library) error {
8590
lm.Libraries[lib.Name].Remove(lib)
8691
return nil
8792
}
93+
94+
//InstallZipLib installs a Zip library on the specified path.
95+
func (lm *LibrariesManager) InstallZipLib(ctx context.Context, archivePath string) error {
96+
libsDir := lm.getUserLibrariesDir()
97+
if libsDir == nil {
98+
return fmt.Errorf("User directory not set")
99+
}
100+
101+
file, err := os.Open(archivePath)
102+
if err != nil {
103+
return err
104+
}
105+
defer file.Close()
106+
107+
if err := extract.Archive(ctx, file, libsDir.String(), nil); err != nil {
108+
return fmt.Errorf("extracting archive: %s", err)
109+
}
110+
return nil
111+
}
112+
113+
//InstallGitLib installs a library hosted on a git repository on the specified path.
114+
func (lm *LibrariesManager) InstallGitLib(url string) error {
115+
libsDir := lm.getUserLibrariesDir()
116+
if libsDir == nil {
117+
return fmt.Errorf("User directory not set")
118+
}
119+
i := strings.LastIndex(url, "/")
120+
folder := strings.TrimRight(url[i+1:], ".git")
121+
path := libsDir.Join(folder)
122+
123+
_, err := git.PlainClone(path.String(), false, &git.CloneOptions{
124+
URL: url,
125+
Progress: os.Stdout,
126+
})
127+
if err != nil {
128+
return err
129+
}
130+
return nil
131+
}

Diff for: cli/lib/install.go

+69-43
Original file line numberDiff line numberDiff line change
@@ -40,65 +40,91 @@ func initInstallCommand() *cobra.Command {
4040
Run: runInstallCommand,
4141
}
4242
installCommand.Flags().BoolVar(&installFlags.noDeps, "no-deps", false, "Do not install dependencies.")
43+
installCommand.Flags().BoolVar(&installFlags.gitURL, "git-url", false, "Enter git url for libraries hosted on repositories")
44+
installCommand.Flags().BoolVar(&installFlags.zipPath, "zip-path", false, "Enter a path to zip file")
4345
return installCommand
4446
}
4547

4648
var installFlags struct {
47-
noDeps bool
49+
noDeps bool
50+
gitURL bool
51+
zipPath bool
4852
}
4953

5054
func runInstallCommand(cmd *cobra.Command, args []string) {
5155
instance := instance.CreateInstanceIgnorePlatformIndexErrors()
52-
libRefs, err := ParseLibraryReferenceArgsAndAdjustCase(instance, args)
53-
if err != nil {
54-
feedback.Errorf("Arguments error: %v", err)
55-
os.Exit(errorcodes.ErrBadArgument)
56-
}
57-
58-
toInstall := map[string]*rpc.LibraryDependencyStatus{}
59-
if installFlags.noDeps {
60-
for _, libRef := range libRefs {
61-
toInstall[libRef.Name] = &rpc.LibraryDependencyStatus{
62-
Name: libRef.Name,
63-
VersionRequired: libRef.Version,
64-
}
56+
if installFlags.zipPath {
57+
ziplibraryInstallReq := &rpc.ZipLibraryInstallReq{
58+
Instance: instance,
59+
Path: args[0],
60+
}
61+
err := lib.ZipLibraryInstall(context.Background(), ziplibraryInstallReq, output.TaskProgress())
62+
if err != nil {
63+
feedback.Errorf("Error installing Zip Library: %v", err)
64+
os.Exit(errorcodes.ErrGeneric)
65+
}
66+
} else if installFlags.gitURL {
67+
gitlibraryInstallReq := &rpc.GitLibraryInstallReq{
68+
Instance: instance,
69+
Url: args[0],
70+
}
71+
err := lib.GitLibraryInstall(context.Background(), gitlibraryInstallReq, output.TaskProgress())
72+
if err != nil {
73+
feedback.Errorf("Error installing Git Library: %v", err)
74+
os.Exit(errorcodes.ErrGeneric)
6575
}
6676
} else {
67-
for _, libRef := range libRefs {
68-
depsResp, err := lib.LibraryResolveDependencies(context.Background(), &rpc.LibraryResolveDependenciesReq{
69-
Instance: instance,
70-
Name: libRef.Name,
71-
Version: libRef.Version,
72-
})
73-
if err != nil {
74-
feedback.Errorf("Error resolving dependencies for %s: %s", libRef, err)
75-
os.Exit(errorcodes.ErrGeneric)
77+
libRefs, err := ParseLibraryReferenceArgsAndAdjustCase(instance, args)
78+
if err != nil {
79+
feedback.Errorf("Arguments error: %v", err)
80+
os.Exit(errorcodes.ErrBadArgument)
81+
}
82+
83+
toInstall := map[string]*rpc.LibraryDependencyStatus{}
84+
if installFlags.noDeps {
85+
for _, libRef := range libRefs {
86+
toInstall[libRef.Name] = &rpc.LibraryDependencyStatus{
87+
Name: libRef.Name,
88+
VersionRequired: libRef.Version,
89+
}
7690
}
77-
for _, dep := range depsResp.GetDependencies() {
78-
feedback.Printf("%s depends on %s@%s", libRef, dep.GetName(), dep.GetVersionRequired())
79-
if existingDep, has := toInstall[dep.GetName()]; has {
80-
if existingDep.GetVersionRequired() != dep.GetVersionRequired() {
81-
// TODO: make a better error
82-
feedback.Errorf("The library %s is required in two different versions: %s and %s",
83-
dep.GetName(), dep.GetVersionRequired(), existingDep.GetVersionRequired())
84-
os.Exit(errorcodes.ErrGeneric)
91+
} else {
92+
for _, libRef := range libRefs {
93+
depsResp, err := lib.LibraryResolveDependencies(context.Background(), &rpc.LibraryResolveDependenciesReq{
94+
Instance: instance,
95+
Name: libRef.Name,
96+
Version: libRef.Version,
97+
})
98+
if err != nil {
99+
feedback.Errorf("Error resolving dependencies for %s: %s", libRef, err)
100+
os.Exit(errorcodes.ErrGeneric)
101+
}
102+
for _, dep := range depsResp.GetDependencies() {
103+
feedback.Printf("%s depends on %s@%s", libRef, dep.GetName(), dep.GetVersionRequired())
104+
if existingDep, has := toInstall[dep.GetName()]; has {
105+
if existingDep.GetVersionRequired() != dep.GetVersionRequired() {
106+
// TODO: make a better error
107+
feedback.Errorf("The library %s is required in two different versions: %s and %s",
108+
dep.GetName(), dep.GetVersionRequired(), existingDep.GetVersionRequired())
109+
os.Exit(errorcodes.ErrGeneric)
110+
}
85111
}
112+
toInstall[dep.GetName()] = dep
86113
}
87-
toInstall[dep.GetName()] = dep
88114
}
89115
}
90-
}
91116

92-
for _, library := range toInstall {
93-
libraryInstallReq := &rpc.LibraryInstallReq{
94-
Instance: instance,
95-
Name: library.Name,
96-
Version: library.VersionRequired,
97-
}
98-
err := lib.LibraryInstall(context.Background(), libraryInstallReq, output.ProgressBar(), output.TaskProgress())
99-
if err != nil {
100-
feedback.Errorf("Error installing %s: %v", library, err)
101-
os.Exit(errorcodes.ErrGeneric)
117+
for _, library := range toInstall {
118+
libraryInstallReq := &rpc.LibraryInstallReq{
119+
Instance: instance,
120+
Name: library.Name,
121+
Version: library.VersionRequired,
122+
}
123+
err := lib.LibraryInstall(context.Background(), libraryInstallReq, output.ProgressBar(), output.TaskProgress())
124+
if err != nil {
125+
feedback.Errorf("Error installing %s: %v", library, err)
126+
os.Exit(errorcodes.ErrGeneric)
127+
}
102128
}
103129
}
104130
}

Diff for: commands/daemon/daemon.go

+24
Original file line numberDiff line numberDiff line change
@@ -356,3 +356,27 @@ func (s *ArduinoCoreServerImpl) LibraryList(ctx context.Context, req *rpc.Librar
356356
func (s *ArduinoCoreServerImpl) ArchiveSketch(ctx context.Context, req *rpc.ArchiveSketchReq) (*rpc.ArchiveSketchResp, error) {
357357
return sketch.ArchiveSketch(ctx, req)
358358
}
359+
360+
//ZipLibraryInstall FIXMEDOC
361+
func (s *ArduinoCoreServerImpl) ZipLibraryInstall(req *rpc.ZipLibraryInstallReq, stream rpc.ArduinoCore_ZipLibraryInstallServer) error {
362+
err := lib.ZipLibraryInstall(
363+
stream.Context(), req,
364+
func(p *rpc.TaskProgress) { stream.Send(&rpc.ZipLibraryInstallResp{TaskProgress: p}) },
365+
)
366+
if err != nil {
367+
return err
368+
}
369+
return stream.Send(&rpc.ZipLibraryInstallResp{})
370+
}
371+
372+
//GitLibraryInstall FIXMEDOC
373+
func (s *ArduinoCoreServerImpl) GitLibraryInstall(req *rpc.GitLibraryInstallReq, stream rpc.ArduinoCore_GitLibraryInstallServer) error {
374+
err := lib.GitLibraryInstall(
375+
stream.Context(), req,
376+
func(p *rpc.TaskProgress) { stream.Send(&rpc.GitLibraryInstallResp{TaskProgress: p}) },
377+
)
378+
if err != nil {
379+
return err
380+
}
381+
return stream.Send(&rpc.GitLibraryInstallResp{})
382+
}

Diff for: commands/lib/install.go

+22
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,25 @@ func installLibrary(lm *librariesmanager.LibrariesManager, libRelease *libraries
7575
taskCB(&rpc.TaskProgress{Message: "Installed " + libRelease.String(), Completed: true})
7676
return nil
7777
}
78+
79+
//ZipLibraryInstall FIXMEDOC
80+
func ZipLibraryInstall(ctx context.Context, req *rpc.ZipLibraryInstallReq, taskCB commands.TaskProgressCB) error {
81+
lm := commands.GetLibraryManager(req.GetInstance().GetId())
82+
archivePath := req.GetPath()
83+
if err := lm.InstallZipLib(ctx, archivePath); err != nil {
84+
return err
85+
}
86+
taskCB(&rpc.TaskProgress{Message: "Installed Archived Library", Completed: true})
87+
return nil
88+
}
89+
90+
//GitLibraryInstall FIXMEDOC
91+
func GitLibraryInstall(ctx context.Context, req *rpc.GitLibraryInstallReq, taskCB commands.TaskProgressCB) error {
92+
lm := commands.GetLibraryManager(req.GetInstance().GetId())
93+
url := req.GetUrl()
94+
if err := lm.InstallGitLib(url); err != nil {
95+
return err
96+
}
97+
taskCB(&rpc.TaskProgress{Message: "Installed Library from Git URL", Completed: true})
98+
return nil
99+
}

Diff for: go.mod

+8-2
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,22 @@ require (
2121
github.com/h2non/filetype v1.0.8 // indirect
2222
github.com/imjasonmiller/godice v0.1.2
2323
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
24+
github.com/kr/pretty v0.2.0 // indirect
25+
github.com/kr/text v0.2.0 // indirect
2426
github.com/leonelquinteros/gotext v1.4.0
2527
github.com/marcinbor85/gohex v0.0.0-20200531163658-baab2527a9a2
2628
github.com/mattn/go-colorable v0.1.2
2729
github.com/mattn/go-isatty v0.0.8
2830
github.com/mattn/go-runewidth v0.0.9 // indirect
2931
github.com/miekg/dns v1.0.5 // indirect
32+
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
3033
github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228 // indirect
3134
github.com/pkg/errors v0.9.1
3235
github.com/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583
3336
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
3437
github.com/schollz/closestmatch v2.1.0+incompatible
3538
github.com/segmentio/stats/v4 v4.5.3
39+
github.com/sergi/go-diff v1.1.0 // indirect
3640
github.com/sirupsen/logrus v1.4.2
3741
github.com/spf13/cobra v1.0.1-0.20200710201246-675ae5f5a98c
3842
github.com/spf13/jwalterweatherman v1.0.0
@@ -43,11 +47,13 @@ require (
4347
go.bug.st/relaxed-semver v0.0.0-20190922224835-391e10178d18
4448
go.bug.st/serial v1.1.1
4549
go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45 // indirect
46-
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
47-
golang.org/x/net v0.0.0-20200202094626-16171245cfb2
50+
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71
51+
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
4852
golang.org/x/text v0.3.2
4953
google.golang.org/grpc v1.27.0
5054
google.golang.org/protobuf v1.25.0
55+
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
5156
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect
57+
gopkg.in/src-d/go-git.v4 v4.13.1
5258
gopkg.in/yaml.v2 v2.3.0
5359
)

0 commit comments

Comments
 (0)