Skip to content

Commit 2a634d7

Browse files
committed
gRPC UpdateIndex and UpdateLibrariesIndex improvements
The two calls now have the update_if_older_than_secs field that allows to avoid updating the index if it has been already updated. Also the response is more explicit with oneof(..) clause and the status of each update (in case of multiple index update) is returned in the response.
1 parent ad9936f commit 2a634d7

File tree

9 files changed

+1334
-794
lines changed

9 files changed

+1334
-794
lines changed

Diff for: commands/daemon/daemon.go

+22-4
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,36 @@ func (s *ArduinoCoreServerImpl) Destroy(ctx context.Context, req *rpc.DestroyReq
121121
// UpdateIndex FIXMEDOC
122122
func (s *ArduinoCoreServerImpl) UpdateIndex(req *rpc.UpdateIndexRequest, stream rpc.ArduinoCoreService_UpdateIndexServer) error {
123123
syncSend := NewSynchronizedSend(stream.Send)
124-
err := commands.UpdateIndex(stream.Context(), req,
125-
func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.UpdateIndexResponse{DownloadProgress: p}) },
124+
res, err := commands.UpdateIndex(stream.Context(), req,
125+
func(p *rpc.DownloadProgress) {
126+
syncSend.Send(&rpc.UpdateIndexResponse{
127+
Message: &rpc.UpdateIndexResponse_DownloadProgress{DownloadProgress: p},
128+
})
129+
},
126130
)
131+
if res != nil {
132+
syncSend.Send(&rpc.UpdateIndexResponse{
133+
Message: &rpc.UpdateIndexResponse_Result_{Result: res},
134+
})
135+
}
127136
return convertErrorToRPCStatus(err)
128137
}
129138

130139
// UpdateLibrariesIndex FIXMEDOC
131140
func (s *ArduinoCoreServerImpl) UpdateLibrariesIndex(req *rpc.UpdateLibrariesIndexRequest, stream rpc.ArduinoCoreService_UpdateLibrariesIndexServer) error {
132141
syncSend := NewSynchronizedSend(stream.Send)
133-
err := commands.UpdateLibrariesIndex(stream.Context(), req,
134-
func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.UpdateLibrariesIndexResponse{DownloadProgress: p}) },
142+
res, err := commands.UpdateLibrariesIndex(stream.Context(), req,
143+
func(p *rpc.DownloadProgress) {
144+
syncSend.Send(&rpc.UpdateLibrariesIndexResponse{
145+
Message: &rpc.UpdateLibrariesIndexResponse_DownloadProgress{DownloadProgress: p},
146+
})
147+
},
135148
)
149+
if res != nil {
150+
syncSend.Send(&rpc.UpdateLibrariesIndexResponse{
151+
Message: &rpc.UpdateLibrariesIndexResponse_Result_{Result: res},
152+
})
153+
}
136154
return convertErrorToRPCStatus(err)
137155
}
138156

Diff for: commands/instances.go

+68-11
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"net/url"
2222
"path/filepath"
2323
"strings"
24+
"time"
2425

2526
"github.com/arduino/arduino-cli/commands/cmderrors"
2627
"github.com/arduino/arduino-cli/commands/internal/instances"
@@ -406,30 +407,59 @@ func Destroy(ctx context.Context, req *rpc.DestroyRequest) (*rpc.DestroyResponse
406407
}
407408

408409
// UpdateLibrariesIndex updates the library_index.json
409-
func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexRequest, downloadCB rpc.DownloadProgressCB) error {
410+
func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexRequest, downloadCB rpc.DownloadProgressCB) (*rpc.UpdateLibrariesIndexResponse_Result, error) {
410411
logrus.Info("Updating libraries index")
412+
411413
pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance())
412414
if err != nil {
413-
return err
415+
return nil, err
414416
}
415417
indexDir := pme.IndexDir
416418
release()
417419

420+
index := globals.LibrariesIndexResource
421+
result := func(status rpc.IndexUpdateReport_Status) *rpc.UpdateLibrariesIndexResponse_Result {
422+
return &rpc.UpdateLibrariesIndexResponse_Result{
423+
LibrariesIndex: &rpc.IndexUpdateReport{
424+
IndexUrl: globals.LibrariesIndexResource.URL.String(),
425+
Status: status,
426+
},
427+
}
428+
}
429+
430+
// Create the index directory if it doesn't exist
418431
if err := indexDir.MkdirAll(); err != nil {
419-
return &cmderrors.PermissionDeniedError{Message: tr("Could not create index directory"), Cause: err}
432+
return result(rpc.IndexUpdateReport_STATUS_FAILED), &cmderrors.PermissionDeniedError{Message: tr("Could not create index directory"), Cause: err}
420433
}
421434

435+
// Check if the index file is already up-to-date
436+
indexFileName, _ := index.IndexFileName()
437+
if info, err := indexDir.Join(indexFileName).Stat(); err == nil {
438+
ageSecs := int64(time.Since(info.ModTime()).Seconds())
439+
if ageSecs < req.GetUpdateIfOlderThanSecs() {
440+
return result(rpc.IndexUpdateReport_STATUS_ALREADY_UP_TO_DATE), nil
441+
}
442+
}
443+
444+
// Perform index update
422445
if err := globals.LibrariesIndexResource.Download(indexDir, downloadCB); err != nil {
423-
return err
446+
return nil, err
424447
}
425448

426-
return nil
449+
return result(rpc.IndexUpdateReport_STATUS_UPDATED), nil
427450
}
428451

429452
// UpdateIndex FIXMEDOC
430-
func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB rpc.DownloadProgressCB) error {
453+
func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB rpc.DownloadProgressCB) (*rpc.UpdateIndexResponse_Result, error) {
431454
if !instances.IsValid(req.GetInstance()) {
432-
return &cmderrors.InvalidInstanceError{}
455+
return nil, &cmderrors.InvalidInstanceError{}
456+
}
457+
458+
report := func(indexURL *url.URL, status rpc.IndexUpdateReport_Status) *rpc.IndexUpdateReport {
459+
return &rpc.IndexUpdateReport{
460+
IndexUrl: indexURL.String(),
461+
Status: status,
462+
}
433463
}
434464

435465
indexpath := configuration.DataDir(configuration.Settings)
@@ -440,6 +470,7 @@ func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB rp
440470
}
441471

442472
failed := false
473+
result := &rpc.UpdateIndexResponse_Result{}
443474
for _, u := range urls {
444475
URL, err := utils.URLParse(u)
445476
if err != nil {
@@ -448,6 +479,7 @@ func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB rp
448479
downloadCB.Start(u, tr("Downloading index: %s", u))
449480
downloadCB.End(false, msg)
450481
failed = true
482+
result.UpdatedIndexes = append(result.UpdatedIndexes, report(URL, rpc.IndexUpdateReport_STATUS_FAILED))
451483
continue
452484
}
453485

@@ -460,26 +492,51 @@ func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB rp
460492
msg := fmt.Sprintf("%s: %v", tr("Invalid package index in %s", path), err)
461493
downloadCB.End(false, msg)
462494
failed = true
495+
result.UpdatedIndexes = append(result.UpdatedIndexes, report(URL, rpc.IndexUpdateReport_STATUS_FAILED))
463496
} else {
464497
downloadCB.End(true, "")
498+
result.UpdatedIndexes = append(result.UpdatedIndexes, report(URL, rpc.IndexUpdateReport_STATUS_SKIPPED))
465499
}
466500
continue
467501
}
468502

503+
// Check if the index is up-to-date
469504
indexResource := resources.IndexResource{URL: URL}
505+
indexFileName, err := indexResource.IndexFileName()
506+
if err != nil {
507+
downloadCB.Start(u, tr("Downloading index: %s", filepath.Base(URL.Path)))
508+
downloadCB.End(false, tr("Invalid index URL: %s", err))
509+
failed = true
510+
result.UpdatedIndexes = append(result.UpdatedIndexes, report(URL, rpc.IndexUpdateReport_STATUS_FAILED))
511+
continue
512+
}
513+
indexFile := indexpath.Join(indexFileName)
514+
if info, err := indexFile.Stat(); err == nil {
515+
ageSecs := int64(time.Since(info.ModTime()).Seconds())
516+
if ageSecs < req.GetUpdateIfOlderThanSecs() {
517+
downloadCB.Start(u, tr("Downloading index: %s", filepath.Base(URL.Path)))
518+
downloadCB.End(true, tr("Index is already up-to-date"))
519+
result.UpdatedIndexes = append(result.UpdatedIndexes, report(URL, rpc.IndexUpdateReport_STATUS_ALREADY_UP_TO_DATE))
520+
continue
521+
}
522+
}
523+
470524
if strings.HasSuffix(URL.Host, "arduino.cc") && strings.HasSuffix(URL.Path, ".json") {
471525
indexResource.SignatureURL, _ = url.Parse(u) // should not fail because we already parsed it
472526
indexResource.SignatureURL.Path += ".sig"
473527
}
474528
if err := indexResource.Download(indexpath, downloadCB); err != nil {
475529
failed = true
530+
result.UpdatedIndexes = append(result.UpdatedIndexes, report(URL, rpc.IndexUpdateReport_STATUS_FAILED))
531+
} else {
532+
result.UpdatedIndexes = append(result.UpdatedIndexes, report(URL, rpc.IndexUpdateReport_STATUS_UPDATED))
476533
}
477534
}
478535

479536
if failed {
480-
return &cmderrors.FailedDownloadError{Message: tr("Some indexes could not be updated.")}
537+
return result, &cmderrors.FailedDownloadError{Message: tr("Some indexes could not be updated.")}
481538
}
482-
return nil
539+
return result, nil
483540
}
484541

485542
// firstUpdate downloads libraries and packages indexes if they don't exist.
@@ -493,7 +550,7 @@ func firstUpdate(ctx context.Context, instance *rpc.Instance, downloadCb func(ms
493550
// The library_index.json file doesn't exists, that means the CLI is run for the first time
494551
// so we proceed with the first update that downloads the file
495552
req := &rpc.UpdateLibrariesIndexRequest{Instance: instance}
496-
if err := UpdateLibrariesIndex(ctx, req, downloadCb); err != nil {
553+
if _, err := UpdateLibrariesIndex(ctx, req, downloadCb); err != nil {
497554
return err
498555
}
499556
}
@@ -515,7 +572,7 @@ func firstUpdate(ctx context.Context, instance *rpc.Instance, downloadCb func(ms
515572
// library update we download that file and all the other package indexes from
516573
// additional_urls
517574
req := &rpc.UpdateIndexRequest{Instance: instance}
518-
if err := UpdateIndex(ctx, req, downloadCb); err != nil {
575+
if _, err := UpdateIndex(ctx, req, downloadCb); err != nil {
519576
return err
520577
}
521578
break

Diff for: docs/UPGRADING.md

+70
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,76 @@ Here you can find a list of migration guides to handle breaking changes between
44

55
## 0.36.0
66

7+
### The gRPC `cc.arduino.cli.commands.v1.UpdateIndexResponse` and `UpdateLibrariesIndexResponse` have changed.
8+
9+
The responses coming from the update index commands:
10+
11+
```proto
12+
message UpdateIndexResponse {
13+
// Progress of the package index download.
14+
DownloadProgress download_progress = 1;
15+
}
16+
17+
message UpdateLibrariesIndexResponse {
18+
// Progress of the libraries index download.
19+
DownloadProgress download_progress = 1;
20+
}
21+
```
22+
23+
are now more explicit and contains details about the result of the operation:
24+
25+
```proto
26+
message UpdateIndexResponse {
27+
message Result {
28+
// The result of the packages index update.
29+
repeated IndexUpdateReport updated_indexes = 1;
30+
}
31+
oneof message {
32+
// Progress of the package index download.
33+
DownloadProgress download_progress = 1;
34+
// The result of the index update.
35+
Result result = 2;
36+
}
37+
}
38+
39+
message UpdateLibrariesIndexResponse {
40+
message Result {
41+
// The result of the libraries index update.
42+
IndexUpdateReport libraries_index = 1;
43+
}
44+
oneof message {
45+
// Progress of the libraries index download.
46+
DownloadProgress download_progress = 1;
47+
// The result of the index update.
48+
Result result = 2;
49+
}
50+
}
51+
```
52+
53+
The `IndexUpdateReport` message contains details for each index update operation performed:
54+
55+
```proto
56+
message IndexUpdateReport {
57+
enum Status {
58+
// The status of the index update is unspecified.
59+
STATUS_UNSPECIFIED = 0;
60+
// The index has been successfully updated.
61+
STATUS_UPDATED = 1;
62+
// The index was already up to date.
63+
STATUS_ALREADY_UP_TO_DATE = 2;
64+
// The index update failed.
65+
STATUS_FAILED = 3;
66+
// The index update was skipped.
67+
STATUS_SKIPPED = 4;
68+
}
69+
70+
// The URL of the index that was updated.
71+
string index_url = 1;
72+
// The result of the index update.
73+
Status status = 2;
74+
}
75+
```
76+
777
### The gRPC `cc.arduino.cli.commands.v1.Profile` message has been removed in favor of `SketchProfile`
878

979
The message `Profile` has been replaced with `SketchProfile` in the `InitResponse.profile` field:

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

+13-65
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,11 @@ import (
1919
"context"
2020
"fmt"
2121
"os"
22-
"path"
2322
"strings"
2423
"time"
2524

2625
"github.com/arduino/arduino-cli/commands"
2726
"github.com/arduino/arduino-cli/commands/core"
28-
"github.com/arduino/arduino-cli/internal/arduino/globals"
29-
"github.com/arduino/arduino-cli/internal/arduino/utils"
30-
"github.com/arduino/arduino-cli/internal/cli/configuration"
3127
"github.com/arduino/arduino-cli/internal/cli/feedback"
3228
"github.com/arduino/arduino-cli/internal/cli/feedback/result"
3329
"github.com/arduino/arduino-cli/internal/cli/feedback/table"
@@ -55,17 +51,24 @@ func initSearchCommand() *cobra.Command {
5551
}
5652

5753
// indexUpdateInterval specifies the time threshold over which indexes are updated
58-
const indexUpdateInterval = "24h"
54+
const indexUpdateInterval = 24 * time.Hour
5955

6056
func runSearchCommand(cmd *cobra.Command, args []string, allVersions bool) {
6157
inst := instance.CreateAndInit()
6258

63-
if indexesNeedUpdating(indexUpdateInterval) {
64-
err := commands.UpdateIndex(context.Background(), &rpc.UpdateIndexRequest{Instance: inst}, feedback.ProgressBar())
65-
if err != nil {
66-
feedback.FatalError(err, feedback.ErrGeneric)
59+
res, err := commands.UpdateIndex(
60+
context.Background(),
61+
&rpc.UpdateIndexRequest{Instance: inst, UpdateIfOlderThanSecs: int64(indexUpdateInterval.Seconds())},
62+
feedback.ProgressBar())
63+
if err != nil {
64+
feedback.FatalError(err, feedback.ErrGeneric)
65+
}
66+
for _, idxRes := range res.GetUpdatedIndexes() {
67+
if idxRes.GetStatus() == rpc.IndexUpdateReport_STATUS_UPDATED {
68+
// At least one index has been updated, reinitialize the instance
69+
instance.Init(inst)
70+
break
6771
}
68-
instance.Init(inst)
6972
}
7073

7174
arguments := strings.ToLower(strings.Join(args, " "))
@@ -134,58 +137,3 @@ func (sr searchResults) String() string {
134137
}
135138
return t.Render()
136139
}
137-
138-
// indexesNeedUpdating returns whether one or more index files need updating.
139-
// A duration string must be provided to calculate the time threshold
140-
// used to update the indexes, if the duration is not valid a default
141-
// of 24 hours is used.
142-
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
143-
func indexesNeedUpdating(duration string) bool {
144-
indexpath := configuration.DataDir(configuration.Settings)
145-
146-
now := time.Now()
147-
modTimeThreshold, err := time.ParseDuration(duration)
148-
if err != nil {
149-
feedback.Fatal(tr("Invalid timeout: %s", err), feedback.ErrBadArgument)
150-
}
151-
152-
urls := []string{globals.DefaultIndexURL}
153-
urls = append(urls, configuration.Settings.GetStringSlice("board_manager.additional_urls")...)
154-
for _, u := range urls {
155-
URL, err := utils.URLParse(u)
156-
if err != nil {
157-
continue
158-
}
159-
160-
if URL.Scheme == "file" {
161-
// No need to update local files
162-
continue
163-
}
164-
165-
// should handle:
166-
// - package_index.json
167-
// - package_index.json.sig
168-
// - package_index.json.gz
169-
// - package_index.tar.bz2
170-
indexFileName := path.Base(URL.Path)
171-
indexFileName = strings.TrimSuffix(indexFileName, ".tar.bz2")
172-
indexFileName = strings.TrimSuffix(indexFileName, ".gz")
173-
indexFileName = strings.TrimSuffix(indexFileName, ".sig")
174-
indexFileName = strings.TrimSuffix(indexFileName, ".json")
175-
// and obtain package_index.json as result
176-
coreIndexPath := indexpath.Join(indexFileName + ".json")
177-
if coreIndexPath.NotExist() {
178-
return true
179-
}
180-
181-
info, err := coreIndexPath.Stat()
182-
if err != nil {
183-
return true
184-
}
185-
186-
if now.After(info.ModTime().Add(modTimeThreshold)) {
187-
return true
188-
}
189-
}
190-
return false
191-
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func runUpdateIndexCommand(cmd *cobra.Command, args []string) {
4747

4848
// UpdateIndex updates the index of platforms.
4949
func UpdateIndex(inst *rpc.Instance) {
50-
err := commands.UpdateIndex(context.Background(), &rpc.UpdateIndexRequest{Instance: inst}, feedback.ProgressBar())
50+
_, err := commands.UpdateIndex(context.Background(), &rpc.UpdateIndexRequest{Instance: inst}, feedback.ProgressBar())
5151
if err != nil {
5252
feedback.FatalError(err, feedback.ErrGeneric)
5353
}

0 commit comments

Comments
 (0)