Skip to content

Commit 82e6f5d

Browse files
authored
Automatically download indexes, if missing, in gRPC Init call (#2119)
* Created core.PlatformList implementaion follow gRPC naming Previously it was named GetPlatforms with a different return type than the one defined in gRPC API .proto files. * Added test for #1529 * Perform first-update automatically in gRPC Init * Made cli.instance.Create function private * Extract function to compute index file name * Auto-download 3rd party indexes as part of the Init
1 parent 1877431 commit 82e6f5d

File tree

17 files changed

+157
-137
lines changed

17 files changed

+157
-137
lines changed

Diff for: arduino/resources/index.go

+15-7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ type IndexResource struct {
3737
SignatureURL *url.URL
3838
}
3939

40+
// IndexFileName returns the index file name as it is saved in data dir (package_xxx_index.json).
41+
func (res *IndexResource) IndexFileName() string {
42+
filename := path.Base(res.URL.Path) // == package_index.json[.gz] || packacge_index.tar.bz2
43+
if i := strings.Index(filename, "."); i != -1 {
44+
filename = filename[:i]
45+
}
46+
return filename + ".json"
47+
}
48+
4049
// Download will download the index and possibly check the signature using the Arduino's public key.
4150
// If the file is in .gz format it will be unpacked first.
4251
func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadProgressCB) error {
@@ -53,18 +62,18 @@ func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadP
5362
defer tmp.RemoveAll()
5463

5564
// Download index file
56-
indexFileName := path.Base(res.URL.Path) // == package_index.json[.gz]
57-
tmpIndexPath := tmp.Join(indexFileName)
58-
if err := httpclient.DownloadFile(tmpIndexPath, res.URL.String(), "", tr("Downloading index: %s", indexFileName), downloadCB, nil, downloader.NoResume); err != nil {
65+
downloadFileName := path.Base(res.URL.Path) // == package_index.json[.gz] || package_index.tar.bz2
66+
indexFileName := res.IndexFileName() // == package_index.json
67+
tmpIndexPath := tmp.Join(downloadFileName)
68+
if err := httpclient.DownloadFile(tmpIndexPath, res.URL.String(), "", tr("Downloading index: %s", downloadFileName), downloadCB, nil, downloader.NoResume); err != nil {
5969
return &arduino.FailedDownloadError{Message: tr("Error downloading index '%s'", res.URL), Cause: err}
6070
}
6171

6272
var signaturePath, tmpSignaturePath *paths.Path
6373
hasSignature := false
6474

6575
// Expand the index if it is compressed
66-
if strings.HasSuffix(indexFileName, ".tar.bz2") {
67-
indexFileName = strings.TrimSuffix(indexFileName, ".tar.bz2") + ".json" // == package_index.json
76+
if strings.HasSuffix(downloadFileName, ".tar.bz2") {
6877
signatureFileName := indexFileName + ".sig"
6978
signaturePath = destDir.Join(signatureFileName)
7079

@@ -95,8 +104,7 @@ func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadP
95104
} else {
96105
logrus.Infof("No signature %s found in package index archive %s", signatureFileName, tmpArchivePath.Base())
97106
}
98-
} else if strings.HasSuffix(indexFileName, ".gz") {
99-
indexFileName = strings.TrimSuffix(indexFileName, ".gz") // == package_index.json
107+
} else if strings.HasSuffix(downloadFileName, ".gz") {
100108
tmpUnzippedIndexPath := tmp.Join(indexFileName)
101109
if err := paths.GUnzip(tmpIndexPath, tmpUnzippedIndexPath); err != nil {
102110
return &arduino.PermissionDeniedError{Message: tr("Error extracting %s", indexFileName), Cause: err}

Diff for: commands/core/list.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ import (
2525
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2626
)
2727

28-
// GetPlatforms returns a list of installed platforms, optionally filtered by
28+
// PlatformList returns a list of installed platforms, optionally filtered by
2929
// those requiring an update.
30-
func GetPlatforms(req *rpc.PlatformListRequest) ([]*rpc.Platform, error) {
30+
func PlatformList(req *rpc.PlatformListRequest) (*rpc.PlatformListResponse, error) {
3131
pme, release := commands.GetPackageManagerExplorer(req)
3232
if pme == nil {
3333
return nil, &arduino.InvalidInstanceError{}
@@ -85,5 +85,5 @@ func GetPlatforms(req *rpc.PlatformListRequest) ([]*rpc.Platform, error) {
8585
}
8686
return false
8787
})
88-
return res, nil
88+
return &rpc.PlatformListResponse{InstalledPlatforms: res}, nil
8989
}

Diff for: commands/daemon/daemon.go

+2-5
Original file line numberDiff line numberDiff line change
@@ -284,11 +284,8 @@ func (s *ArduinoCoreServerImpl) PlatformSearch(ctx context.Context, req *rpc.Pla
284284

285285
// PlatformList FIXMEDOC
286286
func (s *ArduinoCoreServerImpl) PlatformList(ctx context.Context, req *rpc.PlatformListRequest) (*rpc.PlatformListResponse, error) {
287-
platforms, err := core.GetPlatforms(req)
288-
if err != nil {
289-
return nil, convertErrorToRPCStatus(err)
290-
}
291-
return &rpc.PlatformListResponse{InstalledPlatforms: platforms}, nil
287+
platforms, err := core.PlatformList(req)
288+
return platforms, convertErrorToRPCStatus(err)
292289
}
293290

294291
// Upload FIXMEDOC

Diff for: commands/instances.go

+57-14
Original file line numberDiff line numberDiff line change
@@ -262,20 +262,11 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
262262
})
263263
}
264264

265-
{
266-
// We need to rebuild the PackageManager currently in use by this instance
267-
// in case this is not the first Init on this instances, that might happen
268-
// after reinitializing an instance after installing or uninstalling a core.
269-
// If this is not done the information of the uninstall core is kept in memory,
270-
// even if it should not.
271-
pmb, commitPackageManager := instance.pm.NewBuilder()
272-
273-
// Load packages index
274-
urls := []string{globals.DefaultIndexURL}
275-
if profile == nil {
276-
urls = append(urls, configuration.Settings.GetStringSlice("board_manager.additional_urls")...)
277-
}
278-
for _, u := range urls {
265+
// Perform first-update of indexes if needed
266+
defaultIndexURL, _ := utils.URLParse(globals.DefaultIndexURL)
267+
allPackageIndexUrls := []*url.URL{defaultIndexURL}
268+
if profile == nil {
269+
for _, u := range configuration.Settings.GetStringSlice("board_manager.additional_urls") {
279270
URL, err := utils.URLParse(u)
280271
if err != nil {
281272
e := &arduino.InitFailedError{
@@ -286,7 +277,21 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
286277
responseError(e.ToRPCStatus())
287278
continue
288279
}
280+
allPackageIndexUrls = append(allPackageIndexUrls, URL)
281+
}
282+
}
283+
firstUpdate(context.Background(), req.GetInstance(), downloadCallback, allPackageIndexUrls)
289284

285+
{
286+
// We need to rebuild the PackageManager currently in use by this instance
287+
// in case this is not the first Init on this instances, that might happen
288+
// after reinitializing an instance after installing or uninstalling a core.
289+
// If this is not done the information of the uninstall core is kept in memory,
290+
// even if it should not.
291+
pmb, commitPackageManager := instance.pm.NewBuilder()
292+
293+
// Load packages index
294+
for _, URL := range allPackageIndexUrls {
290295
if URL.Scheme == "file" {
291296
_, err := pmb.LoadPackageIndexFromFile(paths.New(URL.Path))
292297
if err != nil {
@@ -595,3 +600,41 @@ func LoadSketch(ctx context.Context, req *rpc.LoadSketchRequest) (*rpc.LoadSketc
595600
RootFolderFiles: rootFolderFiles,
596601
}, nil
597602
}
603+
604+
// firstUpdate downloads libraries and packages indexes if they don't exist.
605+
// This ideally is only executed the first time the CLI is run.
606+
func firstUpdate(ctx context.Context, instance *rpc.Instance, downloadCb func(msg *rpc.DownloadProgress), externalPackageIndexes []*url.URL) error {
607+
// Gets the data directory to verify if library_index.json and package_index.json exist
608+
dataDir := configuration.DataDir(configuration.Settings)
609+
libraryIndex := dataDir.Join("library_index.json")
610+
611+
if libraryIndex.NotExist() {
612+
// The library_index.json file doesn't exists, that means the CLI is run for the first time
613+
// so we proceed with the first update that downloads the file
614+
req := &rpc.UpdateLibrariesIndexRequest{Instance: instance}
615+
if err := UpdateLibrariesIndex(ctx, req, downloadCb); err != nil {
616+
return err
617+
}
618+
}
619+
620+
for _, URL := range externalPackageIndexes {
621+
if URL.Scheme == "file" {
622+
continue
623+
}
624+
packageIndexFileName := (&resources.IndexResource{URL: URL}).IndexFileName()
625+
packageIndexFile := dataDir.Join(packageIndexFileName)
626+
if packageIndexFile.NotExist() {
627+
// The index file doesn't exists, that means the CLI is run for the first time,
628+
// or the 3rd party package index URL has just been added. Similarly to the
629+
// library update we download that file and all the other package indexes from
630+
// additional_urls
631+
req := &rpc.UpdateIndexRequest{Instance: instance}
632+
if err := UpdateIndex(ctx, req, downloadCb); err != nil {
633+
return err
634+
}
635+
break
636+
}
637+
}
638+
639+
return nil
640+
}

Diff for: docs/UPGRADING.md

+34
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,40 @@ has been removed as well.
118118

119119
That method was outdated and must not be used.
120120

121+
### golang API: method `github.com/arduino/arduino-cli/commands/core/GetPlatforms` renamed
122+
123+
The following method in `github.com/arduino/arduino-cli/commands/core`:
124+
125+
```go
126+
func GetPlatforms(req *rpc.PlatformListRequest) ([]*rpc.Platform, error) { ... }
127+
```
128+
129+
has been changed to:
130+
131+
```go
132+
func PlatformList(req *rpc.PlatformListRequest) (*rpc.PlatformListResponse, error) { ... }
133+
```
134+
135+
now it better follows the gRPC API interface. Old code like the following:
136+
137+
```go
138+
platforms, _ := core.GetPlatforms(&rpc.PlatformListRequest{Instance: inst})
139+
for _, i := range platforms {
140+
...
141+
}
142+
```
143+
144+
must be changed as follows:
145+
146+
```go
147+
// Use PlatformList function instead of GetPlatforms
148+
platforms, _ := core.PlatformList(&rpc.PlatformListRequest{Instance: inst})
149+
// Access installed platforms through the .InstalledPlatforms field
150+
for _, i := range platforms.InstalledPlatforms {
151+
...
152+
}
153+
```
154+
121155
## 0.31.0
122156

123157
### Added `post_install` script support for tools

Diff for: internal/cli/arguments/completion.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,14 @@ func GetInstalledProgrammers() []string {
124124
func GetUninstallableCores() []string {
125125
inst := instance.CreateAndInit()
126126

127-
platforms, _ := core.GetPlatforms(&rpc.PlatformListRequest{
127+
platforms, _ := core.PlatformList(&rpc.PlatformListRequest{
128128
Instance: inst,
129129
UpdatableOnly: false,
130130
All: false,
131131
})
132132
var res []string
133133
// transform the data structure for the completion
134-
for _, i := range platforms {
134+
for _, i := range platforms.InstalledPlatforms {
135135
res = append(res, i.Id+"\t"+i.Name)
136136
}
137137
return res

Diff for: internal/cli/arguments/reference.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,15 @@ func ParseReference(arg string) (*Reference, error) {
9292
ret.Architecture = toks[1]
9393

9494
// Now that we have the required informations in `ret` we can
95-
// try to use core.GetPlatforms to optimize what the user typed
95+
// try to use core.PlatformList to optimize what the user typed
9696
// (by replacing the PackageName and Architecture in ret with the content of core.GetPlatform())
97-
platforms, _ := core.GetPlatforms(&rpc.PlatformListRequest{
97+
platforms, _ := core.PlatformList(&rpc.PlatformListRequest{
9898
Instance: instance.CreateAndInit(),
9999
UpdatableOnly: false,
100100
All: true, // this is true because we want also the installable platforms
101101
})
102102
foundPlatforms := []string{}
103-
for _, platform := range platforms {
103+
for _, platform := range platforms.InstalledPlatforms {
104104
platformID := platform.GetId()
105105
platformUser := ret.PackageName + ":" + ret.Architecture
106106
// At first we check if the platform the user is searching for matches an available one,

Diff for: internal/cli/core/list.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,15 @@ func List(inst *rpc.Instance, all bool, updatableOnly bool) {
6060

6161
// GetList returns a list of installed platforms.
6262
func GetList(inst *rpc.Instance, all bool, updatableOnly bool) []*rpc.Platform {
63-
platforms, err := core.GetPlatforms(&rpc.PlatformListRequest{
63+
platforms, err := core.PlatformList(&rpc.PlatformListRequest{
6464
Instance: inst,
6565
UpdatableOnly: updatableOnly,
6666
All: all,
6767
})
6868
if err != nil {
6969
feedback.Fatal(tr("Error listing platforms: %v", err), feedback.ErrGeneric)
7070
}
71-
return platforms
71+
return platforms.InstalledPlatforms
7272
}
7373

7474
// output from this command requires special formatting, let's create a dedicated

Diff for: internal/cli/core/search.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,16 @@ func initSearchCommand() *cobra.Command {
5858
const indexUpdateInterval = "24h"
5959

6060
func runSearchCommand(cmd *cobra.Command, args []string) {
61-
inst, status := instance.Create()
62-
if status != nil {
63-
feedback.Fatal(tr("Error creating instance: %v", status), feedback.ErrGeneric)
64-
}
61+
inst := instance.CreateAndInit()
6562

6663
if indexesNeedUpdating(indexUpdateInterval) {
6764
err := commands.UpdateIndex(context.Background(), &rpc.UpdateIndexRequest{Instance: inst}, feedback.ProgressBar())
6865
if err != nil {
6966
feedback.FatalError(err, feedback.ErrGeneric)
7067
}
68+
instance.Init(inst)
7169
}
7270

73-
instance.Init(inst)
74-
7571
arguments := strings.ToLower(strings.Join(args, " "))
7672
logrus.Infof("Executing `arduino-cli core search` with args: '%s'", arguments)
7773

Diff for: internal/cli/core/update_index.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func initUpdateIndexCommand() *cobra.Command {
4040
}
4141

4242
func runUpdateIndexCommand(cmd *cobra.Command, args []string) {
43-
inst := instance.CreateInstanceAndRunFirstUpdate()
43+
inst := instance.CreateAndInit()
4444
logrus.Info("Executing `arduino-cli core update-index`")
4545
UpdateIndex(inst)
4646
}

Diff for: internal/cli/core/upgrade.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,20 @@ func runUpgradeCommand(args []string, skipPostInstall bool) {
6060
func Upgrade(inst *rpc.Instance, args []string, skipPostInstall bool) {
6161
// if no platform was passed, upgrade allthethings
6262
if len(args) == 0 {
63-
targets, err := core.GetPlatforms(&rpc.PlatformListRequest{
63+
targets, err := core.PlatformList(&rpc.PlatformListRequest{
6464
Instance: inst,
6565
UpdatableOnly: true,
6666
})
6767
if err != nil {
6868
feedback.Fatal(tr("Error retrieving core list: %v", err), feedback.ErrGeneric)
6969
}
7070

71-
if len(targets) == 0 {
71+
if len(targets.InstalledPlatforms) == 0 {
7272
feedback.Print(tr("All the cores are already at the latest version"))
7373
return
7474
}
7575

76-
for _, t := range targets {
76+
for _, t := range targets.InstalledPlatforms {
7777
args = append(args, t.Id)
7878
}
7979
}

0 commit comments

Comments
 (0)