@@ -20,7 +20,9 @@ import (
2020 "fmt"
2121 "net/url"
2222 "path/filepath"
23+ "runtime"
2324 "strings"
25+ "time"
2426
2527 "github.com/arduino/arduino-cli/commands/cmderrors"
2628 "github.com/arduino/arduino-cli/commands/internal/instances"
@@ -406,30 +408,59 @@ func Destroy(ctx context.Context, req *rpc.DestroyRequest) (*rpc.DestroyResponse
406408}
407409
408410// UpdateLibrariesIndex updates the library_index.json
409- func UpdateLibrariesIndex (ctx context.Context , req * rpc.UpdateLibrariesIndexRequest , downloadCB rpc.DownloadProgressCB ) error {
411+ func UpdateLibrariesIndex (ctx context.Context , req * rpc.UpdateLibrariesIndexRequest , downloadCB rpc.DownloadProgressCB ) ( * rpc. UpdateLibrariesIndexResponse_Result , error ) {
410412 logrus .Info ("Updating libraries index" )
413+
411414 pme , release , err := instances .GetPackageManagerExplorer (req .GetInstance ())
412415 if err != nil {
413- return err
416+ return nil , err
414417 }
415418 indexDir := pme .IndexDir
416419 release ()
417420
421+ index := globals .LibrariesIndexResource
422+ result := func (status rpc.IndexUpdateReport_Status ) * rpc.UpdateLibrariesIndexResponse_Result {
423+ return & rpc.UpdateLibrariesIndexResponse_Result {
424+ LibrariesIndex : & rpc.IndexUpdateReport {
425+ IndexUrl : globals .LibrariesIndexResource .URL .String (),
426+ Status : status ,
427+ },
428+ }
429+ }
430+
431+ // Create the index directory if it doesn't exist
418432 if err := indexDir .MkdirAll (); err != nil {
419- return & cmderrors.PermissionDeniedError {Message : tr ("Could not create index directory" ), Cause : err }
433+ return result (rpc .IndexUpdateReport_STATUS_FAILED ), & cmderrors.PermissionDeniedError {Message : tr ("Could not create index directory" ), Cause : err }
434+ }
435+
436+ // Check if the index file is already up-to-date
437+ indexFileName , _ := index .IndexFileName ()
438+ if info , err := indexDir .Join (indexFileName ).Stat (); err == nil {
439+ ageSecs := int64 (time .Since (info .ModTime ()).Seconds ())
440+ if ageSecs < req .GetUpdateIfOlderThanSecs () {
441+ return result (rpc .IndexUpdateReport_STATUS_ALREADY_UP_TO_DATE ), nil
442+ }
420443 }
421444
445+ // Perform index update
422446 if err := globals .LibrariesIndexResource .Download (indexDir , downloadCB ); err != nil {
423- return err
447+ return nil , err
424448 }
425449
426- return nil
450+ return result ( rpc . IndexUpdateReport_STATUS_UPDATED ), nil
427451}
428452
429453// UpdateIndex FIXMEDOC
430- func UpdateIndex (ctx context.Context , req * rpc.UpdateIndexRequest , downloadCB rpc.DownloadProgressCB ) error {
454+ func UpdateIndex (ctx context.Context , req * rpc.UpdateIndexRequest , downloadCB rpc.DownloadProgressCB ) ( * rpc. UpdateIndexResponse_Result , error ) {
431455 if ! instances .IsValid (req .GetInstance ()) {
432- return & cmderrors.InvalidInstanceError {}
456+ return nil , & cmderrors.InvalidInstanceError {}
457+ }
458+
459+ report := func (indexURL * url.URL , status rpc.IndexUpdateReport_Status ) * rpc.IndexUpdateReport {
460+ return & rpc.IndexUpdateReport {
461+ IndexUrl : indexURL .String (),
462+ Status : status ,
463+ }
433464 }
434465
435466 indexpath := configuration .DataDir (configuration .Settings )
@@ -440,46 +471,75 @@ func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB rp
440471 }
441472
442473 failed := false
474+ result := & rpc.UpdateIndexResponse_Result {}
443475 for _ , u := range urls {
444- URL , err := utils . URLParse (u )
476+ URL , err := url . Parse (u )
445477 if err != nil {
446478 logrus .Warnf ("unable to parse additional URL: %s" , u )
447479 msg := fmt .Sprintf ("%s: %v" , tr ("Unable to parse URL" ), err )
448480 downloadCB .Start (u , tr ("Downloading index: %s" , u ))
449481 downloadCB .End (false , msg )
450482 failed = true
483+ result .UpdatedIndexes = append (result .UpdatedIndexes , report (URL , rpc .IndexUpdateReport_STATUS_FAILED ))
451484 continue
452485 }
453486
454487 logrus .WithField ("url" , URL ).Print ("Updating index" )
455488
456489 if URL .Scheme == "file" {
457- downloadCB .Start (u , tr ("Downloading index: %s" , filepath .Base (URL .Path )))
458490 path := paths .New (URL .Path )
491+ if URL .Scheme == "file" && runtime .GOOS == "windows" && len (URL .Path ) > 1 {
492+ // https://github.com/golang/go/issues/32456
493+ // Parsed local file URLs on Windows are returned with a leading / so we remove it
494+ path = paths .New (URL .Path [1 :])
495+ }
459496 if _ , err := packageindex .LoadIndexNoSign (path ); err != nil {
460497 msg := fmt .Sprintf ("%s: %v" , tr ("Invalid package index in %s" , path ), err )
498+ downloadCB .Start (u , tr ("Downloading index: %s" , filepath .Base (URL .Path )))
461499 downloadCB .End (false , msg )
462500 failed = true
501+ result .UpdatedIndexes = append (result .UpdatedIndexes , report (URL , rpc .IndexUpdateReport_STATUS_FAILED ))
463502 } else {
464- downloadCB . End ( true , "" )
503+ result . UpdatedIndexes = append ( result . UpdatedIndexes , report ( URL , rpc . IndexUpdateReport_STATUS_SKIPPED ) )
465504 }
466505 continue
467506 }
468507
508+ // Check if the index is up-to-date
469509 indexResource := resources.IndexResource {URL : URL }
510+ indexFileName , err := indexResource .IndexFileName ()
511+ if err != nil {
512+ downloadCB .Start (u , tr ("Downloading index: %s" , filepath .Base (URL .Path )))
513+ downloadCB .End (false , tr ("Invalid index URL: %s" , err ))
514+ failed = true
515+ result .UpdatedIndexes = append (result .UpdatedIndexes , report (URL , rpc .IndexUpdateReport_STATUS_FAILED ))
516+ continue
517+ }
518+ indexFile := indexpath .Join (indexFileName )
519+ if info , err := indexFile .Stat (); err == nil {
520+ ageSecs := int64 (time .Since (info .ModTime ()).Seconds ())
521+ if ageSecs < req .GetUpdateIfOlderThanSecs () {
522+ result .UpdatedIndexes = append (result .UpdatedIndexes , report (URL , rpc .IndexUpdateReport_STATUS_ALREADY_UP_TO_DATE ))
523+ continue
524+ }
525+ }
526+
470527 if strings .HasSuffix (URL .Host , "arduino.cc" ) && strings .HasSuffix (URL .Path , ".json" ) {
471528 indexResource .SignatureURL , _ = url .Parse (u ) // should not fail because we already parsed it
472529 indexResource .SignatureURL .Path += ".sig"
473530 }
474531 if err := indexResource .Download (indexpath , downloadCB ); err != nil {
475532 failed = true
533+ result .UpdatedIndexes = append (result .UpdatedIndexes , report (URL , rpc .IndexUpdateReport_STATUS_FAILED ))
534+ } else {
535+ result .UpdatedIndexes = append (result .UpdatedIndexes , report (URL , rpc .IndexUpdateReport_STATUS_UPDATED ))
476536 }
477537 }
478538
479539 if failed {
480- return & cmderrors.FailedDownloadError {Message : tr ("Some indexes could not be updated." )}
540+ return result , & cmderrors.FailedDownloadError {Message : tr ("Some indexes could not be updated." )}
481541 }
482- return nil
542+ return result , nil
483543}
484544
485545// firstUpdate downloads libraries and packages indexes if they don't exist.
@@ -493,7 +553,7 @@ func firstUpdate(ctx context.Context, instance *rpc.Instance, downloadCb func(ms
493553 // The library_index.json file doesn't exists, that means the CLI is run for the first time
494554 // so we proceed with the first update that downloads the file
495555 req := & rpc.UpdateLibrariesIndexRequest {Instance : instance }
496- if err := UpdateLibrariesIndex (ctx , req , downloadCb ); err != nil {
556+ if _ , err := UpdateLibrariesIndex (ctx , req , downloadCb ); err != nil {
497557 return err
498558 }
499559 }
@@ -515,7 +575,7 @@ func firstUpdate(ctx context.Context, instance *rpc.Instance, downloadCb func(ms
515575 // library update we download that file and all the other package indexes from
516576 // additional_urls
517577 req := & rpc.UpdateIndexRequest {Instance : instance }
518- if err := UpdateIndex (ctx , req , downloadCb ); err != nil {
578+ if _ , err := UpdateIndex (ctx , req , downloadCb ); err != nil {
519579 return err
520580 }
521581 break
0 commit comments