From dc6b3b495fbc057be15c9485db17d402c2be7cfd Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 20 Mar 2024 17:23:27 +0100 Subject: [PATCH] Removed global *viper.Viper settings instance 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. --- commands/instances.go | 79 ++++++++++++------- commands/internal/instances/instances.go | 9 ++- commands/service.go | 7 +- commands/service_board_list.go | 25 +++--- commands/service_board_list_test.go | 32 +++----- commands/service_compile.go | 19 ++--- commands/service_debug_test.go | 3 +- commands/service_library_download.go | 8 +- commands/service_library_install.go | 2 +- commands/service_platform_download.go | 4 +- commands/service_platform_search_test.go | 10 +-- commands/service_settings.go | 22 +++--- commands/service_settings_test.go | 76 ++++++++++-------- commands/service_sketch_load_test.go | 3 +- commands/service_sketch_new.go | 3 +- commands/service_sketch_new_test.go | 11 +-- commands/service_upload_test.go | 3 +- .../arduino/cores/packagemanager/download.go | 9 +-- .../cores/packagemanager/install_uninstall.go | 4 +- .../arduino/cores/packagemanager/loader.go | 4 +- .../cores/packagemanager/loader_test.go | 3 +- .../cores/packagemanager/package_manager.go | 11 ++- .../packagemanager/package_manager_test.go | 34 ++++---- .../arduino/cores/packagemanager/profiles.go | 22 +++--- internal/arduino/httpclient/httpclient.go | 64 +-------------- internal/arduino/resources/download.go | 2 +- internal/arduino/resources/helpers_test.go | 21 +++-- internal/arduino/resources/index.go | 6 +- internal/arduino/resources/resources_test.go | 8 +- internal/cli/arguments/reference_test.go | 8 +- internal/cli/cache/cache.go | 5 +- internal/cli/cache/clean.go | 10 ++- internal/cli/cli.go | 38 ++++----- internal/cli/compile/compile.go | 4 +- internal/cli/config/add.go | 16 ++-- internal/cli/config/config.go | 23 +++--- internal/cli/config/delete.go | 23 +++--- internal/cli/config/dump.go | 12 ++- internal/cli/config/get.go | 16 ++-- internal/cli/config/init.go | 26 +++--- internal/cli/config/remove.go | 16 ++-- internal/cli/config/set.go | 14 ++-- internal/cli/configuration/configuration.go | 22 ++++-- internal/cli/configuration/defaults.go | 4 +- internal/cli/configuration/directories.go | 19 +++-- internal/cli/configuration/network.go | 47 ++++++++++- .../configuration/network_test.go} | 21 +++-- internal/cli/core/core.go | 5 +- internal/cli/core/search.go | 19 ++--- internal/cli/daemon/daemon.go | 54 ++++++------- internal/cli/lib/install.go | 9 ++- internal/cli/lib/lib.go | 7 +- internal/cli/lib/search.go | 15 ++-- internal/cli/updater/updater.go | 48 ++++------- internal/cli/version/version.go | 11 ++- internal/docsgen/main.go | 4 +- main.go | 8 +- 57 files changed, 509 insertions(+), 499 deletions(-) rename internal/{arduino/httpclient/httpclient_test.go => cli/configuration/network_test.go} (79%) diff --git a/commands/instances.go b/commands/instances.go index 50aa45c1ee5..743bae6b931 100644 --- a/commands/instances.go +++ b/commands/instances.go @@ -47,8 +47,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}) @@ -60,16 +61,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 { @@ -78,8 +79,8 @@ 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 { @@ -87,7 +88,11 @@ func (s *arduinoCoreServerImpl) Create(ctx context.Context, req *rpc.CreateReque } } - 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 } @@ -106,6 +111,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{} @@ -168,7 +175,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{ @@ -183,7 +190,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, @@ -236,15 +243,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()) @@ -342,7 +347,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, @@ -351,14 +356,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() { @@ -371,7 +376,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()) @@ -407,7 +419,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 } @@ -449,7 +461,11 @@ func (s *arduinoCoreServerImpl) UpdateLibrariesIndex(req *rpc.UpdateLibrariesInd // 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 { return err } @@ -477,11 +493,11 @@ func (s *arduinoCoreServerImpl) UpdateIndex(req *rpc.UpdateIndexRequest, stream var downloadCB rpc.DownloadProgressCB = func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.UpdateIndexResponse{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 @@ -511,12 +527,19 @@ func (s *arduinoCoreServerImpl) UpdateIndex(req *rpc.UpdateIndexRequest, stream continue } + 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 + } + indexResource := resources.IndexResource{URL: URL} 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 } } @@ -529,10 +552,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 @@ -554,7 +575,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 diff --git a/commands/internal/instances/instances.go b/commands/internal/instances/instances.go index 019927a4967..b5cd63a3287 100644 --- a/commands/internal/instances/instances.go +++ b/commands/internal/instances/instances.go @@ -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 @@ -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{ diff --git a/commands/service.go b/commands/service.go index e8bf689d59a..03da5135b29 100644 --- a/commands/service.go +++ b/commands/service.go @@ -18,14 +18,16 @@ 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, } } @@ -33,6 +35,9 @@ 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 diff --git a/commands/service_board_list.go b/commands/service_board_list.go index ba6cd3c727f..aff1085c9c2 100644 --- a/commands/service_board_list.go +++ b/commands/service_board_list.go @@ -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" @@ -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) @@ -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 { @@ -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)) @@ -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) } @@ -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 @@ -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") @@ -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()) } @@ -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() } diff --git a/commands/service_board_list_test.go b/commands/service_board_list_test.go index 2db649e4554..e9132a50949 100644 --- a/commands/service_board_list_test.go +++ b/commands/service_board_list_test.go @@ -27,12 +27,11 @@ import ( "github.com/arduino/go-properties-orderedmap" discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2" "github.com/stretchr/testify/require" + "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) func TestGetByVidPid(t *testing.T) { - configuration.Settings = configuration.Init("") - configuration.Settings.Set("locale", "en") ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, ` { @@ -49,34 +48,31 @@ func TestGetByVidPid(t *testing.T) { defer ts.Close() vidPidURL = ts.URL - res, err := apiByVidPid("0xf420", "0XF069") + res, err := apiByVidPid("0xf420", "0XF069", configuration.Init("")) require.Nil(t, err) require.Len(t, res, 1) require.Equal(t, "Arduino/Genuino MKR1000", res[0].GetName()) require.Equal(t, "arduino:samd:mkr1000", res[0].GetFqbn()) // wrong vid (too long), wrong pid (not an hex value) - _, err = apiByVidPid("0xfffff", "0xDEFG") + + _, err = apiByVidPid("0xfffff", "0xDEFG", configuration.Init("")) require.NotNil(t, err) } func TestGetByVidPidNotFound(t *testing.T) { - configuration.Settings = configuration.Init("") - configuration.Settings.Set("locale", "en") ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })) defer ts.Close() vidPidURL = ts.URL - res, err := apiByVidPid("0x0420", "0x0069") + res, err := apiByVidPid("0x0420", "0x0069", configuration.Init("")) require.NoError(t, err) require.Empty(t, res) } func TestGetByVidPid5xx(t *testing.T) { - configuration.Settings = configuration.Init("") - configuration.Settings.Set("locale", "en") ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("500 - Ooooops!")) @@ -84,46 +80,39 @@ func TestGetByVidPid5xx(t *testing.T) { defer ts.Close() vidPidURL = ts.URL - res, err := apiByVidPid("0x0420", "0x0069") + res, err := apiByVidPid("0x0420", "0x0069", configuration.Init("")) require.NotNil(t, err) require.Equal(t, "the server responded with status 500 Internal Server Error", err.Error()) require.Len(t, res, 0) } func TestGetByVidPidMalformedResponse(t *testing.T) { - configuration.Settings = configuration.Init("") - configuration.Settings.Set("locale", "en") ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "{}") })) defer ts.Close() vidPidURL = ts.URL - res, err := apiByVidPid("0x0420", "0x0069") + res, err := apiByVidPid("0x0420", "0x0069", configuration.Init("")) require.NotNil(t, err) require.Equal(t, "wrong format in server response", err.Error()) require.Len(t, res, 0) } func TestBoardDetectionViaAPIWithNonUSBPort(t *testing.T) { - configuration.Settings = configuration.Init("") - configuration.Settings.Set("locale", "en") - items, err := identifyViaCloudAPI(properties.NewMap()) + items, err := identifyViaCloudAPI(properties.NewMap(), configuration.Init("")) require.NoError(t, err) require.Empty(t, items) } func TestBoardIdentifySorting(t *testing.T) { - configuration.Settings = configuration.Init("") - configuration.Settings.Set("locale", "en") - dataDir := paths.TempDir().Join("test", "data_dir") t.Setenv("ARDUINO_DATA_DIR", dataDir.String()) dataDir.MkdirAll() defer paths.TempDir().Join("test").RemoveAll() // We don't really care about the paths in this case - pmb := packagemanager.NewBuilder(dataDir, dataDir, dataDir, dataDir, "test") + pmb := packagemanager.NewBuilder(dataDir, dataDir, dataDir, dataDir, "test", downloader.GetDefaultConfig()) // Create some boards with identical VID:PID combination pack := pmb.GetOrCreatePackage("packager") @@ -159,7 +148,8 @@ func TestBoardIdentifySorting(t *testing.T) { pme, release := pm.NewExplorer() defer release() - res, err := identify(pme, &discovery.Port{Properties: idPrefs}) + settings := configuration.Init("") + res, err := identify(pme, &discovery.Port{Properties: idPrefs}, settings) require.NoError(t, err) require.NotNil(t, res) require.Len(t, res, 4) diff --git a/commands/service_compile.go b/commands/service_compile.go index 0acdabd95ab..4745fbc28ec 100644 --- a/commands/service_compile.go +++ b/commands/service_compile.go @@ -22,6 +22,7 @@ import ( "io" "sort" "strings" + "time" "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" @@ -69,7 +70,7 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu // There is a binding between the export binaries setting and the CLI flag to explicitly set it, // since we want this binding to work also for the gRPC interface we must read it here in this // package instead of the cli/compile one, otherwise we'd lose the binding. - exportBinaries := configuration.Settings.GetBool("sketch.always_export_binaries") + exportBinaries := s.settings.GetBool("sketch.always_export_binaries") // If we'd just read the binding in any case, even if the request sets the export binaries setting, // the settings value would always overwrite the request one and it wouldn't have any effect // setting it for individual requests. To solve this we use a wrapper.BoolValue to handle @@ -180,7 +181,10 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu } buildcache.New(buildPath.Parent()).GetOrCreate(buildPath.Base()) // cache is purged after compilation to not remove entries that might be required - defer maybePurgeBuildCache() + + defer maybePurgeBuildCache( + s.settings.GetUint("build_cache.compilations_before_purge"), + s.settings.GetDuration("build_cache.ttl").Abs()) var coreBuildCachePath *paths.Path if req.GetBuildCachePath() == "" { @@ -202,7 +206,7 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu actualPlatform := buildPlatform otherLibrariesDirs := paths.NewPathList(req.GetLibraries()...) - otherLibrariesDirs.Add(configuration.LibrariesDir(configuration.Settings)) + otherLibrariesDirs.Add(configuration.LibrariesDir(s.settings)) var libsManager *librariesmanager.LibrariesManager if pme.GetProfile() != nil { @@ -235,9 +239,9 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu coreBuildCachePath, int(req.GetJobs()), req.GetBuildProperties(), - configuration.HardwareDirectories(configuration.Settings), + configuration.HardwareDirectories(s.settings), otherLibrariesDirs, - configuration.IDEBuiltinLibrariesDir(configuration.Settings), + configuration.IDEBuiltinLibrariesDir(s.settings), fqbn, req.GetClean(), req.GetSourceOverride(), @@ -402,9 +406,7 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu } // maybePurgeBuildCache runs the build files cache purge if the policy conditions are met. -func maybePurgeBuildCache() { - - compilationsBeforePurge := configuration.Settings.GetUint("build_cache.compilations_before_purge") +func maybePurgeBuildCache(compilationsBeforePurge uint, cacheTTL time.Duration) { // 0 means never purge if compilationsBeforePurge == 0 { return @@ -417,7 +419,6 @@ func maybePurgeBuildCache() { return } inventory.Store.Set("build_cache.compilation_count_since_last_purge", 0) - cacheTTL := configuration.Settings.GetDuration("build_cache.ttl").Abs() buildcache.New(paths.TempDir().Join("arduino", "cores")).Purge(cacheTTL) buildcache.New(paths.TempDir().Join("arduino", "sketches")).Purge(cacheTTL) } diff --git a/commands/service_debug_test.go b/commands/service_debug_test.go index e3bb45b0fd8..8e627c44b76 100644 --- a/commands/service_debug_test.go +++ b/commands/service_debug_test.go @@ -27,6 +27,7 @@ import ( "github.com/arduino/go-properties-orderedmap" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.bug.st/downloader/v2" ) func TestGetCommandLine(t *testing.T) { @@ -36,7 +37,7 @@ func TestGetCommandLine(t *testing.T) { sketchPath := paths.New("testdata", "debug", sketch) require.NoError(t, sketchPath.ToAbs()) - pmb := packagemanager.NewBuilder(nil, nil, nil, nil, "test") + pmb := packagemanager.NewBuilder(nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) pmb.LoadHardwareFromDirectory(customHardware) pmb.LoadHardwareFromDirectory(dataDir) diff --git a/commands/service_library_download.go b/commands/service_library_download.go index 37b4ecbb53e..8543e8cdea6 100644 --- a/commands/service_library_download.go +++ b/commands/service_library_download.go @@ -20,8 +20,8 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" - "github.com/arduino/arduino-cli/internal/arduino/httpclient" "github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex" + "github.com/arduino/arduino-cli/internal/cli/configuration" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" ) @@ -66,7 +66,7 @@ func (s *arduinoCoreServerImpl) LibraryDownload(req *rpc.LibraryDownloadRequest, return err } - if err := downloadLibrary(ctx, downloadsDir, lib, downloadCB, func(*rpc.TaskProgress) {}, "download"); err != nil { + if err := downloadLibrary(ctx, downloadsDir, lib, downloadCB, func(*rpc.TaskProgress) {}, "download", s.settings); err != nil { return err } @@ -74,10 +74,10 @@ func (s *arduinoCoreServerImpl) LibraryDownload(req *rpc.LibraryDownloadRequest, } func downloadLibrary(_ context.Context, downloadsDir *paths.Path, libRelease *librariesindex.Release, - downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, queryParameter string) error { + downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, queryParameter string, settings *configuration.Settings) error { taskCB(&rpc.TaskProgress{Name: tr("Downloading %s", libRelease)}) - config, err := httpclient.GetDownloaderConfig() + config, err := settings.DownloaderConfig() if err != nil { return &cmderrors.FailedDownloadError{Message: tr("Can't download library"), Cause: err} } diff --git a/commands/service_library_install.go b/commands/service_library_install.go index d4440d3d2ae..20b98695de2 100644 --- a/commands/service_library_install.go +++ b/commands/service_library_install.go @@ -147,7 +147,7 @@ func (s *arduinoCoreServerImpl) LibraryInstall(req *rpc.LibraryInstallRequest, s downloadReason += "-builtin" } } - if err := downloadLibrary(ctx, downloadsDir, libRelease, downloadCB, taskCB, downloadReason); err != nil { + if err := downloadLibrary(ctx, downloadsDir, libRelease, downloadCB, taskCB, downloadReason, s.settings); err != nil { return err } if err := installLibrary(lmi, downloadsDir, libRelease, installTask, taskCB); err != nil { diff --git a/commands/service_platform_download.go b/commands/service_platform_download.go index 23a8096120d..09f49a2d76b 100644 --- a/commands/service_platform_download.go +++ b/commands/service_platform_download.go @@ -67,13 +67,13 @@ func (s *arduinoCoreServerImpl) PlatformDownload(req *rpc.PlatformDownloadReques // TODO: pass context // ctx := stream.Context() - if err := pme.DownloadPlatformRelease(platform, nil, downloadCB); err != nil { + if err := pme.DownloadPlatformRelease(platform, downloadCB); err != nil { return err } for _, tool := range tools { // TODO: pass context - if err := pme.DownloadToolRelease(tool, nil, downloadCB); err != nil { + if err := pme.DownloadToolRelease(tool, downloadCB); err != nil { return err } } diff --git a/commands/service_platform_search_test.go b/commands/service_platform_search_test.go index 63dc02c5265..3b7b87cf30d 100644 --- a/commands/service_platform_search_test.go +++ b/commands/service_platform_search_test.go @@ -36,9 +36,8 @@ func TestPlatformSearch(t *testing.T) { err := paths.New("testdata", "platform", "package_index.json").CopyTo(dataDir.Join("package_index.json")) require.Nil(t, err) - configuration.Settings = configuration.Init(paths.TempDir().Join("test", "arduino-cli.yaml").String()) - - srv := NewArduinoCoreServer("") + settings := configuration.Init(paths.TempDir().Join("test", "arduino-cli.yaml").String()) + srv := NewArduinoCoreServer("", settings) ctx := context.Background() createResp, err := srv.Create(ctx, &rpc.CreateRequest{}) require.NoError(t, err) @@ -338,9 +337,8 @@ func TestPlatformSearchSorting(t *testing.T) { err := paths.New("testdata", "platform", "package_index.json").CopyTo(dataDir.Join("package_index.json")) require.Nil(t, err) - configuration.Settings = configuration.Init(paths.TempDir().Join("test", "arduino-cli.yaml").String()) - - srv := NewArduinoCoreServer("") + settings := configuration.Init(paths.TempDir().Join("test", "arduino-cli.yaml").String()) + srv := NewArduinoCoreServer("", settings) ctx := context.Background() createResp, err := srv.Create(ctx, &rpc.CreateRequest{}) diff --git a/commands/service_settings.go b/commands/service_settings.go index f4675e0467e..177d0819178 100644 --- a/commands/service_settings.go +++ b/commands/service_settings.go @@ -29,7 +29,7 @@ import ( // SettingsGetAll returns a message with a string field containing all the settings // currently in use, marshalled in JSON format. func (s *arduinoCoreServerImpl) SettingsGetAll(ctx context.Context, req *rpc.SettingsGetAllRequest) (*rpc.SettingsGetAllResponse, error) { - b, err := json.Marshal(configuration.Settings.AllSettings()) + b, err := json.Marshal(s.settings.AllSettings()) if err == nil { return &rpc.SettingsGetAllResponse{ JsonData: string(b), @@ -83,9 +83,9 @@ func (s *arduinoCoreServerImpl) SettingsMerge(ctx context.Context, req *rpc.Sett for k, v := range mapped { updatedSettings.Set(k, v) } - configPath := configuration.Settings.ConfigFileUsed() + configPath := s.settings.ConfigFileUsed() updatedSettings.SetConfigFile(configPath) - configuration.Settings = updatedSettings + s.settings = updatedSettings return &rpc.SettingsMergeResponse{}, nil } @@ -100,7 +100,7 @@ func (s *arduinoCoreServerImpl) SettingsGetValue(ctx context.Context, req *rpc.S // since that doesn't check for keys formatted like daemon.port or those set // with Viper.Set(). This way we check for all existing settings for sure. keyExists := false - for _, k := range configuration.Settings.AllKeys() { + for _, k := range s.settings.AllKeys() { if k == key || strings.HasPrefix(k, key) { keyExists = true break @@ -110,7 +110,7 @@ func (s *arduinoCoreServerImpl) SettingsGetValue(ctx context.Context, req *rpc.S return nil, errors.New(tr("key not found in settings")) } - b, err := json.Marshal(configuration.Settings.Get(key)) + b, err := json.Marshal(s.settings.Get(key)) value := &rpc.SettingsGetValueResponse{} if err == nil { value.Key = key @@ -127,7 +127,7 @@ func (s *arduinoCoreServerImpl) SettingsSetValue(ctx context.Context, val *rpc.S err := json.Unmarshal([]byte(val.GetJsonData()), &value) if err == nil { - configuration.Settings.Set(key, value) + s.settings.Set(key, value) } return &rpc.SettingsSetValueResponse{}, err @@ -138,7 +138,7 @@ func (s *arduinoCoreServerImpl) SettingsSetValue(ctx context.Context, val *rpc.S // and that's picked up when the CLI is run as daemon, either using the default path or a custom one // set with the --config-file flag. func (s *arduinoCoreServerImpl) SettingsWrite(ctx context.Context, req *rpc.SettingsWriteRequest) (*rpc.SettingsWriteResponse, error) { - if err := configuration.Settings.WriteConfigAs(req.GetFilePath()); err != nil { + if err := s.settings.WriteConfigAs(req.GetFilePath()); err != nil { return nil, err } return &rpc.SettingsWriteResponse{}, nil @@ -153,7 +153,7 @@ func (s *arduinoCoreServerImpl) SettingsDelete(ctx context.Context, req *rpc.Set // with Viper.Set(). This way we check for all existing settings for sure. keyExists := false keys := []string{} - for _, k := range configuration.Settings.AllKeys() { + for _, k := range s.settings.AllKeys() { if !strings.HasPrefix(k, toDelete) { keys = append(keys, k) continue @@ -168,11 +168,11 @@ func (s *arduinoCoreServerImpl) SettingsDelete(ctx context.Context, req *rpc.Set // Override current settings to delete the key updatedSettings := configuration.Init("") for _, k := range keys { - updatedSettings.Set(k, configuration.Settings.Get(k)) + updatedSettings.Set(k, s.settings.Get(k)) } - configPath := configuration.Settings.ConfigFileUsed() + configPath := s.settings.ConfigFileUsed() updatedSettings.SetConfigFile(configPath) - configuration.Settings = updatedSettings + s.settings = updatedSettings return &rpc.SettingsDeleteResponse{}, nil } diff --git a/commands/service_settings_test.go b/commands/service_settings_test.go index 226a2663990..e1d67fb0fad 100644 --- a/commands/service_settings_test.go +++ b/commands/service_settings_test.go @@ -27,71 +27,68 @@ import ( "github.com/stretchr/testify/require" ) -var svc = NewArduinoCoreServer("") - -func init() { - configuration.Settings = configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) -} - -func reset() { - configuration.Settings = configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) -} - func TestGetAll(t *testing.T) { + settings := configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) + svc := NewArduinoCoreServer("", settings) resp, err := svc.SettingsGetAll(context.Background(), &rpc.SettingsGetAllRequest{}) require.Nil(t, err) - content, err := json.Marshal(configuration.Settings.AllSettings()) + content, err := json.Marshal(settings.AllSettings()) require.Nil(t, err) require.Equal(t, string(content), resp.GetJsonData()) } func TestMerge(t *testing.T) { + initialSettings := configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) + svc := NewArduinoCoreServer("", initialSettings).(*arduinoCoreServerImpl) + ctx := context.Background() + // Verify defaults - require.Equal(t, "50051", configuration.Settings.GetString("daemon.port")) - require.Equal(t, "", configuration.Settings.GetString("foo")) - require.Equal(t, false, configuration.Settings.GetBool("sketch.always_export_binaries")) + require.Equal(t, "50051", svc.settings.GetString("daemon.port")) + require.Equal(t, "", svc.settings.GetString("foo")) + require.Equal(t, false, svc.settings.GetBool("sketch.always_export_binaries")) bulkSettings := `{"foo": "bar", "daemon":{"port":"420"}, "sketch": {"always_export_binaries": "true"}}` - res, err := svc.SettingsMerge(context.Background(), &rpc.SettingsMergeRequest{JsonData: bulkSettings}) + res, err := svc.SettingsMerge(ctx, &rpc.SettingsMergeRequest{JsonData: bulkSettings}) require.NotNil(t, res) require.NoError(t, err) - require.Equal(t, "420", configuration.Settings.GetString("daemon.port")) - require.Equal(t, "bar", configuration.Settings.GetString("foo")) - require.Equal(t, true, configuration.Settings.GetBool("sketch.always_export_binaries")) + require.Equal(t, "420", svc.settings.GetString("daemon.port")) + require.Equal(t, "bar", svc.settings.GetString("foo")) + require.Equal(t, true, svc.settings.GetBool("sketch.always_export_binaries")) bulkSettings = `{"foo":"", "daemon": {}, "sketch": {"always_export_binaries": "false"}}` - res, err = svc.SettingsMerge(context.Background(), &rpc.SettingsMergeRequest{JsonData: bulkSettings}) + res, err = svc.SettingsMerge(ctx, &rpc.SettingsMergeRequest{JsonData: bulkSettings}) require.NotNil(t, res) require.NoError(t, err) - require.Equal(t, "50051", configuration.Settings.GetString("daemon.port")) - require.Equal(t, "", configuration.Settings.GetString("foo")) - require.Equal(t, false, configuration.Settings.GetBool("sketch.always_export_binaries")) + require.Equal(t, "50051", svc.settings.GetString("daemon.port")) + require.Equal(t, "", svc.settings.GetString("foo")) + require.Equal(t, false, svc.settings.GetBool("sketch.always_export_binaries")) bulkSettings = `{"daemon": {"port":""}}` - res, err = svc.SettingsMerge(context.Background(), &rpc.SettingsMergeRequest{JsonData: bulkSettings}) + res, err = svc.SettingsMerge(ctx, &rpc.SettingsMergeRequest{JsonData: bulkSettings}) require.NotNil(t, res) require.NoError(t, err) - require.Equal(t, "", configuration.Settings.GetString("daemon.port")) + require.Equal(t, "", svc.settings.GetString("daemon.port")) // Verifies other values are not changed - require.Equal(t, "", configuration.Settings.GetString("foo")) - require.Equal(t, false, configuration.Settings.GetBool("sketch.always_export_binaries")) + require.Equal(t, "", svc.settings.GetString("foo")) + require.Equal(t, false, svc.settings.GetBool("sketch.always_export_binaries")) bulkSettings = `{"network": {}}` - res, err = svc.SettingsMerge(context.Background(), &rpc.SettingsMergeRequest{JsonData: bulkSettings}) + res, err = svc.SettingsMerge(ctx, &rpc.SettingsMergeRequest{JsonData: bulkSettings}) require.NotNil(t, res) require.NoError(t, err) - require.Equal(t, "", configuration.Settings.GetString("proxy")) - - reset() + require.Equal(t, "", svc.settings.GetString("proxy")) } func TestGetValue(t *testing.T) { + settings := configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) + svc := NewArduinoCoreServer("", settings) + key := &rpc.SettingsGetValueRequest{Key: "daemon"} resp, err := svc.SettingsGetValue(context.Background(), key) require.NoError(t, err) @@ -104,6 +101,9 @@ func TestGetValue(t *testing.T) { } func TestGetMergedValue(t *testing.T) { + settings := configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) + svc := NewArduinoCoreServer("", settings) + // Verifies value is not set key := &rpc.SettingsGetValueRequest{Key: "foo"} res, err := svc.SettingsGetValue(context.Background(), key) @@ -120,27 +120,34 @@ func TestGetMergedValue(t *testing.T) { res, err = svc.SettingsGetValue(context.Background(), key) require.NoError(t, err) require.Equal(t, `"bar"`, res.GetJsonData()) - - reset() } func TestGetValueNotFound(t *testing.T) { + settings := configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) + svc := NewArduinoCoreServer("", settings) + key := &rpc.SettingsGetValueRequest{Key: "DOESNTEXIST"} _, err := svc.SettingsGetValue(context.Background(), key) require.Error(t, err) } func TestSetValue(t *testing.T) { + settings := configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) + svc := NewArduinoCoreServer("", settings) + val := &rpc.SettingsSetValueRequest{ Key: "foo", JsonData: `"bar"`, } _, err := svc.SettingsSetValue(context.Background(), val) require.Nil(t, err) - require.Equal(t, "bar", configuration.Settings.GetString("foo")) + require.Equal(t, "bar", settings.GetString("foo")) } func TestWrite(t *testing.T) { + settings := configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) + svc := NewArduinoCoreServer("", settings) + // Writes some settings val := &rpc.SettingsSetValueRequest{ Key: "foo", @@ -169,6 +176,9 @@ func TestWrite(t *testing.T) { } func TestDelete(t *testing.T) { + settings := configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) + svc := NewArduinoCoreServer("", settings) + _, err := svc.SettingsDelete(context.Background(), &rpc.SettingsDeleteRequest{ Key: "doesnotexist", }) diff --git a/commands/service_sketch_load_test.go b/commands/service_sketch_load_test.go index 36bb58ea351..822dfea7c5d 100644 --- a/commands/service_sketch_load_test.go +++ b/commands/service_sketch_load_test.go @@ -19,12 +19,13 @@ import ( "context" "testing" + "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/stretchr/testify/require" ) func TestLoadSketchProfiles(t *testing.T) { - srv := NewArduinoCoreServer("") + srv := NewArduinoCoreServer("", configuration.Init("")) loadResp, err := srv.LoadSketch(context.Background(), &commands.LoadSketchRequest{ SketchPath: "./testdata/sketch_with_profile", }) diff --git a/commands/service_sketch_new.go b/commands/service_sketch_new.go index a8eb518a815..b0c9c079f73 100644 --- a/commands/service_sketch_new.go +++ b/commands/service_sketch_new.go @@ -22,7 +22,6 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/internal/arduino/globals" - "github.com/arduino/arduino-cli/internal/cli/configuration" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" ) @@ -48,7 +47,7 @@ func (s *arduinoCoreServerImpl) NewSketch(ctx context.Context, req *rpc.NewSketc if len(req.GetSketchDir()) > 0 { sketchesDir = req.GetSketchDir() } else { - sketchesDir = configuration.Settings.GetString("directories.User") + sketchesDir = s.settings.GetString("directories.User") } if err := validateSketchName(req.GetSketchName()); err != nil { diff --git a/commands/service_sketch_new_test.go b/commands/service_sketch_new_test.go index 0d0b3e05aa6..353dec17a05 100644 --- a/commands/service_sketch_new_test.go +++ b/commands/service_sketch_new_test.go @@ -20,6 +20,7 @@ import ( "fmt" "testing" + "github.com/arduino/arduino-cli/internal/cli/configuration" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/stretchr/testify/require" ) @@ -35,7 +36,7 @@ func Test_SketchNameWrongPattern(t *testing.T) { ",`hack[}attempt{];", } - srv := NewArduinoCoreServer("") + srv := NewArduinoCoreServer("", configuration.Init("")) for _, name := range invalidNames { _, err := srv.NewSketch(context.Background(), &rpc.NewSketchRequest{ SketchName: name, @@ -48,7 +49,7 @@ func Test_SketchNameWrongPattern(t *testing.T) { } func Test_SketchNameEmpty(t *testing.T) { - srv := NewArduinoCoreServer("") + srv := NewArduinoCoreServer("", configuration.Init("")) _, err := srv.NewSketch(context.Background(), &rpc.NewSketchRequest{ SketchName: "", SketchDir: t.TempDir(), @@ -62,7 +63,7 @@ func Test_SketchNameTooLong(t *testing.T) { for i := range tooLongName { tooLongName[i] = 'a' } - srv := NewArduinoCoreServer("") + srv := NewArduinoCoreServer("", configuration.Init("")) _, err := srv.NewSketch(context.Background(), &rpc.NewSketchRequest{ SketchName: string(tooLongName), SketchDir: t.TempDir(), @@ -86,7 +87,7 @@ func Test_SketchNameOk(t *testing.T) { "_hello_world", string(lengthLimitName), } - srv := NewArduinoCoreServer("") + srv := NewArduinoCoreServer("", configuration.Init("")) for _, name := range validNames { _, err := srv.NewSketch(context.Background(), &rpc.NewSketchRequest{ SketchName: name, @@ -99,7 +100,7 @@ func Test_SketchNameOk(t *testing.T) { func Test_SketchNameReserved(t *testing.T) { invalidNames := []string{"CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"} - srv := NewArduinoCoreServer("") + srv := NewArduinoCoreServer("", configuration.Init("")) for _, name := range invalidNames { _, err := srv.NewSketch(context.Background(), &rpc.NewSketchRequest{ SketchName: name, diff --git a/commands/service_upload_test.go b/commands/service_upload_test.go index cf2f4ff3306..e8d70e19c4d 100644 --- a/commands/service_upload_test.go +++ b/commands/service_upload_test.go @@ -29,6 +29,7 @@ import ( properties "github.com/arduino/go-properties-orderedmap" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" + "go.bug.st/downloader/v2" ) func TestDetectSketchNameFromBuildPath(t *testing.T) { @@ -127,7 +128,7 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) { } func TestUploadPropertiesComposition(t *testing.T) { - pmb := packagemanager.NewBuilder(nil, nil, nil, nil, "test") + pmb := packagemanager.NewBuilder(nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) errs := pmb.LoadHardwareFromDirectory(paths.New("testdata", "upload", "hardware")) require.Len(t, errs, 0) buildPath1 := paths.New("testdata", "upload", "build_path_1") diff --git a/internal/arduino/cores/packagemanager/download.go b/internal/arduino/cores/packagemanager/download.go index c4a7cb62d59..4ac06cf19f8 100644 --- a/internal/arduino/cores/packagemanager/download.go +++ b/internal/arduino/cores/packagemanager/download.go @@ -22,7 +22,6 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/internal/arduino/cores" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) @@ -120,21 +119,21 @@ func (pme *Explorer) FindPlatformReleaseDependencies(item *PlatformReference) (* // DownloadToolRelease downloads a ToolRelease. If the tool is already downloaded a nil Downloader // is returned. Uses the given downloader configuration for download, or the default config if nil. -func (pme *Explorer) DownloadToolRelease(tool *cores.ToolRelease, config *downloader.Config, progressCB rpc.DownloadProgressCB) error { +func (pme *Explorer) DownloadToolRelease(tool *cores.ToolRelease, progressCB rpc.DownloadProgressCB) error { resource := tool.GetCompatibleFlavour() if resource == nil { return &cmderrors.FailedDownloadError{ Message: tr("Error downloading tool %s", tool), Cause: errors.New(tr("no versions available for the current OS, try contacting %s", tool.Tool.Package.Email))} } - return resource.Download(pme.DownloadDir, config, tool.String(), progressCB, "") + return resource.Download(pme.DownloadDir, pme.downloaderConfig, tool.String(), progressCB, "") } // DownloadPlatformRelease downloads a PlatformRelease. If the platform is already downloaded a // nil Downloader is returned. -func (pme *Explorer) DownloadPlatformRelease(platform *cores.PlatformRelease, config *downloader.Config, progressCB rpc.DownloadProgressCB) error { +func (pme *Explorer) DownloadPlatformRelease(platform *cores.PlatformRelease, progressCB rpc.DownloadProgressCB) error { if platform.Resource == nil { return &cmderrors.PlatformNotFoundError{Platform: platform.String()} } - return platform.Resource.Download(pme.DownloadDir, config, platform.String(), progressCB, "") + return platform.Resource.Download(pme.DownloadDir, pme.downloaderConfig, platform.String(), progressCB, "") } diff --git a/internal/arduino/cores/packagemanager/install_uninstall.go b/internal/arduino/cores/packagemanager/install_uninstall.go index bc29f08fa43..4b8c1cba9b4 100644 --- a/internal/arduino/cores/packagemanager/install_uninstall.go +++ b/internal/arduino/cores/packagemanager/install_uninstall.go @@ -92,11 +92,11 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools( // Package download taskCB(&rpc.TaskProgress{Name: tr("Downloading packages")}) for _, tool := range toolsToInstall { - if err := pme.DownloadToolRelease(tool, nil, downloadCB); err != nil { + if err := pme.DownloadToolRelease(tool, downloadCB); err != nil { return err } } - if err := pme.DownloadPlatformRelease(platformRelease, nil, downloadCB); err != nil { + if err := pme.DownloadPlatformRelease(platformRelease, downloadCB); err != nil { return err } taskCB(&rpc.TaskProgress{Completed: true}) diff --git a/internal/arduino/cores/packagemanager/loader.go b/internal/arduino/cores/packagemanager/loader.go index 68c603044e5..29e8fb511a4 100644 --- a/internal/arduino/cores/packagemanager/loader.go +++ b/internal/arduino/cores/packagemanager/loader.go @@ -31,8 +31,8 @@ import ( ) // LoadHardware read all plaforms from the configured paths -func (pm *Builder) LoadHardware() []error { - hardwareDirs := configuration.HardwareDirectories(configuration.Settings) +func (pm *Builder) LoadHardware(settings *configuration.Settings) []error { + hardwareDirs := configuration.HardwareDirectories(settings) return pm.LoadHardwareFromDirectories(hardwareDirs) } diff --git a/internal/arduino/cores/packagemanager/loader_test.go b/internal/arduino/cores/packagemanager/loader_test.go index d0d41992179..a9ee015c86d 100644 --- a/internal/arduino/cores/packagemanager/loader_test.go +++ b/internal/arduino/cores/packagemanager/loader_test.go @@ -21,6 +21,7 @@ import ( "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" "github.com/stretchr/testify/require" + "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) @@ -174,7 +175,7 @@ func TestLoadDiscoveries(t *testing.T) { defer fakePath.RemoveAll() createTestPackageManager := func() *PackageManager { - pmb := NewBuilder(fakePath, fakePath, fakePath, fakePath, "test") + pmb := NewBuilder(fakePath, fakePath, fakePath, fakePath, "test", downloader.GetDefaultConfig()) pack := pmb.packages.GetOrCreatePackage("arduino") // ble-discovery tool tool := pack.GetOrCreateTool("ble-discovery") diff --git a/internal/arduino/cores/packagemanager/package_manager.go b/internal/arduino/cores/packagemanager/package_manager.go index cea5e799d1b..d9a5f79a2ac 100644 --- a/internal/arduino/cores/packagemanager/package_manager.go +++ b/internal/arduino/cores/packagemanager/package_manager.go @@ -33,12 +33,12 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/cores/packageindex" "github.com/arduino/arduino-cli/internal/arduino/discovery/discoverymanager" "github.com/arduino/arduino-cli/internal/arduino/sketch" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/i18n" paths "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" "github.com/arduino/go-timeutils" "github.com/sirupsen/logrus" + "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) @@ -60,6 +60,7 @@ type PackageManager struct { profile *sketch.Profile discoveryManager *discoverymanager.DiscoveryManager userAgent string + downloaderConfig downloader.Config } // Builder is used to create a new PackageManager. The builder @@ -75,7 +76,7 @@ type Explorer PackageManager var tr = i18n.Tr // NewBuilder returns a new Builder -func NewBuilder(indexDir, packagesDir, downloadDir, tempDir *paths.Path, userAgent string) *Builder { +func NewBuilder(indexDir, packagesDir, downloadDir, tempDir *paths.Path, userAgent string, downloaderConfig downloader.Config) *Builder { return &Builder{ log: logrus.StandardLogger(), packages: cores.NewPackages(), @@ -84,8 +85,9 @@ func NewBuilder(indexDir, packagesDir, downloadDir, tempDir *paths.Path, userAge DownloadDir: downloadDir, tempDir: tempDir, packagesCustomGlobalProperties: properties.NewMap(), - discoveryManager: discoverymanager.New(configuration.UserAgent(configuration.Settings)), + discoveryManager: discoverymanager.New(userAgent), userAgent: userAgent, + downloaderConfig: downloaderConfig, } } @@ -164,7 +166,7 @@ func (pmb *Builder) calculateCompatibleReleases() { // this function will make the builder write the new configuration into this // PackageManager. func (pm *PackageManager) NewBuilder() (builder *Builder, commit func()) { - pmb := NewBuilder(pm.IndexDir, pm.PackagesDir, pm.DownloadDir, pm.tempDir, pm.userAgent) + pmb := NewBuilder(pm.IndexDir, pm.PackagesDir, pm.DownloadDir, pm.tempDir, pm.userAgent, pm.downloaderConfig) return pmb, func() { pmb.calculateCompatibleReleases() pmb.BuildIntoExistingPackageManager(pm) @@ -188,6 +190,7 @@ func (pm *PackageManager) NewExplorer() (explorer *Explorer, release func()) { profile: pm.profile, discoveryManager: pm.discoveryManager, userAgent: pm.userAgent, + downloaderConfig: pm.downloaderConfig, }, pm.packagesLock.RUnlock } diff --git a/internal/arduino/cores/packagemanager/package_manager_test.go b/internal/arduino/cores/packagemanager/package_manager_test.go index 055a6d332d0..fb215816d0c 100644 --- a/internal/arduino/cores/packagemanager/package_manager_test.go +++ b/internal/arduino/cores/packagemanager/package_manager_test.go @@ -28,6 +28,7 @@ import ( "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" "github.com/stretchr/testify/require" + "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) @@ -38,7 +39,7 @@ var dataDir1 = paths.New("testdata", "data_dir_1") var extraHardware = paths.New("testdata", "extra_hardware") func TestFindBoardWithFQBN(t *testing.T) { - pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test") + pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test", downloader.GetDefaultConfig()) pmb.LoadHardwareFromDirectory(customHardware) pm := pmb.Build() pme, release := pm.NewExplorer() @@ -56,7 +57,7 @@ func TestFindBoardWithFQBN(t *testing.T) { func TestResolveFQBN(t *testing.T) { // Pass nil, since these paths are only used for installing - pmb := NewBuilder(nil, nil, nil, nil, "test") + pmb := NewBuilder(nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) // Hardware from main packages directory pmb.LoadHardwareFromDirectory(dataDir1.Join("packages")) // This contains the arduino:avr core @@ -341,7 +342,7 @@ func TestResolveFQBN(t *testing.T) { } func TestBoardOptionsFunctions(t *testing.T) { - pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test") + pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test", downloader.GetDefaultConfig()) pmb.LoadHardwareFromDirectory(customHardware) pm := pmb.Build() pme, release := pm.NewExplorer() @@ -381,7 +382,7 @@ func TestBoardOptionsFunctions(t *testing.T) { } func TestBoardOrdering(t *testing.T) { - pmb := NewBuilder(dataDir1, dataDir1.Join("packages"), nil, nil, "") + pmb := NewBuilder(dataDir1, dataDir1.Join("packages"), nil, nil, "", downloader.GetDefaultConfig()) _ = pmb.LoadHardwareFromDirectories(paths.NewPathList(dataDir1.Join("packages").String())) pm := pmb.Build() pme, release := pm.NewExplorer() @@ -432,13 +433,14 @@ func TestBoardOrdering(t *testing.T) { func TestFindToolsRequiredForBoard(t *testing.T) { t.Setenv("ARDUINO_DATA_DIR", dataDir1.String()) - configuration.Settings = configuration.Init("") + settings := configuration.Init("") pmb := NewBuilder( dataDir1, - configuration.PackagesDir(configuration.Settings), - configuration.DownloadsDir(configuration.Settings), + configuration.PackagesDir(settings), + configuration.DownloadsDir(settings), dataDir1, "test", + downloader.GetDefaultConfig(), ) loadIndex := func(addr string) { @@ -454,7 +456,7 @@ func TestFindToolsRequiredForBoard(t *testing.T) { // We ignore the errors returned since they might not be necessarily blocking // but just warnings for the user, like in the case a board is not loaded // because of malformed menus - pmb.LoadHardware() + pmb.LoadHardware(settings) pm := pmb.Build() pme, release := pm.NewExplorer() defer release() @@ -567,7 +569,7 @@ func TestFindToolsRequiredForBoard(t *testing.T) { } func TestIdentifyBoard(t *testing.T) { - pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test") + pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test", downloader.GetDefaultConfig()) pmb.LoadHardwareFromDirectory(customHardware) pm := pmb.Build() pme, release := pm.NewExplorer() @@ -594,12 +596,12 @@ func TestIdentifyBoard(t *testing.T) { func TestPackageManagerClear(t *testing.T) { // Create a PackageManager and load the harware - pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test") + pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test", downloader.GetDefaultConfig()) pmb.LoadHardwareFromDirectory(customHardware) pm := pmb.Build() // Creates another PackageManager but don't load the hardware - emptyPmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test") + emptyPmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test", downloader.GetDefaultConfig()) emptyPm := emptyPmb.Build() // Verifies they're not equal @@ -621,7 +623,7 @@ func TestFindToolsRequiredFromPlatformRelease(t *testing.T) { require.NoError(t, err) defer fakePath.RemoveAll() - pmb := NewBuilder(fakePath, fakePath, fakePath, fakePath, "test") + pmb := NewBuilder(fakePath, fakePath, fakePath, fakePath, "test", downloader.GetDefaultConfig()) pack := pmb.GetOrCreatePackage("arduino") { @@ -742,7 +744,7 @@ func TestFindToolsRequiredFromPlatformRelease(t *testing.T) { } func TestFindPlatformReleaseDependencies(t *testing.T) { - pmb := NewBuilder(nil, nil, nil, nil, "test") + pmb := NewBuilder(nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) pmb.LoadPackageIndexFromFile(paths.New("testdata", "package_tooltest_index.json")) pmb.calculateCompatibleReleases() pm := pmb.Build() @@ -758,7 +760,7 @@ func TestFindPlatformReleaseDependencies(t *testing.T) { func TestLegacyPackageConversionToPluggableDiscovery(t *testing.T) { // Pass nil, since these paths are only used for installing - pmb := NewBuilder(nil, nil, nil, nil, "test") + pmb := NewBuilder(nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) // Hardware from main packages directory pmb.LoadHardwareFromDirectory(dataDir1.Join("packages")) pm := pmb.Build() @@ -828,7 +830,7 @@ func TestLegacyPackageConversionToPluggableDiscovery(t *testing.T) { func TestVariantAndCoreSelection(t *testing.T) { // Pass nil, since these paths are only used for installing - pmb := NewBuilder(nil, nil, nil, nil, "test") + pmb := NewBuilder(nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) // Hardware from main packages directory pmb.LoadHardwareFromDirectory(dataDir1.Join("packages")) pm := pmb.Build() @@ -923,7 +925,7 @@ func TestVariantAndCoreSelection(t *testing.T) { } func TestRunScript(t *testing.T) { - pmb := NewBuilder(nil, nil, nil, nil, "test") + pmb := NewBuilder(nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) pm := pmb.Build() pme, release := pm.NewExplorer() defer release() diff --git a/internal/arduino/cores/packagemanager/profiles.go b/internal/arduino/cores/packagemanager/profiles.go index 2422766e899..6cd6f3578d2 100644 --- a/internal/arduino/cores/packagemanager/profiles.go +++ b/internal/arduino/cores/packagemanager/profiles.go @@ -32,7 +32,7 @@ import ( // LoadHardwareForProfile load the hardware platforms for the given profile. // If installMissing is true then possibly missing tools and platforms will be downloaded and installed. -func (pmb *Builder) LoadHardwareForProfile(p *sketch.Profile, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) []error { +func (pmb *Builder) LoadHardwareForProfile(p *sketch.Profile, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, settings *configuration.Settings) []error { pmb.profile = p // Load required platforms @@ -40,7 +40,7 @@ func (pmb *Builder) LoadHardwareForProfile(p *sketch.Profile, installMissing boo var platformReleases []*cores.PlatformRelease indexURLs := map[string]*url.URL{} for _, platformRef := range p.Platforms { - if platformRelease, err := pmb.loadProfilePlatform(platformRef, installMissing, downloadCB, taskCB); err != nil { + if platformRelease, err := pmb.loadProfilePlatform(platformRef, installMissing, downloadCB, taskCB, settings); err != nil { merr = append(merr, fmt.Errorf("%s: %w", tr("loading required platform %s", platformRef), err)) logrus.WithField("platform", platformRef).WithError(err).Debugf("Error loading platform for profile") } else { @@ -56,7 +56,7 @@ func (pmb *Builder) LoadHardwareForProfile(p *sketch.Profile, installMissing boo for _, toolDep := range platformRelease.ToolDependencies { indexURL := indexURLs[toolDep.ToolPackager] - if err := pmb.loadProfileTool(toolDep, indexURL, installMissing, downloadCB, taskCB); err != nil { + if err := pmb.loadProfileTool(toolDep, indexURL, installMissing, downloadCB, taskCB, settings); err != nil { merr = append(merr, fmt.Errorf("%s: %w", tr("loading required tool %s", toolDep), err)) logrus.WithField("tool", toolDep).WithField("index_url", indexURL).WithError(err).Debugf("Error loading tool for profile") } else { @@ -68,13 +68,13 @@ func (pmb *Builder) LoadHardwareForProfile(p *sketch.Profile, installMissing boo return merr } -func (pmb *Builder) loadProfilePlatform(platformRef *sketch.ProfilePlatformReference, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) (*cores.PlatformRelease, error) { +func (pmb *Builder) loadProfilePlatform(platformRef *sketch.ProfilePlatformReference, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, settings *configuration.Settings) (*cores.PlatformRelease, error) { targetPackage := pmb.packages.GetOrCreatePackage(platformRef.Packager) platform := targetPackage.GetOrCreatePlatform(platformRef.Architecture) release := platform.GetOrCreateRelease(platformRef.Version) uid := platformRef.InternalUniqueIdentifier() - destDir := configuration.ProfilesCacheDir(configuration.Settings).Join(uid) + destDir := configuration.ProfilesCacheDir(settings).Join(uid) if !destDir.IsDir() && installMissing { // Try installing the missing platform if err := pmb.installMissingProfilePlatform(platformRef, destDir, downloadCB, taskCB); err != nil { @@ -91,7 +91,7 @@ func (pmb *Builder) installMissingProfilePlatform(platformRef *sketch.ProfilePla if err != nil { return fmt.Errorf("installing missing platform: could not create temp dir %s", err) } - tmpPmb := NewBuilder(tmp, tmp, pmb.DownloadDir, tmp, pmb.userAgent) + tmpPmb := NewBuilder(tmp, tmp, pmb.DownloadDir, tmp, pmb.userAgent, pmb.downloaderConfig) defer tmp.RemoveAll() // Download the main index and parse it @@ -103,7 +103,7 @@ func (pmb *Builder) installMissingProfilePlatform(platformRef *sketch.ProfilePla } for _, indexURL := range indexesToDownload { indexResource := resources.IndexResource{URL: indexURL} - if err := indexResource.Download(tmpPmb.IndexDir, downloadCB); err != nil { + if err := indexResource.Download(tmpPmb.IndexDir, downloadCB, pmb.downloaderConfig); err != nil { taskCB(&rpc.TaskProgress{Name: tr("Error downloading %s", indexURL)}) return &cmderrors.FailedDownloadError{Message: tr("Error downloading %s", indexURL), Cause: err} } @@ -121,7 +121,7 @@ func (pmb *Builder) installMissingProfilePlatform(platformRef *sketch.ProfilePla tmpPme, tmpRelease := tmpPm.NewExplorer() defer tmpRelease() - if err := tmpPme.DownloadPlatformRelease(tmpPlatformRelease, nil, downloadCB); err != nil { + if err := tmpPme.DownloadPlatformRelease(tmpPlatformRelease, downloadCB); err != nil { taskCB(&rpc.TaskProgress{Name: tr("Error downloading platform %s", tmpPlatformRelease)}) return &cmderrors.FailedInstallError{Message: tr("Error downloading platform %s", tmpPlatformRelease), Cause: err} } @@ -137,12 +137,12 @@ func (pmb *Builder) installMissingProfilePlatform(platformRef *sketch.ProfilePla return nil } -func (pmb *Builder) loadProfileTool(toolRef *cores.ToolDependency, indexURL *url.URL, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { +func (pmb *Builder) loadProfileTool(toolRef *cores.ToolDependency, indexURL *url.URL, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, settings *configuration.Settings) error { targetPackage := pmb.packages.GetOrCreatePackage(toolRef.ToolPackager) tool := targetPackage.GetOrCreateTool(toolRef.ToolName) uid := toolRef.InternalUniqueIdentifier(indexURL) - destDir := configuration.ProfilesCacheDir(configuration.Settings).Join(uid) + destDir := configuration.ProfilesCacheDir(settings).Join(uid) if !destDir.IsDir() && installMissing { // Try installing the missing tool @@ -172,7 +172,7 @@ func (pmb *Builder) installMissingProfileTool(toolRelease *cores.ToolRelease, de return &cmderrors.InvalidVersionError{Cause: fmt.Errorf(tr("version %s not available for this operating system", toolRelease))} } taskCB(&rpc.TaskProgress{Name: tr("Downloading tool %s", toolRelease)}) - if err := toolResource.Download(pmb.DownloadDir, nil, toolRelease.String(), downloadCB, ""); err != nil { + if err := toolResource.Download(pmb.DownloadDir, pmb.downloaderConfig, toolRelease.String(), downloadCB, ""); err != nil { taskCB(&rpc.TaskProgress{Name: tr("Error downloading tool %s", toolRelease)}) return &cmderrors.FailedInstallError{Message: tr("Error installing tool %s", toolRelease), Cause: err} } diff --git a/internal/arduino/httpclient/httpclient.go b/internal/arduino/httpclient/httpclient.go index ec4b4acc4a6..0e904dddf8f 100644 --- a/internal/arduino/httpclient/httpclient.go +++ b/internal/arduino/httpclient/httpclient.go @@ -16,12 +16,9 @@ package httpclient import ( - "net/http" - "net/url" "time" "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" @@ -34,7 +31,7 @@ var tr = i18n.Tr // DownloadFile downloads a file from a URL into the specified path. An optional config and options may be passed (or nil to use the defaults). // A DownloadProgressCB callback function must be passed to monitor download progress. // If a not empty queryParameter is passed, it is appended to the URL for analysis purposes. -func DownloadFile(path *paths.Path, URL string, queryParameter string, label string, downloadCB rpc.DownloadProgressCB, config *downloader.Config, options ...downloader.DownloadOptions) (returnedError error) { +func DownloadFile(path *paths.Path, URL string, queryParameter string, label string, downloadCB rpc.DownloadProgressCB, config downloader.Config, options ...downloader.DownloadOptions) (returnedError error) { if queryParameter != "" { URL = URL + "?query=" + queryParameter } @@ -48,15 +45,7 @@ func DownloadFile(path *paths.Path, URL string, queryParameter string, label str } }() - if config == nil { - c, err := GetDownloaderConfig() - if err != nil { - return err - } - config = c - } - - d, err := downloader.DownloadWithConfig(path.String(), URL, *config, options...) + d, err := downloader.DownloadWithConfig(path.String(), URL, config, options...) if err != nil { return err } @@ -76,52 +65,3 @@ func DownloadFile(path *paths.Path, URL string, queryParameter string, label str return nil } - -// Config is the configuration of the http client -type Config struct { - UserAgent string - Proxy *url.URL -} - -// New returns a default http client for use in the arduino-cli -func New() (*http.Client, error) { - userAgent := configuration.UserAgent(configuration.Settings) - proxy, err := configuration.NetworkProxy(configuration.Settings) - if err != nil { - return nil, err - } - return NewWithConfig(&Config{UserAgent: userAgent, Proxy: proxy}), nil -} - -// NewWithConfig creates a http client for use in the arduino-cli, with a given configuration -func NewWithConfig(config *Config) *http.Client { - return &http.Client{ - Transport: &httpClientRoundTripper{ - transport: &http.Transport{ - Proxy: http.ProxyURL(config.Proxy), - }, - userAgent: config.UserAgent, - }, - } -} - -// GetDownloaderConfig returns the downloader configuration based on current settings. -func GetDownloaderConfig() (*downloader.Config, error) { - httpClient, err := New() - if err != nil { - return nil, &cmderrors.InvalidArgumentError{Message: tr("Could not connect via HTTP"), Cause: err} - } - return &downloader.Config{ - HttpClient: *httpClient, - }, nil -} - -type httpClientRoundTripper struct { - transport http.RoundTripper - userAgent string -} - -func (h *httpClientRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - req.Header.Add("User-Agent", h.userAgent) - return h.transport.RoundTrip(req) -} diff --git a/internal/arduino/resources/download.go b/internal/arduino/resources/download.go index 4f1df1ad5b3..19b37df48ac 100644 --- a/internal/arduino/resources/download.go +++ b/internal/arduino/resources/download.go @@ -28,7 +28,7 @@ import ( // Download performs a download loop using the provided downloader.Config. // Messages are passed back to the DownloadProgressCB using label as text for the File field. // queryParameter is passed for analysis purposes. -func (r *DownloadResource) Download(downloadDir *paths.Path, config *downloader.Config, label string, downloadCB rpc.DownloadProgressCB, queryParameter string) error { +func (r *DownloadResource) Download(downloadDir *paths.Path, config downloader.Config, label string, downloadCB rpc.DownloadProgressCB, queryParameter string) error { path, err := r.ArchivePath(downloadDir) if err != nil { return fmt.Errorf(tr("getting archive path: %s"), err) diff --git a/internal/arduino/resources/helpers_test.go b/internal/arduino/resources/helpers_test.go index e56747d8b40..72c595f6c89 100644 --- a/internal/arduino/resources/helpers_test.go +++ b/internal/arduino/resources/helpers_test.go @@ -22,11 +22,10 @@ import ( "strings" "testing" - "github.com/arduino/arduino-cli/internal/arduino/httpclient" + "github.com/arduino/arduino-cli/internal/cli/configuration" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" - "go.bug.st/downloader/v2" ) type EchoHandler struct{} @@ -37,8 +36,7 @@ func (h *EchoHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reques } func TestDownloadApplyUserAgentHeaderUsingConfig(t *testing.T) { - goldUserAgentValue := "arduino-cli/0.0.0-test.preview (amd64; linux; go1.12.4) Commit:deadbeef/Build:2019-06-12 11:11:11.111" - goldUserAgentString := "User-Agent: " + goldUserAgentValue + goldUserAgentValue := "arduino-cli/0.0.0-test.preview" tmp, err := paths.MkTempDir("", "") require.NoError(t, err) @@ -54,9 +52,11 @@ func TestDownloadApplyUserAgentHeaderUsingConfig(t *testing.T) { URL: srv.URL, } - httpClient := httpclient.NewWithConfig(&httpclient.Config{UserAgent: goldUserAgentValue}) - - err = r.Download(tmp, &downloader.Config{HttpClient: *httpClient}, "", func(progress *rpc.DownloadProgress) {}, "") + settings := configuration.Init("") + settings.Set("network.user_agent_ext", goldUserAgentValue) + config, err := settings.DownloaderConfig() + require.NoError(t, err) + err = r.Download(tmp, config, "", func(progress *rpc.DownloadProgress) {}, "") require.NoError(t, err) // leverage the download helper to download the echo for the request made by the downloader itself @@ -71,12 +71,11 @@ func TestDownloadApplyUserAgentHeaderUsingConfig(t *testing.T) { require.NoError(t, err) requestLines := strings.Split(string(b), "\r\n") - userAgentHeaderString := "" + userAgentHeader := "" for _, line := range requestLines { if strings.Contains(line, "User-Agent: ") { - userAgentHeaderString = line + userAgentHeader = line } } - require.Equal(t, goldUserAgentString, userAgentHeaderString) - + require.Contains(t, userAgentHeader, goldUserAgentValue) } diff --git a/internal/arduino/resources/index.go b/internal/arduino/resources/index.go index 4740c6f12f0..fb05f55d776 100644 --- a/internal/arduino/resources/index.go +++ b/internal/arduino/resources/index.go @@ -58,7 +58,7 @@ func (res *IndexResource) IndexFileName() (string, error) { // Download will download the index and possibly check the signature using the Arduino's public key. // If the file is in .gz format it will be unpacked first. -func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadProgressCB) error { +func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadProgressCB, config downloader.Config) error { // Create destination directory if err := destDir.MkdirAll(); err != nil { return &cmderrors.PermissionDeniedError{Message: tr("Can't create data directory %s", destDir), Cause: err} @@ -78,7 +78,7 @@ func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadP return err } tmpIndexPath := tmp.Join(downloadFileName) - if err := httpclient.DownloadFile(tmpIndexPath, res.URL.String(), "", tr("Downloading index: %s", downloadFileName), downloadCB, nil, downloader.NoResume); err != nil { + if err := httpclient.DownloadFile(tmpIndexPath, res.URL.String(), "", tr("Downloading index: %s", downloadFileName), downloadCB, config, downloader.NoResume); err != nil { return &cmderrors.FailedDownloadError{Message: tr("Error downloading index '%s'", res.URL), Cause: err} } @@ -133,7 +133,7 @@ func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadP // Download signature signaturePath = destDir.Join(signatureFileName) tmpSignaturePath = tmp.Join(signatureFileName) - if err := httpclient.DownloadFile(tmpSignaturePath, res.SignatureURL.String(), "", tr("Downloading index signature: %s", signatureFileName), downloadCB, nil, downloader.NoResume); err != nil { + if err := httpclient.DownloadFile(tmpSignaturePath, res.SignatureURL.String(), "", tr("Downloading index signature: %s", signatureFileName), downloadCB, config, downloader.NoResume); err != nil { return &cmderrors.FailedDownloadError{Message: tr("Error downloading index signature '%s'", res.SignatureURL), Cause: err} } diff --git a/internal/arduino/resources/resources_test.go b/internal/arduino/resources/resources_test.go index 9cf8ae54509..ee987ddcea6 100644 --- a/internal/arduino/resources/resources_test.go +++ b/internal/arduino/resources/resources_test.go @@ -49,7 +49,7 @@ func TestDownloadAndChecksums(t *testing.T) { require.NoError(t, err) downloadAndTestChecksum := func() { - err := r.Download(tmp, &downloader.Config{}, "", func(*rpc.DownloadProgress) {}, "") + err := r.Download(tmp, downloader.Config{}, "", func(*rpc.DownloadProgress) {}, "") require.NoError(t, err) data, err := testFile.ReadFile() @@ -63,7 +63,7 @@ func TestDownloadAndChecksums(t *testing.T) { downloadAndTestChecksum() // Download with cached file - err = r.Download(tmp, &downloader.Config{}, "", func(*rpc.DownloadProgress) {}, "") + err = r.Download(tmp, downloader.Config{}, "", func(*rpc.DownloadProgress) {}, "") require.NoError(t, err) // Download if cached file has data in excess (redownload) @@ -132,7 +132,7 @@ func TestIndexDownloadAndSignatureWithinArchive(t *testing.T) { destDir, err := paths.MkTempDir("", "") require.NoError(t, err) defer destDir.RemoveAll() - err = idxResource.Download(destDir, func(curr *rpc.DownloadProgress) {}) + err = idxResource.Download(destDir, func(curr *rpc.DownloadProgress) {}, downloader.GetDefaultConfig()) require.NoError(t, err) require.True(t, destDir.Join("package_index.json").Exist()) require.True(t, destDir.Join("package_index.json.sig").Exist()) @@ -143,7 +143,7 @@ func TestIndexDownloadAndSignatureWithinArchive(t *testing.T) { invDestDir, err := paths.MkTempDir("", "") require.NoError(t, err) defer invDestDir.RemoveAll() - err = invIdxResource.Download(invDestDir, func(curr *rpc.DownloadProgress) {}) + err = invIdxResource.Download(invDestDir, func(curr *rpc.DownloadProgress) {}, downloader.GetDefaultConfig()) require.Error(t, err) require.Contains(t, err.Error(), "invalid signature") require.False(t, invDestDir.Join("package_index.json").Exist()) diff --git a/internal/cli/arguments/reference_test.go b/internal/cli/arguments/reference_test.go index b4176dc4e5e..f91109f8de6 100644 --- a/internal/cli/arguments/reference_test.go +++ b/internal/cli/arguments/reference_test.go @@ -48,10 +48,6 @@ var badCores = []struct { {"", nil}, } -func init() { - configuration.Settings = configuration.Init("") -} - func TestArgsStringify(t *testing.T) { for _, core := range goodCores { require.Equal(t, core.in, core.expected.String()) @@ -59,7 +55,7 @@ func TestArgsStringify(t *testing.T) { } func TestParseReferenceCores(t *testing.T) { - srv := commands.NewArduinoCoreServer("") + srv := commands.NewArduinoCoreServer("", configuration.Init("")) ctx := context.Background() for _, tt := range goodCores { actual, err := arguments.ParseReference(ctx, srv, tt.in) @@ -80,7 +76,7 @@ func TestParseArgs(t *testing.T) { input = append(input, tt.in) } - srv := commands.NewArduinoCoreServer("") + srv := commands.NewArduinoCoreServer("", configuration.Init("")) refs, err := arguments.ParseReferences(context.Background(), srv, input) assert.Nil(t, err) assert.Equal(t, len(goodCores), len(refs)) diff --git a/internal/cli/cache/cache.go b/internal/cli/cache/cache.go index 43c9d775234..09635356b1d 100644 --- a/internal/cli/cache/cache.go +++ b/internal/cli/cache/cache.go @@ -18,6 +18,7 @@ package cache import ( "os" + "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/i18n" "github.com/spf13/cobra" ) @@ -25,7 +26,7 @@ import ( var tr = i18n.Tr // NewCommand created a new `cache` command -func NewCommand() *cobra.Command { +func NewCommand(settings *configuration.Settings) *cobra.Command { cacheCommand := &cobra.Command{ Use: "cache", Short: tr("Arduino cache commands."), @@ -34,7 +35,7 @@ func NewCommand() *cobra.Command { " " + os.Args[0] + " cache clean\n\n", } - cacheCommand.AddCommand(initCleanCommand()) + cacheCommand.AddCommand(initCleanCommand(settings)) return cacheCommand } diff --git a/internal/cli/cache/clean.go b/internal/cli/cache/clean.go index c4c37a1cf1e..9d28e86b82e 100644 --- a/internal/cli/cache/clean.go +++ b/internal/cli/cache/clean.go @@ -24,22 +24,24 @@ import ( "github.com/spf13/cobra" ) -func initCleanCommand() *cobra.Command { +func initCleanCommand(settings *configuration.Settings) *cobra.Command { cleanCommand := &cobra.Command{ Use: "clean", Short: tr("Delete Boards/Library Manager download cache."), Long: tr("Delete contents of the `directories.downloads` folder, where archive files are staged during installation of libraries and boards platforms."), Example: " " + os.Args[0] + " cache clean", Args: cobra.NoArgs, - Run: runCleanCommand, + Run: func(cmd *cobra.Command, args []string) { + runCleanCommand(settings) + }, } return cleanCommand } -func runCleanCommand(cmd *cobra.Command, args []string) { +func runCleanCommand(settings *configuration.Settings) { logrus.Info("Executing `arduino-cli cache clean`") - cachePath := configuration.DownloadsDir(configuration.Settings) + cachePath := configuration.DownloadsDir(settings) err := cachePath.RemoveAll() if err != nil { feedback.Fatal(tr("Error cleaning caches: %v", err), feedback.ErrGeneric) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 8779a87ded0..3f4b994d541 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -62,7 +62,7 @@ var ( ) // NewCommand creates a new ArduinoCli command root -func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { cobra.AddTemplateFunc("tr", i18n.Tr) // ArduinoCli is the root command @@ -71,21 +71,21 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { Short: tr("Arduino CLI."), Long: tr("Arduino Command Line Interface (arduino-cli)."), Example: fmt.Sprintf(" %s <%s> [%s...]", os.Args[0], tr("command"), tr("flags")), - PersistentPreRun: preRun, + PersistentPreRun: func(cmd *cobra.Command, args []string) { preRun(cmd, defaultSettings) }, PersistentPostRun: postRun, } cmd.SetUsageTemplate(getUsageTemplate()) cmd.AddCommand(board.NewCommand(srv)) - cmd.AddCommand(cache.NewCommand()) - cmd.AddCommand(compile.NewCommand(srv)) + cmd.AddCommand(cache.NewCommand(defaultSettings)) + cmd.AddCommand(compile.NewCommand(srv, defaultSettings)) cmd.AddCommand(completion.NewCommand()) - cmd.AddCommand(config.NewCommand()) - cmd.AddCommand(core.NewCommand(srv)) - cmd.AddCommand(daemon.NewCommand()) + cmd.AddCommand(config.NewCommand(srv, defaultSettings)) + cmd.AddCommand(core.NewCommand(srv, defaultSettings)) + cmd.AddCommand(daemon.NewCommand(srv, defaultSettings)) cmd.AddCommand(generatedocs.NewCommand()) - cmd.AddCommand(lib.NewCommand(srv)) + cmd.AddCommand(lib.NewCommand(srv, defaultSettings)) cmd.AddCommand(monitor.NewCommand(srv)) cmd.AddCommand(outdated.NewCommand(srv)) cmd.AddCommand(sketch.NewCommand(srv)) @@ -94,7 +94,7 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { cmd.AddCommand(upload.NewCommand(srv)) cmd.AddCommand(debug.NewCommand(srv)) cmd.AddCommand(burnbootloader.NewCommand(srv)) - cmd.AddCommand(version.NewCommand()) + cmd.AddCommand(version.NewCommand(defaultSettings)) cmd.AddCommand(feedback.NewCommand()) cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, tr("Print the logs on the standard output.")) cmd.Flag("verbose").Hidden = true @@ -118,7 +118,7 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { cmd.PersistentFlags().StringVar(&configFile, "config-file", "", tr("The custom config file (if not specified the default will be used).")) cmd.PersistentFlags().StringSlice("additional-urls", []string{}, tr("Comma-separated list of additional URLs for the Boards Manager.")) cmd.PersistentFlags().Bool("no-color", false, "Disable colored output.") - configuration.BindFlags(cmd, configuration.Settings) + configuration.BindFlags(cmd, defaultSettings) return cmd } @@ -139,17 +139,17 @@ func toLogLevel(s string) (t logrus.Level, found bool) { return } -func preRun(cmd *cobra.Command, args []string) { - configFile := configuration.Settings.ConfigFileUsed() +func preRun(cmd *cobra.Command, defaultSettings *configuration.Settings) { + configFile := defaultSettings.ConfigFileUsed() // initialize inventory - err := inventory.Init(configuration.DataDir(configuration.Settings).String()) + err := inventory.Init(configuration.DataDir(defaultSettings).String()) if err != nil { feedback.Fatal(fmt.Sprintf("Error: %v", err), feedback.ErrInitializingInventory) } // https://no-color.org/ - color.NoColor = configuration.Settings.GetBool("output.no_color") || os.Getenv("NO_COLOR") != "" + color.NoColor = defaultSettings.GetBool("output.no_color") || os.Getenv("NO_COLOR") != "" // Set default feedback output to colorable feedback.SetOut(colorable.NewColorableStdout()) @@ -166,7 +166,7 @@ func preRun(cmd *cobra.Command, args []string) { if err != nil { updaterMessageChan <- nil } - updaterMessageChan <- updater.CheckForUpdate(currentVersion) + updaterMessageChan <- updater.CheckForUpdate(currentVersion, defaultSettings) }() // @@ -186,13 +186,13 @@ func preRun(cmd *cobra.Command, args []string) { } // set the Logger format - logFormat := strings.ToLower(configuration.Settings.GetString("logging.format")) + logFormat := strings.ToLower(defaultSettings.GetString("logging.format")) if logFormat == "json" { logrus.SetFormatter(&logrus.JSONFormatter{}) } // should we log to file? - logFile := configuration.Settings.GetString("logging.file") + logFile := defaultSettings.GetString("logging.file") if logFile != "" { file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { @@ -208,8 +208,8 @@ func preRun(cmd *cobra.Command, args []string) { } // configure logging filter - if lvl, found := toLogLevel(configuration.Settings.GetString("logging.level")); !found { - feedback.Fatal(tr("Invalid option for --log-level: %s", configuration.Settings.GetString("logging.level")), feedback.ErrBadArgument) + if lvl, found := toLogLevel(defaultSettings.GetString("logging.level")); !found { + feedback.Fatal(tr("Invalid option for --log-level: %s", defaultSettings.GetString("logging.level")), feedback.ErrBadArgument) } else { logrus.SetLevel(lvl) } diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index 389065db755..8d9cd0df4f0 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -76,7 +76,7 @@ var ( ) // NewCommand created a new `compile` command -func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { compileCommand := &cobra.Command{ Use: "compile", Short: tr("Compiles Arduino sketches."), @@ -135,7 +135,7 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { compileCommand.Flags().BoolVar(&skipLibrariesDiscovery, "skip-libraries-discovery", false, "Skip libraries discovery. This flag is provided only for use in language server and other, very specific, use cases. Do not use for normal compiles") compileCommand.Flag("skip-libraries-discovery").Hidden = true compileCommand.Flags().Int32VarP(&jobs, "jobs", "j", 0, tr("Max number of parallel compiles. If set to 0 the number of available CPUs cores will be used.")) - configuration.Settings.BindPFlag("sketch.always_export_binaries", compileCommand.Flags().Lookup("export-binaries")) + defaultSettings.BindPFlag("sketch.always_export_binaries", compileCommand.Flags().Lookup("export-binaries")) compileCommand.Flags().MarkDeprecated("build-properties", tr("please use --build-property instead.")) diff --git a/internal/cli/config/add.go b/internal/cli/config/add.go index 5018b0440de..5678e52e470 100644 --- a/internal/cli/config/add.go +++ b/internal/cli/config/add.go @@ -44,7 +44,7 @@ func uniquify[T comparable](s []T) []T { return result } -func initAddCommand() *cobra.Command { +func initAddCommand(defaultSettings *configuration.Settings) *cobra.Command { addCommand := &cobra.Command{ Use: "add", Short: tr("Adds one or more values to a setting."), @@ -53,15 +53,17 @@ func initAddCommand() *cobra.Command { " " + os.Args[0] + " config add board_manager.additional_urls https://example.com/package_example_index.json\n" + " " + os.Args[0] + " config add board_manager.additional_urls https://example.com/package_example_index.json https://another-url.com/package_another_index.json\n", Args: cobra.MinimumNArgs(2), - Run: runAddCommand, + Run: func(cmd *cobra.Command, args []string) { + runAddCommand(args, defaultSettings) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return GetConfigurationKeys(), cobra.ShellCompDirectiveDefault + return GetSlicesConfigurationKeys(defaultSettings), cobra.ShellCompDirectiveDefault }, } return addCommand } -func runAddCommand(cmd *cobra.Command, args []string) { +func runAddCommand(args []string, defaultSettings *configuration.Settings) { logrus.Info("Executing `arduino-cli config add`") key := args[0] kind := validateKey(key) @@ -71,12 +73,12 @@ func runAddCommand(cmd *cobra.Command, args []string) { feedback.Fatal(msg, feedback.ErrGeneric) } - v := configuration.Settings.GetStringSlice(key) + v := defaultSettings.GetStringSlice(key) v = append(v, args[1:]...) v = uniquify(v) - configuration.Settings.Set(key, v) + defaultSettings.Set(key, v) - if err := configuration.Settings.WriteConfig(); err != nil { + if err := defaultSettings.WriteConfig(); err != nil { feedback.Fatal(tr("Can't write config file: %v", err), feedback.ErrGeneric) } } diff --git a/internal/cli/config/config.go b/internal/cli/config/config.go index c59714478d3..d8d4cbf5e46 100644 --- a/internal/cli/config/config.go +++ b/internal/cli/config/config.go @@ -21,35 +21,36 @@ import ( "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" ) var tr = i18n.Tr // NewCommand created a new `config` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { configCommand := &cobra.Command{ Use: "config", Short: tr("Arduino configuration commands."), Example: " " + os.Args[0] + " config init", } - configCommand.AddCommand(initAddCommand()) - configCommand.AddCommand(initDeleteCommand()) - configCommand.AddCommand(initDumpCommand()) - configCommand.AddCommand(initGetCommand()) - configCommand.AddCommand(initInitCommand()) - configCommand.AddCommand(initRemoveCommand()) - configCommand.AddCommand(initSetCommand()) + configCommand.AddCommand(initAddCommand(defaultSettings)) + configCommand.AddCommand(initDeleteCommand(srv, defaultSettings)) + configCommand.AddCommand(initDumpCommand(defaultSettings)) + configCommand.AddCommand(initGetCommand(srv, defaultSettings)) + configCommand.AddCommand(initInitCommand(defaultSettings)) + configCommand.AddCommand(initRemoveCommand(defaultSettings)) + configCommand.AddCommand(initSetCommand(defaultSettings)) return configCommand } -// GetConfigurationKeys is an helper function useful to autocomplete. +// GetSlicesConfigurationKeys is an helper function useful to autocomplete. // It returns a list of configuration keys which can be changed -func GetConfigurationKeys() []string { +func GetSlicesConfigurationKeys(settings *configuration.Settings) []string { var res []string - keys := configuration.Settings.AllKeys() + keys := settings.AllKeys() for _, key := range keys { kind, _ := typeOf(key) if kind == reflect.Slice { diff --git a/internal/cli/config/delete.go b/internal/cli/config/delete.go index e5889a12952..75b81563aed 100644 --- a/internal/cli/config/delete.go +++ b/internal/cli/config/delete.go @@ -16,9 +16,9 @@ package config import ( + "context" "os" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" @@ -26,7 +26,8 @@ import ( "github.com/spf13/cobra" ) -func initDeleteCommand() *cobra.Command { +func initDeleteCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { + configFile := defaultSettings.ConfigFileUsed() deleteCommand := &cobra.Command{ Use: "delete", Short: tr("Deletes a settings key and all its sub keys."), @@ -35,25 +36,27 @@ func initDeleteCommand() *cobra.Command { " " + os.Args[0] + " config delete board_manager\n" + " " + os.Args[0] + " config delete board_manager.additional_urls", Args: cobra.ExactArgs(1), - Run: runDeleteCommand, + Run: func(cmd *cobra.Command, args []string) { + runDeleteCommand(srv, args, configFile) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return configuration.Settings.AllKeys(), cobra.ShellCompDirectiveDefault + return defaultSettings.AllKeys(), cobra.ShellCompDirectiveDefault }, } return deleteCommand } -func runDeleteCommand(cmd *cobra.Command, args []string) { +func runDeleteCommand(srv rpc.ArduinoCoreServiceServer, args []string, configFile string) { logrus.Info("Executing `arduino-cli config delete`") - toDelete := args[0] + ctx := context.Background() - svc := commands.NewArduinoCoreServer("") - _, err := svc.SettingsDelete(cmd.Context(), &rpc.SettingsDeleteRequest{Key: toDelete}) + toDelete := args[0] + _, err := srv.SettingsDelete(ctx, &rpc.SettingsDeleteRequest{Key: toDelete}) if err != nil { feedback.Fatal(tr("Cannot delete the key %[1]s: %[2]v", toDelete, err), feedback.ErrGeneric) } - _, err = svc.SettingsWrite(cmd.Context(), &rpc.SettingsWriteRequest{FilePath: configuration.Settings.ConfigFileUsed()}) + _, err = srv.SettingsWrite(ctx, &rpc.SettingsWriteRequest{FilePath: configFile}) if err != nil { - feedback.Fatal(tr("Cannot write the file %[1]s: %[2]v", configuration.Settings.ConfigFileUsed(), err), feedback.ErrGeneric) + feedback.Fatal(tr("Cannot write the file %[1]s: %[2]v", configFile, err), feedback.ErrGeneric) } } diff --git a/internal/cli/config/dump.go b/internal/cli/config/dump.go index 9e10bf34f9b..695e5b11ce8 100644 --- a/internal/cli/config/dump.go +++ b/internal/cli/config/dump.go @@ -25,23 +25,21 @@ import ( "gopkg.in/yaml.v3" ) -func initDumpCommand() *cobra.Command { +func initDumpCommand(defaultSettings *configuration.Settings) *cobra.Command { var dumpCommand = &cobra.Command{ Use: "dump", Short: tr("Prints the current configuration"), Long: tr("Prints the current configuration."), Example: " " + os.Args[0] + " config dump", Args: cobra.NoArgs, - Run: runDumpCommand, + Run: func(cmd *cobra.Command, args []string) { + logrus.Info("Executing `arduino-cli config dump`") + feedback.PrintResult(dumpResult{defaultSettings.AllSettings()}) + }, } return dumpCommand } -func runDumpCommand(cmd *cobra.Command, args []string) { - logrus.Info("Executing `arduino-cli config dump`") - feedback.PrintResult(dumpResult{configuration.Settings.AllSettings()}) -} - // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation type dumpResult struct { diff --git a/internal/cli/config/get.go b/internal/cli/config/get.go index 68b8afd0d4a..dec61be03a7 100644 --- a/internal/cli/config/get.go +++ b/internal/cli/config/get.go @@ -16,11 +16,11 @@ package config import ( + "context" "encoding/json" "fmt" "os" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" @@ -29,7 +29,7 @@ import ( "gopkg.in/yaml.v3" ) -func initGetCommand() *cobra.Command { +func initGetCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { getCommand := &cobra.Command{ Use: "get", Short: tr("Gets a settings key value."), @@ -39,20 +39,22 @@ func initGetCommand() *cobra.Command { " " + os.Args[0] + " config get daemon.port\n" + " " + os.Args[0] + " config get board_manager.additional_urls", Args: cobra.MinimumNArgs(1), - Run: runGetCommand, + Run: func(cmd *cobra.Command, args []string) { + runGetCommand(srv, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return configuration.Settings.AllKeys(), cobra.ShellCompDirectiveDefault + return defaultSettings.AllKeys(), cobra.ShellCompDirectiveDefault }, } return getCommand } -func runGetCommand(cmd *cobra.Command, args []string) { +func runGetCommand(srv rpc.ArduinoCoreServiceServer, args []string) { logrus.Info("Executing `arduino-cli config get`") + ctx := context.Background() - svc := commands.NewArduinoCoreServer("") for _, toGet := range args { - resp, err := svc.SettingsGetValue(cmd.Context(), &rpc.SettingsGetValueRequest{Key: toGet}) + resp, err := srv.SettingsGetValue(ctx, &rpc.SettingsGetValueRequest{Key: toGet}) if err != nil { feedback.Fatal(tr("Cannot get the configuration key %[1]s: %[2]v", toGet, err), feedback.ErrGeneric) } diff --git a/internal/cli/config/init.go b/internal/cli/config/init.go index afb05f74279..5bbc753b153 100644 --- a/internal/cli/config/init.go +++ b/internal/cli/config/init.go @@ -25,7 +25,6 @@ import ( "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/viper" ) var ( @@ -36,7 +35,7 @@ var ( const defaultFileName = "arduino-cli.yaml" -func initInitCommand() *cobra.Command { +func initInitCommand(defaultSettings *configuration.Settings) *cobra.Command { initCommand := &cobra.Command{ Use: "init", Short: tr("Writes current configuration to a configuration file."), @@ -50,7 +49,9 @@ func initInitCommand() *cobra.Command { PreRun: func(cmd *cobra.Command, args []string) { arguments.CheckFlagsConflicts(cmd, "dest-file", "dest-dir") }, - Run: runInitCommand, + Run: func(cmd *cobra.Command, args []string) { + runInitCommand(cmd, defaultSettings) + }, } initCommand.Flags().StringVar(&destDir, "dest-dir", "", tr("Sets where to save the configuration file.")) initCommand.Flags().StringVar(&destFile, "dest-file", "", tr("Sets where to save the configuration file.")) @@ -58,11 +59,11 @@ func initInitCommand() *cobra.Command { return initCommand } -func runInitCommand(cmd *cobra.Command, args []string) { +func runInitCommand(cmd *cobra.Command, defaultSettings *configuration.Settings) { logrus.Info("Executing `arduino-cli config init`") var configFileAbsPath *paths.Path - var absPath *paths.Path + var configFileDir *paths.Path var err error switch { @@ -72,30 +73,29 @@ func runInitCommand(cmd *cobra.Command, args []string) { feedback.Fatal(tr("Cannot find absolute path: %v", err), feedback.ErrGeneric) } - absPath = configFileAbsPath.Parent() + configFileDir = configFileAbsPath.Parent() case destDir == "": - destDir = configuration.Settings.GetString("directories.Data") + destDir = defaultSettings.GetString("directories.Data") fallthrough default: - absPath, err = paths.New(destDir).Abs() + configFileDir, err = paths.New(destDir).Abs() if err != nil { feedback.Fatal(tr("Cannot find absolute path: %v", err), feedback.ErrGeneric) } - configFileAbsPath = absPath.Join(defaultFileName) + configFileAbsPath = configFileDir.Join(defaultFileName) } if !overwrite && configFileAbsPath.Exist() { feedback.Fatal(tr("Config file already exists, use --overwrite to discard the existing one."), feedback.ErrGeneric) } - logrus.Infof("Writing config file to: %s", absPath) + logrus.Infof("Writing config file to: %s", configFileDir) - if err := absPath.MkdirAll(); err != nil { + if err := configFileDir.MkdirAll(); err != nil { feedback.Fatal(tr("Cannot create config file directory: %v", err), feedback.ErrGeneric) } - newSettings := viper.New() - configuration.SetDefaults(newSettings) + newSettings := configuration.NewSettings() configuration.BindFlags(cmd, newSettings) for _, url := range newSettings.GetStringSlice("board_manager.additional_urls") { diff --git a/internal/cli/config/remove.go b/internal/cli/config/remove.go index d7870172b0c..efbd416913a 100644 --- a/internal/cli/config/remove.go +++ b/internal/cli/config/remove.go @@ -25,7 +25,7 @@ import ( "github.com/spf13/cobra" ) -func initRemoveCommand() *cobra.Command { +func initRemoveCommand(defaultSettings *configuration.Settings) *cobra.Command { removeCommand := &cobra.Command{ Use: "remove", Short: tr("Removes one or more values from a setting."), @@ -34,15 +34,17 @@ func initRemoveCommand() *cobra.Command { " " + os.Args[0] + " config remove board_manager.additional_urls https://example.com/package_example_index.json\n" + " " + os.Args[0] + " config remove board_manager.additional_urls https://example.com/package_example_index.json https://another-url.com/package_another_index.json\n", Args: cobra.MinimumNArgs(2), - Run: runRemoveCommand, + Run: func(cmd *cobra.Command, args []string) { + runRemoveCommand(args, defaultSettings) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return GetConfigurationKeys(), cobra.ShellCompDirectiveDefault + return GetSlicesConfigurationKeys(defaultSettings), cobra.ShellCompDirectiveDefault }, } return removeCommand } -func runRemoveCommand(cmd *cobra.Command, args []string) { +func runRemoveCommand(args []string, defaultSettings *configuration.Settings) { logrus.Info("Executing `arduino-cli config remove`") key := args[0] kind := validateKey(key) @@ -53,7 +55,7 @@ func runRemoveCommand(cmd *cobra.Command, args []string) { } mappedValues := map[string]bool{} - for _, v := range configuration.Settings.GetStringSlice(key) { + for _, v := range defaultSettings.GetStringSlice(key) { mappedValues[v] = true } for _, arg := range args[1:] { @@ -63,9 +65,9 @@ func runRemoveCommand(cmd *cobra.Command, args []string) { for k := range mappedValues { values = append(values, k) } - configuration.Settings.Set(key, values) + defaultSettings.Set(key, values) - if err := configuration.Settings.WriteConfig(); err != nil { + if err := defaultSettings.WriteConfig(); err != nil { feedback.Fatal(tr("Can't write config file: %v", err), feedback.ErrGeneric) } } diff --git a/internal/cli/config/set.go b/internal/cli/config/set.go index 8fd002277da..10fb9e6f239 100644 --- a/internal/cli/config/set.go +++ b/internal/cli/config/set.go @@ -26,7 +26,7 @@ import ( "github.com/spf13/cobra" ) -func initSetCommand() *cobra.Command { +func initSetCommand(defaultSettings *configuration.Settings) *cobra.Command { setCommand := &cobra.Command{ Use: "set", Short: tr("Sets a setting value."), @@ -37,15 +37,17 @@ func initSetCommand() *cobra.Command { " " + os.Args[0] + " config set sketch.always_export_binaries true\n" + " " + os.Args[0] + " config set board_manager.additional_urls https://example.com/package_example_index.json https://another-url.com/package_another_index.json", Args: cobra.MinimumNArgs(2), - Run: runSetCommand, + Run: func(cmd *cobra.Command, args []string) { + runSetCommand(defaultSettings, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return configuration.Settings.AllKeys(), cobra.ShellCompDirectiveDefault + return defaultSettings.AllKeys(), cobra.ShellCompDirectiveDefault }, } return setCommand } -func runSetCommand(cmd *cobra.Command, args []string) { +func runSetCommand(defaultSettings *configuration.Settings, args []string) { logrus.Info("Executing `arduino-cli config set`") key := args[0] kind := validateKey(key) @@ -68,9 +70,9 @@ func runSetCommand(cmd *cobra.Command, args []string) { } } - configuration.Settings.Set(key, value) + defaultSettings.Set(key, value) - if err := configuration.Settings.WriteConfig(); err != nil { + if err := defaultSettings.WriteConfig(); err != nil { feedback.Fatal(tr("Writing config file: %v", err), feedback.ErrGeneric) } } diff --git a/internal/cli/configuration/configuration.go b/internal/cli/configuration/configuration.go index e251b80c514..996a1b1cbfc 100644 --- a/internal/cli/configuration/configuration.go +++ b/internal/cli/configuration/configuration.go @@ -29,18 +29,26 @@ import ( "github.com/spf13/viper" ) -// Settings is a global instance of viper holding configurations for the CLI and the gRPC consumers -var Settings *viper.Viper - var tr = i18n.Tr +// Settings contains the configuration of the Arduino CLI core service +type Settings struct { + *viper.Viper +} + +// NewSettings creates a new instance of Settings with the default values set +func NewSettings() *Settings { + res := &Settings{viper.New()} + SetDefaults(res) + return res +} + // Init initialize defaults and read the configuration file. // Please note the logging system hasn't been configured yet, // so logging shouldn't be used here. -func Init(configFile string) *viper.Viper { +func Init(configFile string) *Settings { // Create a new viper instance with default values for all the settings - settings := viper.New() - SetDefaults(settings) + settings := NewSettings() // Set config name and config path if configFilePath := paths.New(configFile); configFilePath != nil { @@ -70,7 +78,7 @@ func Init(configFile string) *viper.Viper { } // BindFlags creates all the flags binding between the cobra Command and the instance of viper -func BindFlags(cmd *cobra.Command, settings *viper.Viper) { +func BindFlags(cmd *cobra.Command, settings *Settings) { settings.BindPFlag("logging.level", cmd.Flag("log-level")) settings.BindPFlag("logging.file", cmd.Flag("log-file")) settings.BindPFlag("logging.format", cmd.Flag("log-format")) diff --git a/internal/cli/configuration/defaults.go b/internal/cli/configuration/defaults.go index be1a0088a62..c983310a746 100644 --- a/internal/cli/configuration/defaults.go +++ b/internal/cli/configuration/defaults.go @@ -19,12 +19,10 @@ import ( "path/filepath" "strings" "time" - - "github.com/spf13/viper" ) // SetDefaults sets the default values for certain keys -func SetDefaults(settings *viper.Viper) { +func SetDefaults(settings *Settings) { // logging settings.SetDefault("logging.level", "info") settings.SetDefault("logging.format", "text") diff --git a/internal/cli/configuration/directories.go b/internal/cli/configuration/directories.go index 27669e9017c..0fb9a9d19e5 100644 --- a/internal/cli/configuration/directories.go +++ b/internal/cli/configuration/directories.go @@ -17,15 +17,14 @@ package configuration import ( "github.com/arduino/go-paths-helper" - "github.com/spf13/viper" ) // HardwareDirectories returns all paths that may contains hardware packages. -func HardwareDirectories(settings *viper.Viper) paths.PathList { +func HardwareDirectories(settings *Settings) paths.PathList { res := paths.PathList{} if settings.IsSet("directories.Data") { - packagesDir := PackagesDir(Settings) + packagesDir := PackagesDir(settings) if packagesDir.IsDir() { res.Add(packagesDir) } @@ -44,34 +43,34 @@ func HardwareDirectories(settings *viper.Viper) paths.PathList { // IDEBuiltinLibrariesDir returns the IDE-bundled libraries path. Usually // this directory is present in the Arduino IDE. -func IDEBuiltinLibrariesDir(settings *viper.Viper) *paths.Path { - return paths.New(Settings.GetString("directories.builtin.Libraries")) +func IDEBuiltinLibrariesDir(settings *Settings) *paths.Path { + return paths.New(settings.GetString("directories.builtin.Libraries")) } // LibrariesDir returns the full path to the user directory containing // custom libraries -func LibrariesDir(settings *viper.Viper) *paths.Path { +func LibrariesDir(settings *Settings) *paths.Path { return paths.New(settings.GetString("directories.User")).Join("libraries") } // PackagesDir returns the full path to the packages folder -func PackagesDir(settings *viper.Viper) *paths.Path { +func PackagesDir(settings *Settings) *paths.Path { return DataDir(settings).Join("packages") } // ProfilesCacheDir returns the full path to the profiles cache directory // (it contains all the platforms and libraries used to compile a sketch // using profiles) -func ProfilesCacheDir(settings *viper.Viper) *paths.Path { +func ProfilesCacheDir(settings *Settings) *paths.Path { return DataDir(settings).Join("internal") } // DataDir returns the full path to the data directory -func DataDir(settings *viper.Viper) *paths.Path { +func DataDir(settings *Settings) *paths.Path { return paths.New(settings.GetString("directories.Data")) } // DownloadsDir returns the full path to the download cache directory -func DownloadsDir(settings *viper.Viper) *paths.Path { +func DownloadsDir(settings *Settings) *paths.Path { return paths.New(settings.GetString("directories.Downloads")) } diff --git a/internal/cli/configuration/network.go b/internal/cli/configuration/network.go index 793c67de155..c88b974c10d 100644 --- a/internal/cli/configuration/network.go +++ b/internal/cli/configuration/network.go @@ -17,16 +17,18 @@ package configuration import ( "fmt" + "net/http" "net/url" "os" "runtime" + "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/version" - "github.com/spf13/viper" + "go.bug.st/downloader/v2" ) // UserAgent returns the user agent (mainly used by HTTP clients) -func UserAgent(settings *viper.Viper) string { +func UserAgent(settings *Settings) string { subComponent := "" if settings != nil { subComponent = settings.GetString("network.user_agent_ext") @@ -50,7 +52,7 @@ func UserAgent(settings *viper.Viper) string { } // NetworkProxy returns the proxy configuration (mainly used by HTTP clients) -func NetworkProxy(settings *viper.Viper) (*url.URL, error) { +func NetworkProxy(settings *Settings) (*url.URL, error) { if settings == nil || !settings.IsSet("network.proxy") { return nil, nil } @@ -65,3 +67,42 @@ func NetworkProxy(settings *viper.Viper) (*url.URL, error) { return proxy, nil } } + +// NewHttpClient returns a new http client for use in the arduino-cli +func (settings *Settings) NewHttpClient() (*http.Client, error) { + proxy, err := NetworkProxy(settings) + if err != nil { + return nil, err + } + return &http.Client{ + Transport: &httpClientRoundTripper{ + transport: &http.Transport{ + Proxy: http.ProxyURL(proxy), + }, + userAgent: UserAgent(settings), + }, + }, nil +} + +type httpClientRoundTripper struct { + transport http.RoundTripper + userAgent string +} + +func (h *httpClientRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + req.Header.Add("User-Agent", h.userAgent) + return h.transport.RoundTrip(req) +} + +// DownloaderConfig returns the downloader configuration based on current settings. +func (settings *Settings) DownloaderConfig() (downloader.Config, error) { + httpClient, err := settings.NewHttpClient() + if err != nil { + return downloader.Config{}, &cmderrors.InvalidArgumentError{ + Message: tr("Could not connect via HTTP"), + Cause: err} + } + return downloader.Config{ + HttpClient: *httpClient, + }, nil +} diff --git a/internal/arduino/httpclient/httpclient_test.go b/internal/cli/configuration/network_test.go similarity index 79% rename from internal/arduino/httpclient/httpclient_test.go rename to internal/cli/configuration/network_test.go index a2dbc61c291..6d6218c8f5d 100644 --- a/internal/arduino/httpclient/httpclient_test.go +++ b/internal/cli/configuration/network_test.go @@ -13,16 +13,16 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package httpclient +package configuration_test import ( "fmt" "io" "net/http" "net/http/httptest" - "net/url" "testing" + "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/stretchr/testify/require" ) @@ -32,9 +32,10 @@ func TestUserAgentHeader(t *testing.T) { })) defer ts.Close() - client := NewWithConfig(&Config{ - UserAgent: "test-user-agent", - }) + settings := configuration.Init("") + settings.Set("network.user_agent_ext", "test-user-agent") + client, err := settings.NewHttpClient() + require.NoError(t, err) request, err := http.NewRequest("GET", ts.URL, nil) require.NoError(t, err) @@ -45,7 +46,7 @@ func TestUserAgentHeader(t *testing.T) { b, err := io.ReadAll(response.Body) require.NoError(t, err) - require.Equal(t, "test-user-agent", string(b)) + require.Contains(t, string(b), "test-user-agent") } func TestProxy(t *testing.T) { @@ -54,13 +55,11 @@ func TestProxy(t *testing.T) { })) defer ts.Close() - proxyURL, err := url.Parse(ts.URL) + settings := configuration.Init("") + settings.Set("network.proxy", ts.URL) + client, err := settings.NewHttpClient() require.NoError(t, err) - client := NewWithConfig(&Config{ - Proxy: proxyURL, - }) - request, err := http.NewRequest("GET", "http://arduino.cc", nil) require.NoError(t, err) diff --git a/internal/cli/core/core.go b/internal/cli/core/core.go index ac337df6c9e..e8e3c60220b 100644 --- a/internal/cli/core/core.go +++ b/internal/cli/core/core.go @@ -18,6 +18,7 @@ package core import ( "os" + "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" @@ -26,7 +27,7 @@ import ( var tr = i18n.Tr // NewCommand created a new `core` command -func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { coreCommand := &cobra.Command{ Use: "core", Short: tr("Arduino core operations."), @@ -40,7 +41,7 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { coreCommand.AddCommand(initUpdateIndexCommand(srv)) coreCommand.AddCommand(initUpgradeCommand(srv)) coreCommand.AddCommand(initUninstallCommand(srv)) - coreCommand.AddCommand(initSearchCommand(srv)) + coreCommand.AddCommand(initSearchCommand(srv, defaultSettings)) return coreCommand } diff --git a/internal/cli/core/search.go b/internal/cli/core/search.go index c2d2690bcd4..164fcd4d4f2 100644 --- a/internal/cli/core/search.go +++ b/internal/cli/core/search.go @@ -32,12 +32,15 @@ import ( "github.com/arduino/arduino-cli/internal/cli/feedback/table" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -func initSearchCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { +func initSearchCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { var allVersions bool + indexDir := configuration.DataDir(defaultSettings) + additionalURLs := defaultSettings.GetStringSlice("board_manager.additional_urls") searchCommand := &cobra.Command{ Use: fmt.Sprintf("search <%s...>", tr("keywords")), Short: tr("Search for a core in Boards Manager."), @@ -45,7 +48,7 @@ func initSearchCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { Example: " " + os.Args[0] + " core search MKRZero -a -v", Args: cobra.ArbitraryArgs, Run: func(cmd *cobra.Command, args []string) { - runSearchCommand(srv, args, allVersions) + runSearchCommand(srv, args, allVersions, indexDir, additionalURLs) }, } searchCommand.Flags().BoolVarP(&allVersions, "all", "a", false, tr("Show all available core versions.")) @@ -56,11 +59,11 @@ func initSearchCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { // indexUpdateInterval specifies the time threshold over which indexes are updated const indexUpdateInterval = "24h" -func runSearchCommand(srv rpc.ArduinoCoreServiceServer, args []string, allVersions bool) { +func runSearchCommand(srv rpc.ArduinoCoreServiceServer, args []string, allVersions bool, indexDir *paths.Path, additionalURLs []string) { ctx := context.Background() inst := instance.CreateAndInit(ctx, srv) - if indexesNeedUpdating(indexUpdateInterval) { + if indexesNeedUpdating(indexDir, additionalURLs, indexUpdateInterval) { stream := commands.UpdateIndexStreamResponseToCallbackFunction(ctx, feedback.ProgressBar()) err := srv.UpdateIndex(&rpc.UpdateIndexRequest{Instance: inst}, stream) if err != nil { @@ -141,9 +144,7 @@ func (sr searchResults) String() string { // used to update the indexes, if the duration is not valid a default // of 24 hours is used. // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". -func indexesNeedUpdating(duration string) bool { - indexpath := configuration.DataDir(configuration.Settings) - +func indexesNeedUpdating(indexDir *paths.Path, additionalURLs []string, duration string) bool { now := time.Now() modTimeThreshold, err := time.ParseDuration(duration) if err != nil { @@ -151,7 +152,7 @@ func indexesNeedUpdating(duration string) bool { } urls := []string{globals.DefaultIndexURL} - urls = append(urls, configuration.Settings.GetStringSlice("board_manager.additional_urls")...) + urls = append(urls, additionalURLs...) for _, u := range urls { URL, err := utils.URLParse(u) if err != nil { @@ -174,7 +175,7 @@ func indexesNeedUpdating(duration string) bool { indexFileName = strings.TrimSuffix(indexFileName, ".sig") indexFileName = strings.TrimSuffix(indexFileName, ".json") // and obtain package_index.json as result - coreIndexPath := indexpath.Join(indexFileName + ".json") + coreIndexPath := indexDir.Join(indexFileName + ".json") if coreIndexPath.NotExist() { return true } diff --git a/internal/cli/daemon/daemon.go b/internal/cli/daemon/daemon.go index 2422a03c2c2..8e719400eaf 100644 --- a/internal/cli/daemon/daemon.go +++ b/internal/cli/daemon/daemon.go @@ -24,12 +24,10 @@ import ( "strings" "syscall" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/i18n" - srv_commands "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "github.com/arduino/arduino-cli/version" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -45,17 +43,24 @@ var ( ) // NewCommand created a new `daemon` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { + var daemonPort string daemonCommand := &cobra.Command{ Use: "daemon", - Short: tr("Run as a daemon on port: %s", configuration.Settings.GetString("daemon.port")), - Long: tr("Running as a daemon the initialization of cores and libraries is done only once."), + Short: tr("Run the Arduino CLI as a gRPC daemon."), Example: " " + os.Args[0] + " daemon", Args: cobra.NoArgs, - Run: runDaemonCommand, + PreRun: func(cmd *cobra.Command, args []string) { + // Bundled libraries support is enabled by default when running as a daemon + defaultSettings.SetDefault( + "directories.builtin.Libraries", + configuration.GetDefaultBuiltinLibrariesDir()) + }, + Run: func(cmd *cobra.Command, args []string) { + runDaemonCommand(srv, daemonPort) + }, } - daemonCommand.PersistentFlags().String("port", "", tr("The TCP port the daemon will listen to")) - configuration.Settings.BindPFlag("daemon.port", daemonCommand.PersistentFlags().Lookup("port")) + daemonCommand.Flags().StringVar(&daemonPort, "port", defaultSettings.GetString("daemon.port"), tr("The TCP port the daemon will listen to")) daemonCommand.Flags().BoolVar(&daemonize, "daemonize", false, tr("Do not terminate daemon process if the parent process dies")) daemonCommand.Flags().BoolVar(&debug, "debug", false, tr("Enable debug logging of gRPC calls")) daemonCommand.Flags().StringVar(&debugFile, "debug-file", "", tr("Append debug logging to the specified file")) @@ -63,13 +68,9 @@ func NewCommand() *cobra.Command { return daemonCommand } -func runDaemonCommand(cmd *cobra.Command, args []string) { +func runDaemonCommand(srv rpc.ArduinoCoreServiceServer, daemonPort string) { logrus.Info("Executing `arduino-cli daemon`") - // Bundled libraries support is enabled by default when running as a daemon - configuration.Settings.SetDefault("directories.builtin.Libraries", configuration.GetDefaultBuiltinLibrariesDir()) - - port := configuration.Settings.GetString("daemon.port") gRPCOptions := []grpc.ServerOption{} if debugFile != "" { if !debug { @@ -98,43 +99,40 @@ func runDaemonCommand(cmd *cobra.Command, args []string) { ) } s := grpc.NewServer(gRPCOptions...) - // Set specific user-agent for the daemon - configuration.Settings.Set("network.user_agent_ext", "daemon") // register the commands service - srv_commands.RegisterArduinoCoreServiceServer(s, - commands.NewArduinoCoreServer(version.VersionInfo.VersionString)) + rpc.RegisterArduinoCoreServiceServer(s, srv) if !daemonize { // When parent process ends terminate also the daemon go feedback.ExitWhenParentProcessEnds() } - ip := "127.0.0.1" - lis, err := net.Listen("tcp", fmt.Sprintf("%s:%s", ip, port)) + daemonIP := "127.0.0.1" + lis, err := net.Listen("tcp", fmt.Sprintf("%s:%s", daemonIP, daemonPort)) if err != nil { // Invalid port, such as "Foo" var dnsError *net.DNSError if errors.As(err, &dnsError) { - feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. %[2]s is unknown name.", port, dnsError.Name), feedback.ErrBadTCPPortArgument) + feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. %[2]s is unknown name.", daemonPort, dnsError.Name), feedback.ErrBadTCPPortArgument) } // Invalid port number, such as -1 var addrError *net.AddrError if errors.As(err, &addrError) { - feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. %[2]s is an invalid port.", port, addrError.Addr), feedback.ErrBadTCPPortArgument) + feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. %[2]s is an invalid port.", daemonPort, addrError.Addr), feedback.ErrBadTCPPortArgument) } // Port is already in use var syscallErr *os.SyscallError if errors.As(err, &syscallErr) && errors.Is(syscallErr.Err, syscall.EADDRINUSE) { - feedback.Fatal(tr("Failed to listen on TCP port: %s. Address already in use.", port), feedback.ErrFailedToListenToTCPPort) + feedback.Fatal(tr("Failed to listen on TCP port: %s. Address already in use.", daemonPort), feedback.ErrFailedToListenToTCPPort) } - feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. Unexpected error: %[2]v", port, err), feedback.ErrFailedToListenToTCPPort) + feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. Unexpected error: %[2]v", daemonPort, err), feedback.ErrFailedToListenToTCPPort) } // We need to retrieve the port used only if the user did not specify it // and let the OS choose it randomly, in all other cases we already know // which port is used. - if port == "0" { + if daemonPort == "0" { address := lis.Addr() split := strings.Split(address.String(), ":") @@ -142,12 +140,12 @@ func runDaemonCommand(cmd *cobra.Command, args []string) { feedback.Fatal(tr("Invalid TCP address: port is missing"), feedback.ErrBadTCPPortArgument) } - port = split[1] + daemonPort = split[1] } feedback.PrintResult(daemonResult{ - IP: ip, - Port: port, + IP: daemonIP, + Port: daemonPort, }) if err := s.Serve(lis); err != nil { diff --git a/internal/cli/lib/install.go b/internal/cli/lib/install.go index dce521eb6d3..d8aafc8bd1a 100644 --- a/internal/cli/lib/install.go +++ b/internal/cli/lib/install.go @@ -34,12 +34,13 @@ import ( semver "go.bug.st/relaxed-semver" ) -func initInstallCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { +func initInstallCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { var noDeps bool var noOverwrite bool var gitURL bool var zipPath bool var useBuiltinLibrariesDir bool + enableUnsafeInstall := defaultSettings.GetBool("library.enable_unsafe_install") installCommand := &cobra.Command{ Use: fmt.Sprintf("install %s[@%s]...", tr("LIBRARY"), tr("VERSION_NUMBER")), Short: tr("Installs one or more specified libraries into the system."), @@ -52,7 +53,7 @@ func initInstallCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { " " + os.Args[0] + " lib install --zip-path /path/to/WiFi101.zip /path/to/ArduinoBLE.zip\n", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runInstallCommand(srv, args, noDeps, noOverwrite, gitURL, zipPath, useBuiltinLibrariesDir) + runInstallCommand(srv, args, noDeps, noOverwrite, gitURL, zipPath, useBuiltinLibrariesDir, enableUnsafeInstall) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return arguments.GetInstallableLibs(context.Background(), srv), cobra.ShellCompDirectiveDefault @@ -66,13 +67,13 @@ func initInstallCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { return installCommand } -func runInstallCommand(srv rpc.ArduinoCoreServiceServer, args []string, noDeps bool, noOverwrite bool, gitURL bool, zipPath bool, useBuiltinLibrariesDir bool) { +func runInstallCommand(srv rpc.ArduinoCoreServiceServer, args []string, noDeps bool, noOverwrite bool, gitURL bool, zipPath bool, useBuiltinLibrariesDir bool, enableUnsafeInstall bool) { ctx := context.Background() instance := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli lib install`") if zipPath || gitURL { - if !configuration.Settings.GetBool("library.enable_unsafe_install") { + if !enableUnsafeInstall { documentationURL := "https://arduino.github.io/arduino-cli/latest/configuration/#configuration-keys" _, err := semver.Parse(version.VersionInfo.VersionString) if err == nil { diff --git a/internal/cli/lib/lib.go b/internal/cli/lib/lib.go index deab795b9f5..35cd5286139 100644 --- a/internal/cli/lib/lib.go +++ b/internal/cli/lib/lib.go @@ -18,6 +18,7 @@ package lib import ( "os" + "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" @@ -26,7 +27,7 @@ import ( var tr = i18n.Tr // NewCommand created a new `lib` command -func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { libCommand := &cobra.Command{ Use: "lib", Short: tr("Arduino commands about libraries."), @@ -37,10 +38,10 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { } libCommand.AddCommand(initDownloadCommand(srv)) - libCommand.AddCommand(initInstallCommand(srv)) + libCommand.AddCommand(initInstallCommand(srv, defaultSettings)) libCommand.AddCommand(initListCommand(srv)) libCommand.AddCommand(initExamplesCommand(srv)) - libCommand.AddCommand(initSearchCommand(srv)) + libCommand.AddCommand(initSearchCommand(srv, defaultSettings)) libCommand.AddCommand(initUninstallCommand(srv)) libCommand.AddCommand(initUpgradeCommand(srv)) libCommand.AddCommand(initUpdateIndexCommand(srv)) diff --git a/internal/cli/lib/search.go b/internal/cli/lib/search.go index 9bb14709df9..31f46ed58cc 100644 --- a/internal/cli/lib/search.go +++ b/internal/cli/lib/search.go @@ -33,7 +33,7 @@ import ( "github.com/spf13/cobra" ) -func initSearchCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { +func initSearchCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *configuration.Settings) *cobra.Command { var namesOnly bool var omitReleasesDetails bool searchCommand := &cobra.Command{ @@ -92,7 +92,8 @@ In addition to the fields listed above, QV terms can use these qualifiers: " " + os.Args[0] + " lib search dependencies=IRremote # " + tr("libraries that depend only on \"IRremote\"") + "\n", Args: cobra.ArbitraryArgs, Run: func(cmd *cobra.Command, args []string) { - runSearchCommand(srv, args, namesOnly, omitReleasesDetails) + indexDir := paths.New(defaultSettings.GetString("directories.Data")) + runSearchCommand(srv, args, namesOnly, omitReleasesDetails, indexDir) }, } searchCommand.Flags().BoolVar(&namesOnly, "names", false, tr("Show library names only.")) @@ -103,13 +104,13 @@ In addition to the fields listed above, QV terms can use these qualifiers: // indexUpdateInterval specifies the time threshold over which indexes are updated const indexUpdateInterval = 60 * time.Minute -func runSearchCommand(srv rpc.ArduinoCoreServiceServer, args []string, namesOnly bool, omitReleasesDetails bool) { +func runSearchCommand(srv rpc.ArduinoCoreServiceServer, args []string, namesOnly bool, omitReleasesDetails bool, indexDir *paths.Path) { ctx := context.Background() inst := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli lib search`") - if indexNeedsUpdating(indexUpdateInterval) { + if indexNeedsUpdating(indexUpdateInterval, indexDir) { req := &rpc.UpdateLibrariesIndexRequest{Instance: inst} stream := commands.UpdateLibrariesIndexStreamResponseToCallbackFunction(ctx, feedback.ProgressBar()) if err := srv.UpdateLibrariesIndex(req, stream); err != nil { @@ -221,11 +222,11 @@ func (res librarySearchResult) String() string { } // indexNeedsUpdating returns whether library_index.json needs updating -func indexNeedsUpdating(timeout time.Duration) bool { +// TODO: Make this a gRPC call +func indexNeedsUpdating(timeout time.Duration, indexDir *paths.Path) bool { // Library index path is constant (relative to the data directory). // It does not depend on board manager URLs or any other configuration. - dataDir := configuration.Settings.GetString("directories.Data") - indexPath := paths.New(dataDir).Join("library_index.json") + indexPath := indexDir.Join("library_index.json") // Verify the index file exists and we can read its fstat attrs. if indexPath.NotExist() { return true diff --git a/internal/cli/updater/updater.go b/internal/cli/updater/updater.go index 8a980fb3ad9..2c5c2c0f34b 100644 --- a/internal/cli/updater/updater.go +++ b/internal/cli/updater/updater.go @@ -20,7 +20,6 @@ import ( "strings" "time" - "github.com/arduino/arduino-cli/internal/arduino/httpclient" "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/i18n" @@ -34,24 +33,27 @@ var tr = i18n.Tr // CheckForUpdate returns the latest available version if greater than // the one running and it makes sense to check for an update, nil in all other cases -func CheckForUpdate(currentVersion *semver.Version) *semver.Version { - if !shouldCheckForUpdate(currentVersion) { +func CheckForUpdate(currentVersion *semver.Version, settings *configuration.Settings) *semver.Version { + if strings.Contains(currentVersion.String(), "git-snapshot") || strings.Contains(currentVersion.String(), "nightly") { + return nil + } + if !settings.GetBool("updater.enable_notification") { + return nil + } + if inventory.Store.IsSet("updater.last_check_time") && time.Since(inventory.Store.GetTime("updater.last_check_time")).Hours() < 24 { + return nil + } + if feedback.IsCI() || !feedback.IsInteractive() || !feedback.HasConsole() { return nil } - return ForceCheckForUpdate(currentVersion) -} - -// ForceCheckForUpdate always returns the latest available version if greater than -// the one running, nil in all other cases -func ForceCheckForUpdate(currentVersion *semver.Version) *semver.Version { defer func() { // Always save the last time we checked for updates at the end inventory.Store.Set("updater.last_check_time", time.Now()) inventory.WriteStore() }() - latestVersion, err := semver.Parse(getLatestRelease()) + latestVersion, err := semver.Parse(getLatestRelease(settings)) if err != nil { return nil } @@ -74,32 +76,10 @@ func NotifyNewVersionIsAvailable(latestVersion string) { feedback.Warning(msg) } -// shouldCheckForUpdate return true if it actually makes sense to check for new updates, -// false in all other cases. -func shouldCheckForUpdate(currentVersion *semver.Version) bool { - if strings.Contains(currentVersion.String(), "git-snapshot") || strings.Contains(currentVersion.String(), "nightly") { - // This is a dev build, no need to check for updates - return false - } - - if !configuration.Settings.GetBool("updater.enable_notification") { - // Don't check if the user disabled the notification - return false - } - - if inventory.Store.IsSet("updater.last_check_time") && time.Since(inventory.Store.GetTime("updater.last_check_time")).Hours() < 24 { - // Checked less than 24 hours ago, let's wait - return false - } - - // Don't check when running on CI or on non interactive consoles - return !feedback.IsCI() && feedback.IsInteractive() && feedback.HasConsole() -} - // getLatestRelease queries the official Arduino download server for the latest release, // if there are no errors or issues a version string is returned, in all other case an empty string. -func getLatestRelease() string { - client, err := httpclient.New() +func getLatestRelease(settings *configuration.Settings) string { + client, err := settings.NewHttpClient() if err != nil { return "" } diff --git a/internal/cli/version/version.go b/internal/cli/version/version.go index 990a4cbd236..1688a79fec6 100644 --- a/internal/cli/version/version.go +++ b/internal/cli/version/version.go @@ -20,6 +20,7 @@ import ( "os" "strings" + "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/updater" "github.com/arduino/arduino-cli/internal/i18n" @@ -32,19 +33,21 @@ import ( var tr = i18n.Tr // NewCommand created a new `version` command -func NewCommand() *cobra.Command { +func NewCommand(defaultSettings *configuration.Settings) *cobra.Command { versionCommand := &cobra.Command{ Use: "version", Short: tr("Shows version number of Arduino CLI."), Long: tr("Shows the version number of Arduino CLI which is installed on your system."), Example: " " + os.Args[0] + " version", Args: cobra.NoArgs, - Run: runVersionCommand, + Run: func(cmd *cobra.Command, args []string) { + runVersionCommand(defaultSettings) + }, } return versionCommand } -func runVersionCommand(cmd *cobra.Command, args []string) { +func runVersionCommand(settings *configuration.Settings) { logrus.Info("Executing `arduino-cli version`") info := version.VersionInfo @@ -59,7 +62,7 @@ func runVersionCommand(cmd *cobra.Command, args []string) { if err != nil { feedback.Fatal(fmt.Sprintf("Error parsing current version: %s", err), feedback.ErrGeneric) } - latestVersion := updater.CheckForUpdate(currentVersion) + latestVersion := updater.CheckForUpdate(currentVersion, settings) if feedback.GetFormat() != feedback.Text && latestVersion != nil { // Set this only we managed to get the latest version diff --git a/internal/docsgen/main.go b/internal/docsgen/main.go index eb8903507e1..69ac4983de9 100644 --- a/internal/docsgen/main.go +++ b/internal/docsgen/main.go @@ -31,8 +31,8 @@ func main() { os.MkdirAll(os.Args[1], 0755) // Create the output folder if it doesn't already exist - configuration.Settings = configuration.Init(configuration.FindConfigFileInArgsFallbackOnEnv(os.Args)) - cli := cli.NewCommand(nil) + settings := configuration.Init(configuration.FindConfigFileInArgsFallbackOnEnv(os.Args)) + cli := cli.NewCommand(nil, settings) cli.DisableAutoGenTag = true // Disable addition of auto-generated date stamp err := doc.GenMarkdownTree(cli, os.Args[1]) if err != nil { diff --git a/main.go b/main.go index 8587037fbf1..2255ae626b4 100644 --- a/main.go +++ b/main.go @@ -27,12 +27,12 @@ import ( ) func main() { - configuration.Settings = configuration.Init(configuration.FindConfigFileInArgsFallbackOnEnv(os.Args)) - i18n.Init(configuration.Settings.GetString("locale")) + defaultSettings := configuration.Init(configuration.FindConfigFileInArgsFallbackOnEnv(os.Args)) + i18n.Init(defaultSettings.GetString("locale")) - srv := commands.NewArduinoCoreServer(version.VersionInfo.VersionString) + srv := commands.NewArduinoCoreServer(version.VersionInfo.VersionString, defaultSettings) - arduinoCmd := cli.NewCommand(srv) + arduinoCmd := cli.NewCommand(srv, defaultSettings) if err := arduinoCmd.Execute(); err != nil { feedback.FatalError(err, feedback.ErrGeneric) }