Skip to content

Commit

Permalink
Removed global *viper.Viper settings instance
Browse files Browse the repository at this point in the history
Now the configuration is kept inside the arduinoCoreServiceImpl struct.

No more direct access to the configuration, the required config values
are passed as arguments or available trough struct fields.

Viper object is now embedded into a new configuration.Setting object.
This would allow to make better getters and setters methods in the next
commits.

HTTP downloader configuration is generated using the configuration.
  • Loading branch information
cmaglie committed Mar 26, 2024
1 parent 888a931 commit cdcec9b
Show file tree
Hide file tree
Showing 52 changed files with 460 additions and 441 deletions.
79 changes: 50 additions & 29 deletions commands/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ import (
func installTool(pm *packagemanager.PackageManager, tool *cores.ToolRelease, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
pme, release := pm.NewExplorer()
defer release()

taskCB(&rpc.TaskProgress{Name: tr("Downloading missing tool %s", tool)})
if err := pme.DownloadToolRelease(tool, nil, downloadCB); err != nil {
if err := pme.DownloadToolRelease(tool, downloadCB); err != nil {
return fmt.Errorf(tr("downloading %[1]s tool: %[2]s"), tool, err)
}
taskCB(&rpc.TaskProgress{Completed: true})
Expand All @@ -62,16 +63,16 @@ func installTool(pm *packagemanager.PackageManager, tool *cores.ToolRelease, dow

// Create a new Instance ready to be initialized, supporting directories are also created.
func (s *arduinoCoreServerImpl) Create(ctx context.Context, req *rpc.CreateRequest) (*rpc.CreateResponse, error) {
var userAgent []string
var userAgent string
if md, ok := metadata.FromIncomingContext(ctx); ok {
userAgent = md.Get("user-agent")
userAgent = strings.Join(md.Get("user-agent"), " ")
}
if len(userAgent) == 0 {
userAgent = []string{"gRPCClientUnknown/0.0.0"}
if userAgent == "" {
userAgent = "gRPCClientUnknown/0.0.0"
}

// Setup downloads directory
downloadsDir := configuration.DownloadsDir(configuration.Settings)
downloadsDir := configuration.DownloadsDir(s.settings)
if downloadsDir.NotExist() {
err := downloadsDir.MkdirAll()
if err != nil {
Expand All @@ -80,16 +81,20 @@ func (s *arduinoCoreServerImpl) Create(ctx context.Context, req *rpc.CreateReque
}

// Setup data directory
dataDir := configuration.DataDir(configuration.Settings)
packagesDir := configuration.PackagesDir(configuration.Settings)
dataDir := configuration.DataDir(s.settings)
packagesDir := configuration.PackagesDir(s.settings)
if packagesDir.NotExist() {
err := packagesDir.MkdirAll()
if err != nil {
return nil, &cmderrors.PermissionDeniedError{Message: tr("Failed to create data directory"), Cause: err}
}
}

inst, err := instances.Create(dataDir, packagesDir, downloadsDir, userAgent...)
config, err := s.settings.DownloaderConfig()
if err != nil {
return nil, err
}
inst, err := instances.Create(dataDir, packagesDir, downloadsDir, userAgent, config)
if err != nil {
return nil, err
}
Expand All @@ -108,6 +113,8 @@ func InitStreamResponseToCallbackFunction(ctx context.Context, cb func(r *rpc.In
// Failures don't stop the loading process, in case of loading failure the Platform or library
// is simply skipped and an error gRPC status is sent to responseCallback.
func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCoreService_InitServer) error {
ctx := stream.Context()

instance := req.GetInstance()
if !instances.IsValid(instance) {
return &cmderrors.InvalidInstanceError{}
Expand Down Expand Up @@ -170,7 +177,7 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor
defaultIndexURL, _ := utils.URLParse(globals.DefaultIndexURL)
allPackageIndexUrls := []*url.URL{defaultIndexURL}
if profile == nil {
for _, u := range configuration.Settings.GetStringSlice("board_manager.additional_urls") {
for _, u := range s.settings.GetStringSlice("board_manager.additional_urls") {
URL, err := utils.URLParse(u)
if err != nil {
e := &cmderrors.InitFailedError{
Expand All @@ -185,7 +192,7 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor
}
}

if err := firstUpdate(context.Background(), s, req.GetInstance(), downloadCallback, allPackageIndexUrls); err != nil {
if err := firstUpdate(ctx, s, req.GetInstance(), configuration.DataDir(s.settings), downloadCallback, allPackageIndexUrls); err != nil {
e := &cmderrors.InitFailedError{
Code: codes.InvalidArgument,
Cause: err,
Expand Down Expand Up @@ -238,15 +245,13 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor

// Load Platforms
if profile == nil {
for _, err := range pmb.LoadHardware() {
for _, err := range pmb.LoadHardware(s.settings) {
s := &cmderrors.PlatformLoadingError{Cause: err}
responseError(s.GRPCStatus())
}
} else {
// Load platforms from profile
errs := pmb.LoadHardwareForProfile(
profile, true, downloadCallback, taskCallback,
)
errs := pmb.LoadHardwareForProfile(profile, true, downloadCallback, taskCallback, s.settings)
for _, err := range errs {
s := &cmderrors.PlatformLoadingError{Cause: err}
responseError(s.GRPCStatus())
Expand Down Expand Up @@ -344,7 +349,7 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor

if profile == nil {
// Add directories of libraries bundled with IDE
if bundledLibsDir := configuration.IDEBuiltinLibrariesDir(configuration.Settings); bundledLibsDir != nil {
if bundledLibsDir := configuration.IDEBuiltinLibrariesDir(s.settings); bundledLibsDir != nil {
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
Path: bundledLibsDir,
Location: libraries.IDEBuiltIn,
Expand All @@ -353,14 +358,14 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor

// Add libraries directory from config file
lmb.AddLibrariesDir(librariesmanager.LibrariesDir{
Path: configuration.LibrariesDir(configuration.Settings),
Path: configuration.LibrariesDir(s.settings),
Location: libraries.User,
})
} else {
// Load libraries required for profile
for _, libraryRef := range profile.Libraries {
uid := libraryRef.InternalUniqueIdentifier()
libRoot := configuration.ProfilesCacheDir(configuration.Settings).Join(uid)
libRoot := configuration.ProfilesCacheDir(s.settings).Join(uid)
libDir := libRoot.Join(libraryRef.Library)

if !libDir.IsDir() {
Expand All @@ -373,7 +378,14 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor
responseError(err.GRPCStatus())
continue
}
if err := libRelease.Resource.Download(pme.DownloadDir, nil, libRelease.String(), downloadCallback, ""); err != nil {
config, err := s.settings.DownloaderConfig()
if err != nil {
taskCallback(&rpc.TaskProgress{Name: tr("Error downloading library %s", libraryRef)})
e := &cmderrors.FailedLibraryInstallError{Cause: err}
responseError(e.GRPCStatus())
continue
}
if err := libRelease.Resource.Download(pme.DownloadDir, config, libRelease.String(), downloadCallback, ""); err != nil {
taskCallback(&rpc.TaskProgress{Name: tr("Error downloading library %s", libraryRef)})
e := &cmderrors.FailedLibraryInstallError{Cause: err}
responseError(e.GRPCStatus())
Expand Down Expand Up @@ -409,7 +421,7 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor
// Refreshes the locale used, this will change the
// language of the CLI if the locale is different
// after started.
i18n.Init(configuration.Settings.GetString("locale"))
i18n.Init(s.settings.GetString("locale"))

return nil
}
Expand Down Expand Up @@ -487,7 +499,11 @@ func (s *arduinoCoreServerImpl) UpdateLibrariesIndex(req *rpc.UpdateLibrariesInd
// Perform index update
// TODO: pass context
// ctx := stream.Context()
if err := globals.LibrariesIndexResource.Download(indexDir, downloadCB); err != nil {
config, err := s.settings.DownloaderConfig()
if err != nil {
return err
}
if err := globals.LibrariesIndexResource.Download(indexDir, downloadCB, config); err != nil {
resultCB(rpc.IndexUpdateReport_STATUS_FAILED)
return err
}
Expand Down Expand Up @@ -532,11 +548,11 @@ func (s *arduinoCoreServerImpl) UpdateIndex(req *rpc.UpdateIndexRequest, stream
Message: &rpc.UpdateIndexResponse_DownloadProgress{DownloadProgress: p},
})
}
indexpath := configuration.DataDir(configuration.Settings)
indexpath := configuration.DataDir(s.settings)

urls := []string{globals.DefaultIndexURL}
if !req.GetIgnoreCustomPackageIndexes() {
urls = append(urls, configuration.Settings.GetStringSlice("board_manager.additional_urls")...)
urls = append(urls, s.settings.GetStringSlice("board_manager.additional_urls")...)
}

failed := false
Expand Down Expand Up @@ -593,11 +609,18 @@ func (s *arduinoCoreServerImpl) UpdateIndex(req *rpc.UpdateIndexRequest, stream
}
}

config, err := s.settings.DownloaderConfig()
if err != nil {
downloadCB.Start(u, tr("Downloading index: %s", filepath.Base(URL.Path)))
downloadCB.End(false, tr("Invalid network configuration: %s", err))
failed = true
}

if strings.HasSuffix(URL.Host, "arduino.cc") && strings.HasSuffix(URL.Path, ".json") {
indexResource.SignatureURL, _ = url.Parse(u) // should not fail because we already parsed it
indexResource.SignatureURL.Path += ".sig"
}
if err := indexResource.Download(indexpath, downloadCB); err != nil {
if err := indexResource.Download(indexpath, downloadCB, config); err != nil {
failed = true
result.UpdatedIndexes = append(result.GetUpdatedIndexes(), report(URL, rpc.IndexUpdateReport_STATUS_FAILED))
} else {
Expand All @@ -615,10 +638,8 @@ func (s *arduinoCoreServerImpl) UpdateIndex(req *rpc.UpdateIndexRequest, stream

// firstUpdate downloads libraries and packages indexes if they don't exist.
// This ideally is only executed the first time the CLI is run.
func firstUpdate(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance *rpc.Instance, downloadCb func(msg *rpc.DownloadProgress), externalPackageIndexes []*url.URL) error {
// Gets the data directory to verify if library_index.json and package_index.json exist
dataDir := configuration.DataDir(configuration.Settings)
libraryIndex := dataDir.Join("library_index.json")
func firstUpdate(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance *rpc.Instance, indexDir *paths.Path, downloadCb func(msg *rpc.DownloadProgress), externalPackageIndexes []*url.URL) error {
libraryIndex := indexDir.Join("library_index.json")

if libraryIndex.NotExist() {
// The library_index.json file doesn't exists, that means the CLI is run for the first time
Expand All @@ -640,7 +661,7 @@ func firstUpdate(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance
Message: tr("Error downloading index '%s'", URL),
Cause: &cmderrors.InvalidURLError{}}
}
packageIndexFile := dataDir.Join(packageIndexFileName)
packageIndexFile := indexDir.Join(packageIndexFileName)
if packageIndexFile.NotExist() {
// The index file doesn't exists, that means the CLI is run for the first time,
// or the 3rd party package index URL has just been added. Similarly to the
Expand Down
9 changes: 5 additions & 4 deletions commands/internal/instances/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/arduino/arduino-cli/version"
"github.com/arduino/go-paths-helper"
"go.bug.st/downloader/v2"
)

// coreInstance is an instance of the Arduino Core Services. The user can
Expand Down Expand Up @@ -133,15 +134,15 @@ func SetLibraryManager(inst *rpc.Instance, lm *librariesmanager.LibrariesManager
}

// Create a new *rpc.Instance ready to be initialized
func Create(dataDir, packagesDir, downloadsDir *paths.Path, extraUserAgent ...string) (*rpc.Instance, error) {
func Create(dataDir, packagesDir, downloadsDir *paths.Path, extraUserAgent string, downloaderConfig downloader.Config) (*rpc.Instance, error) {
// Create package manager
userAgent := "arduino-cli/" + version.VersionInfo.VersionString
for _, ua := range extraUserAgent {
userAgent += " " + ua
if extraUserAgent != "" {
userAgent += " " + extraUserAgent
}
tempDir := dataDir.Join("tmp")

pm := packagemanager.NewBuilder(dataDir, packagesDir, downloadsDir, tempDir, userAgent).Build()
pm := packagemanager.NewBuilder(dataDir, packagesDir, downloadsDir, tempDir, userAgent, downloaderConfig).Build()
lm, _ := librariesmanager.NewBuilder().Build()

instance := &coreInstance{
Expand Down
7 changes: 6 additions & 1 deletion commands/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,26 @@ package commands
import (
"context"

"github.com/arduino/arduino-cli/internal/cli/configuration"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
)

// NewArduinoCoreServer returns an implementation of the ArduinoCoreService gRPC service
// that uses the provided version string.
func NewArduinoCoreServer(version string) rpc.ArduinoCoreServiceServer {
func NewArduinoCoreServer(version string, settings *configuration.Settings) rpc.ArduinoCoreServiceServer {
return &arduinoCoreServerImpl{
versionString: version,
settings: settings,
}
}

type arduinoCoreServerImpl struct {
rpc.UnsafeArduinoCoreServiceServer // Force compile error for unimplemented methods

versionString string

// Settings holds configurations of the CLI and the gRPC consumers
settings *configuration.Settings
}

// Version returns the version of the Arduino CLI
Expand Down
25 changes: 11 additions & 14 deletions commands/service_board_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (
f "github.com/arduino/arduino-cli/internal/algorithms"
"github.com/arduino/arduino-cli/internal/arduino/cores"
"github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager"
"github.com/arduino/arduino-cli/internal/arduino/httpclient"
"github.com/arduino/arduino-cli/internal/cli/configuration"
"github.com/arduino/arduino-cli/internal/inventory"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/arduino/go-properties-orderedmap"
Expand All @@ -45,7 +45,7 @@ var (
validVidPid = regexp.MustCompile(`0[xX][a-fA-F\d]{4}`)
)

func cachedAPIByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) {
func cachedAPIByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
var resp []*rpc.BoardListItem

cacheKey := fmt.Sprintf("cache.builder-api.v3/boards/byvid/pid/%s/%s", vid, pid)
Expand All @@ -59,7 +59,7 @@ func cachedAPIByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) {
}
}

resp, err := apiByVidPid(vid, pid) // Perform API requrest
resp, err := apiByVidPid(vid, pid, settings) // Perform API requrest

if err == nil {
if cachedResp, err := json.Marshal(resp); err == nil {
Expand All @@ -71,7 +71,7 @@ func cachedAPIByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) {
return resp, err
}

func apiByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) {
func apiByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
// ensure vid and pid are valid before hitting the API
if !validVidPid.MatchString(vid) {
return nil, errors.New(tr("Invalid vid value: '%s'", vid))
Expand All @@ -84,10 +84,7 @@ func apiByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) {
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Content-Type", "application/json")

// TODO: use proxy if set

httpClient, err := httpclient.New()

httpClient, err := settings.NewHttpClient()
if err != nil {
return nil, fmt.Errorf("%s: %w", tr("failed to initialize http client"), err)
}
Expand Down Expand Up @@ -130,18 +127,18 @@ func apiByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) {
}, nil
}

func identifyViaCloudAPI(props *properties.Map) ([]*rpc.BoardListItem, error) {
func identifyViaCloudAPI(props *properties.Map, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
// If the port is not USB do not try identification via cloud
if !props.ContainsKey("vid") || !props.ContainsKey("pid") {
return nil, nil
}

logrus.Debug("Querying builder API for board identification...")
return cachedAPIByVidPid(props.Get("vid"), props.Get("pid"))
return cachedAPIByVidPid(props.Get("vid"), props.Get("pid"), settings)
}

// identify returns a list of boards checking first the installed platforms or the Cloud API
func identify(pme *packagemanager.Explorer, port *discovery.Port) ([]*rpc.BoardListItem, error) {
func identify(pme *packagemanager.Explorer, port *discovery.Port, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
boards := []*rpc.BoardListItem{}
if port.Properties == nil {
return boards, nil
Expand Down Expand Up @@ -173,7 +170,7 @@ func identify(pme *packagemanager.Explorer, port *discovery.Port) ([]*rpc.BoardL
// if installed cores didn't recognize the board, try querying
// the builder API if the board is a USB device port
if len(boards) == 0 {
items, err := identifyViaCloudAPI(port.Properties)
items, err := identifyViaCloudAPI(port.Properties, settings)
if err != nil {
// this is bad, but keep going
logrus.WithError(err).Debug("Error querying builder API")
Expand Down Expand Up @@ -227,7 +224,7 @@ func (s *arduinoCoreServerImpl) BoardList(ctx context.Context, req *rpc.BoardLis

ports := []*rpc.DetectedPort{}
for _, port := range dm.List() {
boards, err := identify(pme, port)
boards, err := identify(pme, port, s.settings)
if err != nil {
warnings = append(warnings, err.Error())
}
Expand Down Expand Up @@ -306,7 +303,7 @@ func (s *arduinoCoreServerImpl) BoardListWatch(req *rpc.BoardListWatchRequest, s

boardsError := ""
if event.Type == "add" {
boards, err := identify(pme, event.Port)
boards, err := identify(pme, event.Port, s.settings)
if err != nil {
boardsError = err.Error()
}
Expand Down
Loading

0 comments on commit cdcec9b

Please sign in to comment.