diff --git a/arduino/cores/packagemanager/loader.go b/arduino/cores/packagemanager/loader.go index f0c2f99d548..3b2700cc65e 100644 --- a/arduino/cores/packagemanager/loader.go +++ b/arduino/cores/packagemanager/loader.go @@ -24,25 +24,20 @@ import ( "strings" "github.com/arduino/arduino-cli/arduino/cores" - "github.com/arduino/arduino-cli/configs" + "github.com/arduino/arduino-cli/configuration" "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" semver "go.bug.st/relaxed-semver" ) // LoadHardware read all plaforms from the configured paths -func (pm *PackageManager) LoadHardware(config *configs.Configuration) error { - dirs, err := config.HardwareDirectories() - if err != nil { - return fmt.Errorf("getting hardware directory: %s", err) - } +func (pm *PackageManager) LoadHardware() error { + dirs := configuration.HardwareDirectories() if err := pm.LoadHardwareFromDirectories(dirs); err != nil { return err } - dirs, err = config.BundleToolsDirectories() - if err != nil { - return fmt.Errorf("getting hardware directory: %s", err) - } + + dirs = configuration.BundleToolsDirectories() return pm.LoadToolsFromBundleDirectories(dirs) } diff --git a/arduino/cores/packagemanager/package_manager_test.go b/arduino/cores/packagemanager/package_manager_test.go index 94ffdf5c012..428bc50cb32 100644 --- a/arduino/cores/packagemanager/package_manager_test.go +++ b/arduino/cores/packagemanager/package_manager_test.go @@ -20,13 +20,15 @@ package packagemanager_test import ( "fmt" "net/url" + "os" "testing" "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores/packagemanager" - "github.com/arduino/arduino-cli/configs" + "github.com/arduino/arduino-cli/configuration" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" + "github.com/spf13/viper" "github.com/stretchr/testify/require" semver "go.bug.st/relaxed-semver" ) @@ -212,14 +214,16 @@ func TestBoardOptionsFunctions(t *testing.T) { } func TestFindToolsRequiredForBoard(t *testing.T) { + os.Setenv("ARDUINO_DATA_DIR", dataDir1.String()) + configuration.Init("") + fmt.Println(viper.AllSettings()) pm := packagemanager.NewPackageManager( dataDir1, - dataDir1.Join("packages"), - dataDir1.Join("staging"), - dataDir1) - conf := &configs.Configuration{ - DataDir: dataDir1, - } + paths.New(viper.GetString("directories.Packages")), + paths.New(viper.GetString("directories.Downloads")), + dataDir1, + ) + loadIndex := func(addr string) { res, err := url.Parse(addr) require.NoError(t, err) @@ -228,7 +232,7 @@ func TestFindToolsRequiredForBoard(t *testing.T) { loadIndex("https://dl.espressif.com/dl/package_esp32_index.json") loadIndex("http://arduino.esp8266.com/stable/package_esp8266com_index.json") loadIndex("https://adafruit.github.io/arduino-board-index/package_adafruit_index.json") - require.NoError(t, pm.LoadHardware(conf)) + require.NoError(t, pm.LoadHardware()) esp32, err := pm.FindBoardWithFQBN("esp32:esp32:esp32") require.NoError(t, err) esptool231 := pm.FindToolDependency(&cores.ToolDependency{ diff --git a/cli/cli.go b/cli/cli.go index 12450fa7ea0..c174b358d36 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -21,6 +21,8 @@ import ( "fmt" "io/ioutil" "os" + "path" + "path/filepath" "strings" "github.com/arduino/arduino-cli/cli/board" @@ -37,10 +39,12 @@ import ( "github.com/arduino/arduino-cli/cli/sketch" "github.com/arduino/arduino-cli/cli/upload" "github.com/arduino/arduino-cli/cli/version" + "github.com/arduino/arduino-cli/configuration" "github.com/mattn/go-colorable" "github.com/rifflock/lfshook" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var ( @@ -54,13 +58,8 @@ var ( } verbose bool - logFile string - logFormat string outputFormat string -) - -const ( - defaultLogLevel = "info" + configFile string ) // Init the cobra root command @@ -82,12 +81,16 @@ func createCliCommandTree(cmd *cobra.Command) { cmd.AddCommand(version.NewCommand()) cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Print the logs on the standard output.") - cmd.PersistentFlags().StringVar(&globals.LogLevel, "log-level", defaultLogLevel, "Messages with this level and above will be logged.") - cmd.PersistentFlags().StringVar(&logFile, "log-file", "", "Path to the file where logs will be written.") - cmd.PersistentFlags().StringVar(&logFormat, "log-format", "text", "The output format for the logs, can be [text|json].") + cmd.PersistentFlags().String("log-level", "", "Messages with this level and above will be logged.") + viper.BindPFlag("logging.level", cmd.PersistentFlags().Lookup("log-level")) + cmd.PersistentFlags().String("log-file", "", "Path to the file where logs will be written.") + viper.BindPFlag("logging.file", cmd.PersistentFlags().Lookup("log-file")) + cmd.PersistentFlags().String("log-format", "", "The output format for the logs, can be [text|json].") + viper.BindPFlag("logging.format", cmd.PersistentFlags().Lookup("log-format")) cmd.PersistentFlags().StringVar(&outputFormat, "format", "text", "The output format, can be [text|json].") - cmd.PersistentFlags().StringVar(&globals.YAMLConfigFile, "config-file", "", "The custom config file (if not specified the default will be used).") - cmd.PersistentFlags().StringSliceVar(&globals.AdditionalUrls, "additional-urls", []string{}, "Additional URLs for the board manager.") + cmd.PersistentFlags().StringVar(&configFile, "config-file", "", "The custom config file (if not specified the default will be used).") + cmd.PersistentFlags().StringSlice("additional-urls", []string{}, "Additional URLs for the board manager.") + viper.BindPFlag("board_manager.additional_urls", cmd.PersistentFlags().Lookup("additional-urls")) } // convert the string passed to the `--log-level` option to the corresponding @@ -115,14 +118,73 @@ func parseFormatString(arg string) (feedback.OutputFormat, bool) { return f, found } +// This function is here to replicate the old logic looking for a config +// file in the parent tree of the CWD, aka "project config". +// Please +func searchConfigTree(cwd string) string { + // go back up to root and search for the config file + for { + if _, err := os.Stat(path.Join(cwd, "arduino-cli.yaml")); os.IsNotExist(err) { + // no config file found + next := path.Join(cwd, "..") + if filepath.Clean(next) == filepath.Clean(cwd) { + return "" + } + cwd = next + } else { + return cwd + } + } +} + func preRun(cmd *cobra.Command, args []string) { - // normalize the format strings - outputFormat = strings.ToLower(outputFormat) - // configure the output package - output.OutputFormat = outputFormat - logFormat = strings.ToLower(logFormat) + // + // Prepare the configuration system + // + configPath := "" + + // get cwd, if something is wrong don't do anything and let + // configuration init proceed + if cwd, err := os.Getwd(); err == nil { + configPath = searchConfigTree(cwd) + } + + // override the config path if --config-file was passed + if fi, err := os.Stat(configFile); err == nil { + if fi.IsDir() { + configPath = configFile + } else { + configPath = filepath.Dir(configFile) + } + } + + // initialize the config system + configuration.Init(configPath) + configFile := viper.ConfigFileUsed() + + // + // Prepare logging + // + + // decide whether we should log to stdout + if verbose { + // if we print on stdout, do it in full colors + logrus.SetOutput(colorable.NewColorableStdout()) + logrus.SetFormatter(&logrus.TextFormatter{ + ForceColors: true, + }) + } else { + logrus.SetOutput(ioutil.Discard) + } + + // set the Logger format + logFormat := strings.ToLower(viper.GetString("logging.format")) + if logFormat == "json" { + logrus.SetFormatter(&logrus.JSONFormatter{}) + } // should we log to file? + logFile := viper.GetString("logging.file") if logFile != "" { file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { @@ -138,30 +200,22 @@ func preRun(cmd *cobra.Command, args []string) { } } - // should we log to stdout? - if verbose { - logrus.SetOutput(colorable.NewColorableStdout()) - logrus.SetFormatter(&logrus.TextFormatter{ - ForceColors: true, - }) - } else { - // Discard logrus output if no writer was set - logrus.SetOutput(ioutil.Discard) - } - // configure logging filter - if lvl, found := toLogLevel(globals.LogLevel); !found { - fmt.Printf("Invalid option for --log-level: %s", globals.LogLevel) + if lvl, found := toLogLevel(viper.GetString("logging.level")); !found { + feedback.Errorf("Invalid option for --log-level: %s", viper.GetString("logging.level")) os.Exit(errorcodes.ErrBadArgument) } else { logrus.SetLevel(lvl) } - // set the Logger format - if logFormat == "json" { - logrus.SetFormatter(&logrus.JSONFormatter{}) - } + // + // Prepare the Feedback system + // + // normalize the format strings + outputFormat = strings.ToLower(outputFormat) + // configure the output package + output.OutputFormat = outputFormat // check the right output format was passed format, found := parseFormatString(outputFormat) if !found { @@ -172,12 +226,18 @@ func preRun(cmd *cobra.Command, args []string) { // use the output format to configure the Feedback feedback.SetFormat(format) - globals.InitConfigs() + // + // Print some status info and check command is consistent + // + + if configFile != "" { + logrus.Infof("Using config file: %s", configFile) + } else { + logrus.Info("Config file not found, using default values") + } - logrus.Info(globals.VersionInfo.Application + "-" + globals.VersionInfo.VersionString) - logrus.Info("Starting root command preparation (`arduino`)") + logrus.Info(globals.VersionInfo.Application + " version " + globals.VersionInfo.VersionString) - logrus.Info("Formatter set") if outputFormat != "text" { cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { logrus.Warn("Calling help on JSON format") diff --git a/cli/cli_test.go b/cli/cli_test.go index 217712ac4be..0aa4eeb0662 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -28,6 +28,8 @@ import ( "runtime" "testing" + "github.com/spf13/viper" + "github.com/arduino/arduino-cli/cli/feedback" "bou.ke/monkey" @@ -92,6 +94,7 @@ func TestMain(m *testing.M) { // SetUp currDataDir = tmpDirOrDie() + os.MkdirAll(filepath.Join(currDataDir, "packages"), 0755) os.Setenv("ARDUINO_DATA_DIR", currDataDir) currDownloadDir = tmpDirOrDie() os.Setenv("ARDUINO_DOWNLOADS_DIR", currDownloadDir) @@ -127,25 +130,10 @@ func executeWithArgs(args ...string) (int, []byte) { var output []byte var exitCode int fmt.Printf("RUNNING: %s\n", args) + viper.Reset() // This closure is here because we won't that the defer are executed after the end of the "executeWithArgs" method func() { - // Create an empty config for the CLI test in the same dir of the test - conf := paths.New("arduino-cli.yaml") - if conf.Exist() { - panic("config file must not exist already") - } - - if err := conf.WriteFile([]byte("board_manager:\n additional_urls:\n")); err != nil { - panic(err) - } - - defer func() { - if err := conf.Remove(); err != nil { - panic(err) - } - }() - redirect := &stdOutRedirect{} redirect.Open() // re-init feedback so it'll write to our grabber @@ -364,28 +352,7 @@ func TestCompileCommandsIntegration(t *testing.T) { } func TestInvalidCoreURLIntegration(t *testing.T) { - // override SetUp dirs - tmp := tmpDirOrDie() - defer os.RemoveAll(tmp) - os.Setenv("ARDUINO_SKETCHBOOK_DIR", tmp) - currSketchbookDir = tmp - - configFile := filepath.Join(currDataDir, "arduino-cli.yaml") - err := ioutil.WriteFile(configFile, []byte(` -board_manager: - additional_urls: - - http://www.invalid-domain-asjkdakdhadjkh.com/package_example_index.json -`), os.FileMode(0644)) - require.NoError(t, err, "writing dummy config "+configFile) - - err = ioutil.WriteFile(filepath.Join(currDataDir, "package_index.json"), []byte(`{ "packages": [] }`), os.FileMode(0644)) - require.NoError(t, err, "Writing empty json index file") - - err = ioutil.WriteFile(filepath.Join(currDataDir, "package_example_index.json"), []byte(`{ "packages": [] }`), os.FileMode(0644)) - require.NoError(t, err, "Writing empty json index file") - - err = ioutil.WriteFile(filepath.Join(currDataDir, "library_index.json"), []byte(`{ "libraries": [] }`), os.FileMode(0644)) - require.NoError(t, err, "Writing empty json index file") + configFile := filepath.Join("testdata", t.Name()) // Dump config with cmd-line specific file exitCode, d := executeWithArgs("--config-file", configFile, "config", "dump") @@ -398,19 +365,7 @@ board_manager: } func Test3rdPartyCoreIntegration(t *testing.T) { - // override SetUp dirs - tmp := tmpDirOrDie() - defer os.RemoveAll(tmp) - os.Setenv("ARDUINO_SKETCHBOOK_DIR", tmp) - currSketchbookDir = tmp - - configFile := filepath.Join(currDataDir, "arduino-cli.yaml") - err := ioutil.WriteFile(configFile, []byte(` -board_manager: - additional_urls: - - https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json -`), os.FileMode(0644)) - require.NoError(t, err, "writing dummy config "+configFile) + configFile := filepath.Join("testdata", t.Name()) // Update index and install esp32:esp32 exitCode, _ := executeWithArgs("--config-file", configFile, "core", "update-index") @@ -421,7 +376,7 @@ board_manager: // Build a simple sketch and check if all artifacts are copied tmpSketch := paths.New(currSketchbookDir).Join("Blink") - err = paths.New("testdata/Blink").CopyDirTo(tmpSketch) + err := paths.New("testdata/Blink").CopyDirTo(tmpSketch) require.NoError(t, err, "copying test sketch into temp dir") exitCode, d = executeWithArgs("--config-file", configFile, "compile", "-b", "esp32:esp32:esp32", tmpSketch.String()) require.Zero(t, exitCode) @@ -534,3 +489,27 @@ func TestCoreCommandsIntegration(t *testing.T) { require.Zero(t, exitCode) require.Contains(t, string(d), AVR+" uninstalled") } + +func TestSearchConfigTreeNotFound(t *testing.T) { + tmp := tmpDirOrDie() + require.Empty(t, searchConfigTree(tmp)) +} + +func TestSearchConfigTreeSameFolder(t *testing.T) { + tmp := tmpDirOrDie() + defer os.RemoveAll(tmp) + _, err := os.Create(filepath.Join(tmp, "arduino-cli.yaml")) + require.Nil(t, err) + require.Equal(t, searchConfigTree(tmp), tmp) +} + +func TestSearchConfigTreeInParent(t *testing.T) { + tmp := tmpDirOrDie() + defer os.RemoveAll(tmp) + target := filepath.Join(tmp, "foo", "bar") + err := os.MkdirAll(target, os.ModePerm) + require.Nil(t, err) + _, err = os.Create(filepath.Join(tmp, "arduino-cli.yaml")) + require.Nil(t, err) + require.Equal(t, searchConfigTree(target), tmp) +} diff --git a/cli/compile/compile.go b/cli/compile/compile.go index 1421a331056..f10d6d52f1e 100644 --- a/cli/compile/compile.go +++ b/cli/compile/compile.go @@ -22,7 +22,6 @@ import ( "os" "github.com/arduino/arduino-cli/cli/feedback" - "github.com/arduino/arduino-cli/cli/globals" "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/instance" @@ -32,6 +31,7 @@ import ( "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var ( @@ -107,7 +107,7 @@ func run(cmd *cobra.Command, args []string) { Quiet: quiet, VidPid: vidPid, ExportFile: exportFile, - }, os.Stdout, os.Stderr, globals.Config, globals.LogLevel == "debug") + }, os.Stdout, os.Stderr, viper.GetString("logging.level") == "debug") if err != nil { feedback.Errorf("Error during build: %v", err) diff --git a/cli/config/config.go b/cli/config/config.go index c7fea67adeb..d194747d941 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -1,19 +1,17 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ +// This file is part of arduino-cli. +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. package config diff --git a/cli/config/dump.go b/cli/config/dump.go index 8b8787c8192..f99886e77e8 100644 --- a/cli/config/dump.go +++ b/cli/config/dump.go @@ -1,54 +1,30 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ +// This file is part of arduino-cli. +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. package config import ( - "net/url" "os" - "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/feedback" - "github.com/arduino/arduino-cli/cli/globals" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/spf13/viper" + "gopkg.in/yaml.v2" ) -// FIXME: The way the Config objects is marshalled into JSON shouldn't be here, -// this is a temporary fix for the command `arduino-cli config dump --format json` -type jsonConfig struct { - ProxyType string `json:"proxy_type"` - ProxyManualConfig *jsonProxyConfig `json:"manual_configs,omitempty"` - SketchbookPath string `json:"sketchbook_path,omitempty"` - ArduinoDataDir string `json:"arduino_data,omitempty"` - ArduinoDownloadsDir string `json:"arduino_downloads_dir,omitempty"` - BoardsManager *jsonBoardsManagerConfig `json:"board_manager"` -} - -type jsonBoardsManagerConfig struct { - AdditionalURLS []*url.URL `json:"additional_urls,omitempty"` -} - -type jsonProxyConfig struct { - Hostname string `json:"hostname"` - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` // can be encrypted, see issue #71 -} - var dumpCmd = &cobra.Command{ Use: "dump", Short: "Prints the current configuration", @@ -61,59 +37,24 @@ var dumpCmd = &cobra.Command{ // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation type dumpResult struct { - structured *jsonConfig - plain string + data map[string]interface{} } func (dr dumpResult) Data() interface{} { - return dr.structured + return dr.data } func (dr dumpResult) String() string { - return dr.plain -} - -func runDumpCommand(cmd *cobra.Command, args []string) { - logrus.Info("Executing `arduino config dump`") - - data, err := globals.Config.SerializeToYAML() + bs, err := yaml.Marshal(dr.data) if err != nil { - feedback.Errorf("Error creating configuration: %v", err) - os.Exit(errorcodes.ErrGeneric) - } - - c := globals.Config - - sketchbookDir := "" - if c.SketchbookDir != nil { - sketchbookDir = c.SketchbookDir.String() - } - - arduinoDataDir := "" - if c.DataDir != nil { - arduinoDataDir = c.DataDir.String() + feedback.Errorf("unable to marshal config to YAML: %v", err) + return "" } - arduinoDownloadsDir := "" - if c.ArduinoDownloadsDir != nil { - arduinoDownloadsDir = c.ArduinoDownloadsDir.String() - } + return string(bs) +} - feedback.PrintResult(&dumpResult{ - structured: &jsonConfig{ - ProxyType: c.ProxyType, - ProxyManualConfig: &jsonProxyConfig{ - Hostname: c.ProxyHostname, - Username: c.ProxyUsername, - Password: c.ProxyPassword, - }, - SketchbookPath: sketchbookDir, - ArduinoDataDir: arduinoDataDir, - ArduinoDownloadsDir: arduinoDownloadsDir, - BoardsManager: &jsonBoardsManagerConfig{ - AdditionalURLS: c.BoardManagerAdditionalUrls, - }, - }, - plain: string(data), - }) +func runDumpCommand(cmd *cobra.Command, args []string) { + logrus.Info("Executing `arduino config dump`") + feedback.PrintResult(dumpResult{viper.AllSettings()}) } diff --git a/cli/config/init.go b/cli/config/init.go index d85eb42edb9..a7e298e8448 100644 --- a/cli/config/init.go +++ b/cli/config/init.go @@ -1,30 +1,29 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ +// This file is part of arduino-cli. +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. package config import ( "os" + "path/filepath" "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/feedback" - "github.com/arduino/arduino-cli/cli/globals" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/spf13/viper" ) func initInitCommand() *cobra.Command { @@ -50,20 +49,13 @@ var initFlags struct { func runInitCommand(cmd *cobra.Command, args []string) { logrus.Info("Executing `arduino config init`") - filepath := initFlags.location - if filepath == "" { - filepath = globals.Config.ConfigFile.String() - } - - if err := globals.Config.ConfigFile.Parent().MkdirAll(); err != nil { + configFile := filepath.Join(viper.GetString("directories.Data"), "arduino-cli.yaml") + err := viper.WriteConfigAs(configFile) + if err != nil { feedback.Errorf("Cannot create config file: %v", err) os.Exit(errorcodes.ErrGeneric) } - if err := globals.Config.SaveToYAML(filepath); err != nil { - feedback.Errorf("Cannot create config file: %v", err) - os.Exit(errorcodes.ErrGeneric) - } - feedback.Print("Config file PATH: " + filepath) + feedback.Print("Config file written: " + configFile) logrus.Info("Done") } diff --git a/cli/daemon/daemon.go b/cli/daemon/daemon.go index 643f20a47a4..838af39fbbd 100644 --- a/cli/daemon/daemon.go +++ b/cli/daemon/daemon.go @@ -68,7 +68,6 @@ func runDaemonCommand(cmd *cobra.Command, args []string) { srv_commands.RegisterArduinoCoreServer(s, &daemon.ArduinoCoreServerImpl{ DownloaderHeaders: headers, VersionString: globals.VersionInfo.VersionString, - Config: globals.Config, }) // register the monitors service diff --git a/cli/globals/configs.go b/cli/globals/configs.go deleted file mode 100644 index d0c961cb45a..00000000000 --- a/cli/globals/configs.go +++ /dev/null @@ -1,94 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to modify or -// otherwise use the software for commercial activities involving the Arduino -// software without disclosing the source code of your own applications. To purchase -// a commercial license, send an email to license@arduino.cc. - -package globals - -import ( - "fmt" - "os" - - "github.com/arduino/arduino-cli/cli/errorcodes" - "github.com/arduino/arduino-cli/cli/feedback" - "github.com/arduino/arduino-cli/configs" - "github.com/arduino/go-paths-helper" - "github.com/sirupsen/logrus" -) - -// InitConfigs initializes the configuration from the specified file. -func InitConfigs() { - // Start with default configuration - if conf, err := configs.NewConfiguration(); err != nil { - logrus.WithError(err).Error("Error creating default configuration") - feedback.Errorf("Error creating default configuration: %v", err) - os.Exit(errorcodes.ErrGeneric) - } else { - Config = conf - } - - // Read configuration from global config file - logrus.Info("Checking for config file in: " + Config.ConfigFile.String()) - if Config.ConfigFile.Exist() { - readConfigFrom(Config.ConfigFile) - } - - if Config.IsBundledInDesktopIDE() { - logrus.Info("CLI is bundled into the IDE") - err := Config.LoadFromDesktopIDEPreferences() - if err != nil { - logrus.WithError(err).Warn("Did not manage to get config file of IDE, using default configuration") - } - } else { - logrus.Info("CLI is not bundled into the IDE") - } - - // Read configuration from parent folders (project config) - if pwd, err := paths.Getwd(); err != nil { - logrus.WithError(err).Warn("Did not manage to find current path") - if path := paths.New("arduino-yaml"); path.Exist() { - readConfigFrom(path) - } - } else { - Config.Navigate(pwd) - } - - // Read configuration from old configuration file if found, but output a warning. - if old := paths.New(".cli-config.yml"); old.Exist() { - logrus.Errorf("Old configuration file detected: %s.", old) - logrus.Info("The name of this file has been changed to `arduino-cli.yaml`, please rename the file fix it.") - feedback.Error( - fmt.Errorf("WARNING: Old configuration file detected: %s", old), - "The name of this file has been changed to `arduino-yaml`, in a future release we will not support"+ - "the old name `.cli-config.yml` anymore. Please rename the file to `arduino-cli.yaml` to silence this warning.") - readConfigFrom(old) - } - - // Read configuration from environment vars - Config.LoadFromEnv() - - // Read configuration from user specified file - if YAMLConfigFile != "" { - Config.ConfigFile = paths.New(YAMLConfigFile) - readConfigFrom(Config.ConfigFile) - } - - logrus.Info("Configuration set") -} - -func readConfigFrom(path *paths.Path) { - logrus.Infof("Reading configuration from %s", path) - if err := Config.LoadFromYAML(path); err != nil { - logrus.WithError(err).Warnf("Could not read configuration from %s", path) - } -} diff --git a/cli/globals/globals.go b/cli/globals/globals.go index fd46e0a6f09..0fcf8b0b093 100644 --- a/cli/globals/globals.go +++ b/cli/globals/globals.go @@ -22,24 +22,14 @@ import ( "path/filepath" "runtime" - "github.com/arduino/arduino-cli/configs" "github.com/arduino/arduino-cli/version" ) var ( - // Debug determines whether to dump debug output to stderr or not - Debug bool // VersionInfo contains all info injected during build VersionInfo = version.NewInfo(filepath.Base(os.Args[0])) - // Config FIXMEDOC - Config *configs.Configuration - // YAMLConfigFile contains the path to the config file - YAMLConfigFile string - // AdditionalUrls contains the list of additional urls the boards manager can use - AdditionalUrls []string - // LogLevel is temporarily exported because the compile command will - // forward this information to the underlying legacy package - LogLevel string + // DefaultIndexURL is the default index url + DefaultIndexURL = "https://downloads.arduino.cc/packages/package_index.json" ) // NewHTTPClientHeader returns the http.Header object that must be used by the clients inside the downloaders diff --git a/cli/instance/instance.go b/cli/instance/instance.go index 037130624ed..bd4b68b7c02 100644 --- a/cli/instance/instance.go +++ b/cli/instance/instance.go @@ -11,6 +11,7 @@ import ( "github.com/arduino/arduino-cli/commands" rpc "github.com/arduino/arduino-cli/rpc/commands" "github.com/sirupsen/logrus" + "github.com/spf13/viper" ) // CreateInstaceIgnorePlatformIndexErrors creates and return an instance of the @@ -60,23 +61,17 @@ func initInstance() *rpc.InitResp { } func packageManagerInitReq() *rpc.InitReq { - urls := []string{} + urls := []string{globals.DefaultIndexURL} - for _, urlString := range globals.AdditionalUrls { - urls = append(urls, urlString) - } - - for _, URL := range globals.Config.BoardManagerAdditionalUrls { - urls = append(urls, URL.String()) + for _, URL := range viper.GetStringSlice("board_manager.additional_urls") { + urls = append(urls, URL) } conf := &rpc.Configuration{} - conf.DataDir = globals.Config.DataDir.String() - conf.DownloadsDir = globals.Config.DownloadsDir().String() + conf.DataDir = viper.GetString("directories.Data") + conf.DownloadsDir = viper.GetString("directories.Downloads") conf.BoardManagerAdditionalUrls = urls - if globals.Config.SketchbookDir != nil { - conf.SketchbookDir = globals.Config.SketchbookDir.String() - } + conf.SketchbookDir = viper.GetString("directories.SketchBook") return &rpc.InitReq{Configuration: conf} } diff --git a/cli/testdata/Test3rdPartyCoreIntegration/arduino-cli.yaml b/cli/testdata/Test3rdPartyCoreIntegration/arduino-cli.yaml new file mode 100644 index 00000000000..16c07d44a1e --- /dev/null +++ b/cli/testdata/Test3rdPartyCoreIntegration/arduino-cli.yaml @@ -0,0 +1,3 @@ +board_manager: + additional_urls: + - https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json \ No newline at end of file diff --git a/cli/testdata/TestInvalidCoreURLIntegration/arduino-cli.yaml b/cli/testdata/TestInvalidCoreURLIntegration/arduino-cli.yaml new file mode 100644 index 00000000000..1fbaa8b1c90 --- /dev/null +++ b/cli/testdata/TestInvalidCoreURLIntegration/arduino-cli.yaml @@ -0,0 +1,3 @@ +board_manager: + additional_urls: + - http://www.invalid-domain-asjkdakdhadjkh.com/package_example_index.json \ No newline at end of file diff --git a/commands/compile/compile.go b/commands/compile/compile.go index 05c9d273cd8..9d5cda7fc55 100644 --- a/commands/compile/compile.go +++ b/commands/compile/compile.go @@ -30,7 +30,7 @@ import ( "github.com/arduino/arduino-cli/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/arduino/sketches" "github.com/arduino/arduino-cli/commands" - "github.com/arduino/arduino-cli/configs" + "github.com/arduino/arduino-cli/configuration" "github.com/arduino/arduino-cli/legacy/builder" "github.com/arduino/arduino-cli/legacy/builder/i18n" "github.com/arduino/arduino-cli/legacy/builder/types" @@ -38,10 +38,11 @@ import ( paths "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" "github.com/sirupsen/logrus" + "github.com/spf13/viper" ) // Compile FIXMEDOC -func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.Writer, config *configs.Configuration, debug bool) (*rpc.CompileResp, error) { +func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.Writer, debug bool) (*rpc.CompileResp, error) { pm := commands.GetPackageManager(req.GetInstance().GetId()) if pm == nil { return nil, errors.New("invalid instance") @@ -88,20 +89,11 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W builderCtx.SketchLocation = sketch.FullPath // FIXME: This will be redundant when arduino-builder will be part of the cli - if packagesDir, err := config.HardwareDirectories(); err == nil { - builderCtx.HardwareDirs = packagesDir - } else { - return nil, fmt.Errorf("cannot get hardware directories: %s", err) - } - - if toolsDir, err := config.BundleToolsDirectories(); err == nil { - builderCtx.BuiltInToolsDirs = toolsDir - } else { - return nil, fmt.Errorf("cannot get bundled tools directories: %s", err) - } + builderCtx.HardwareDirs = configuration.HardwareDirectories() + builderCtx.BuiltInToolsDirs = configuration.BundleToolsDirectories() builderCtx.OtherLibrariesDirs = paths.NewPathList() - builderCtx.OtherLibrariesDirs.Add(config.LibrariesDir()) + builderCtx.OtherLibrariesDirs.Add(paths.New(viper.GetString("directories.Libraries"))) if req.GetBuildPath() != "" { builderCtx.BuildPath = paths.New(req.GetBuildPath()) @@ -140,7 +132,8 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W builderCtx.ArduinoAPIVersion = "10607" // Check if Arduino IDE is installed and get it's libraries location. - preferencesTxt := config.DataDir.Join("preferences.txt") + dataDir := paths.New(viper.GetString("directories.Data")) + preferencesTxt := dataDir.Join("preferences.txt") ideProperties, err := properties.LoadFromPath(preferencesTxt) if err == nil { lastIdeSubProperties := ideProperties.SubTree("last").SubTree("ide") diff --git a/commands/daemon/daemon.go b/commands/daemon/daemon.go index e7fea6183c6..e31d07e83d8 100644 --- a/commands/daemon/daemon.go +++ b/commands/daemon/daemon.go @@ -31,7 +31,6 @@ import ( "github.com/arduino/arduino-cli/commands/core" "github.com/arduino/arduino-cli/commands/lib" "github.com/arduino/arduino-cli/commands/upload" - "github.com/arduino/arduino-cli/configs" rpc "github.com/arduino/arduino-cli/rpc/commands" ) @@ -39,7 +38,6 @@ import ( type ArduinoCoreServerImpl struct { DownloaderHeaders http.Header VersionString string - Config *configs.Configuration } // BoardDetails FIXMEDOC @@ -133,7 +131,6 @@ func (s *ArduinoCoreServerImpl) Compile(req *rpc.CompileReq, stream rpc.ArduinoC stream.Context(), req, feedStream(func(data []byte) { stream.Send(&rpc.CompileResp{OutStream: data}) }), feedStream(func(data []byte) { stream.Send(&rpc.CompileResp{ErrStream: data}) }), - s.Config, false) // set debug to false if err != nil { return err diff --git a/commands/instances.go b/commands/instances.go index 3f6ef908e60..0ce25a15dd8 100644 --- a/commands/instances.go +++ b/commands/instances.go @@ -31,10 +31,12 @@ import ( "github.com/arduino/arduino-cli/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/arduino/libraries" "github.com/arduino/arduino-cli/arduino/libraries/librariesmanager" - "github.com/arduino/arduino-cli/configs" + "github.com/arduino/arduino-cli/cli/globals" + "github.com/arduino/arduino-cli/configuration" rpc "github.com/arduino/arduino-cli/rpc/commands" paths "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" + "github.com/spf13/viper" "go.bug.st/downloader" ) @@ -47,7 +49,6 @@ var instancesCount int32 = 1 // instantiate as many as needed by providing a different configuration // for each one. type CoreInstance struct { - config *configs.Configuration PackageManager *packagemanager.PackageManager lm *librariesmanager.LibrariesManager getLibOnly bool @@ -116,7 +117,7 @@ func (instance *CoreInstance) checkForBuiltinTools(downloadCB DownloadProgressCB } if ctagsInstalled || serialDiscoveryInstalled { - if err := instance.PackageManager.LoadHardware(instance.config); err != nil { + if err := instance.PackageManager.LoadHardware(); err != nil { return fmt.Errorf("could not load hardware packages: %s", err) } } @@ -130,29 +131,11 @@ func Init(ctx context.Context, req *rpc.InitReq, downloadCB DownloadProgressCB, return nil, fmt.Errorf("invalid request") } - config, err := configs.NewConfiguration() - if err != nil { - return nil, fmt.Errorf("getting default config values: %s", err) - } - config.DataDir = paths.New(inConfig.DataDir) - config.SketchbookDir = paths.New(inConfig.SketchbookDir) - if inConfig.DownloadsDir != "" { - config.ArduinoDownloadsDir = paths.New(inConfig.DownloadsDir) - } - for _, rawurl := range inConfig.BoardManagerAdditionalUrls { - if u, err := url.Parse(rawurl); err == nil { - config.BoardManagerAdditionalUrls = append(config.BoardManagerAdditionalUrls, u) - } else { - return nil, fmt.Errorf("parsing url %s: %s", rawurl, err) - } - } - - pm, lm, reqPltIndex, reqLibIndex, err := createInstance(ctx, config, req.GetLibraryManagerOnly()) + pm, lm, reqPltIndex, reqLibIndex, err := createInstance(ctx, req.GetLibraryManagerOnly()) if err != nil { return nil, fmt.Errorf("cannot initialize package manager: %s", err) } instance := &CoreInstance{ - config: config, PackageManager: pm, lm: lm, getLibOnly: req.GetLibraryManagerOnly()} @@ -207,13 +190,21 @@ func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexReq, // UpdateIndex FIXMEDOC func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexReq, downloadCB DownloadProgressCB) (*rpc.UpdateIndexResp, error) { id := req.GetInstance().GetId() - coreInstance, ok := instances[id] + _, ok := instances[id] if !ok { return nil, fmt.Errorf("invalid handle") } - indexpath := coreInstance.config.IndexesDir() - for _, URL := range coreInstance.config.BoardManagerAdditionalUrls { + indexpath := paths.New(viper.GetString("directories.Data")) + urls := []string{globals.DefaultIndexURL} + urls = append(urls, viper.GetStringSlice("board_manager.additional_urls")...) + for _, u := range urls { + URL, err := url.Parse(u) + if err != nil { + logrus.Warnf("unable to parse additional URL: %s", u) + continue + } + logrus.WithField("url", URL).Print("Updating index") tmpFile, err := ioutil.TempFile("", "") @@ -261,7 +252,7 @@ func Rescan(instanceID int32) (*rpc.RescanResp, error) { return nil, fmt.Errorf("invalid handle") } - pm, lm, reqPltIndex, reqLibIndex, err := createInstance(context.Background(), coreInstance.config, coreInstance.getLibOnly) + pm, lm, reqPltIndex, reqLibIndex, err := createInstance(context.Background(), coreInstance.getLibOnly) if err != nil { return nil, fmt.Errorf("rescanning filesystem: %s", err) } @@ -274,24 +265,33 @@ func Rescan(instanceID int32) (*rpc.RescanResp, error) { }, nil } -func createInstance(ctx context.Context, config *configs.Configuration, getLibOnly bool) ( +func createInstance(ctx context.Context, getLibOnly bool) ( *packagemanager.PackageManager, *librariesmanager.LibrariesManager, []string, string, error) { var pm *packagemanager.PackageManager platformIndexErrors := []string{} + dataDir := paths.New(viper.GetString("directories.Data")) + downloadsDir := paths.New(viper.GetString("directories.Downloads")) if !getLibOnly { pm = packagemanager.NewPackageManager( - config.IndexesDir(), - config.PackagesDir(), - config.DownloadsDir(), - config.DataDir.Join("tmp")) - - for _, URL := range config.BoardManagerAdditionalUrls { + dataDir, + paths.New(viper.GetString("directories.Packages")), + downloadsDir, + dataDir.Join("tmp")) + + urls := []string{globals.DefaultIndexURL} + urls = append(urls, viper.GetStringSlice("board_manager.additional_urls")...) + for _, u := range urls { + URL, err := url.Parse(u) + if err != nil { + logrus.Warnf("unable to parse additional URL: %s", u) + continue + } if err := pm.LoadPackageIndex(URL); err != nil { platformIndexErrors = append(platformIndexErrors, err.Error()) } } - if err := pm.LoadHardware(config); err != nil { + if err := pm.LoadHardware(); err != nil { return nil, nil, nil, "", fmt.Errorf("loading hardware packages: %s", err) } } @@ -301,17 +301,16 @@ func createInstance(ctx context.Context, config *configs.Configuration, getLibOn // Initialize library manager // -------------------------- - lm := librariesmanager.NewLibraryManager( - config.IndexesDir(), - config.DownloadsDir()) + lm := librariesmanager.NewLibraryManager(dataDir, downloadsDir) // Add IDE builtin libraries dir - if bundledLibsDir := config.IDEBundledLibrariesDir(); bundledLibsDir != nil { + if bundledLibsDir := configuration.IDEBundledLibrariesDir(); bundledLibsDir != nil { lm.AddLibrariesDir(bundledLibsDir, libraries.IDEBuiltIn) } // Add sketchbook libraries dir - lm.AddLibrariesDir(config.LibrariesDir(), libraries.Sketchbook) + libDir := paths.New(viper.GetString("directories.Libraries")) + lm.AddLibrariesDir(libDir, libraries.Sketchbook) // Add libraries dirs from installed platforms if pm != nil { diff --git a/configs/configuration.go b/configs/configuration.go deleted file mode 100644 index c1ce4c82ae9..00000000000 --- a/configs/configuration.go +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ - -// Package configs contains all CLI configurations handling. -package configs - -import ( - "fmt" - "net/url" - - paths "github.com/arduino/go-paths-helper" -) - -// Configuration contains a running configuration -type Configuration struct { - // ConfigFilePath represents the default location of the config file (same directory as executable). - ConfigFile *paths.Path - - // DataDir represents the current root of the arduino tree (defaulted to `$HOME/.arduino15` on linux). - DataDir *paths.Path - - // SketchbookDir represents the current root of the sketchbooks tree (defaulted to `$HOME/Arduino`). - SketchbookDir *paths.Path - - // ArduinoIDEDirectory is the directory of the Arduino IDE if the CLI runs together with it. - ArduinoIDEDirectory *paths.Path - - // IsPortable is set to true if the cli lives in IDE directory and the IDE is portable - IsPortable bool - - // ArduinoDownloadsDir overrides the default directory where the package files are downloaded and cached. - // Use DownloadsDir() method to retrieve it. - ArduinoDownloadsDir *paths.Path - - // IDEBundledCheckResult contains the result of the check to see if the CLI is bundled with the IDE: - // the field is true if the CLI is bundled with the Arduino IDE, false if the CLI is running - // standalone or nil if the detection has not been performed. - IDEBundledCheckResult *bool - - // BoardManagerAdditionalUrls contains the additional URL for 3rd party packages - BoardManagerAdditionalUrls []*url.URL - - // ProxyType is the type of proxy configured - ProxyType string - - // ProxyHostname is the proxy hostname - ProxyHostname string - - // ProxyUsername is the proxy user - ProxyUsername string - - // ProxyPassword is the proxy password - ProxyPassword string -} - -var defaultPackageIndexURL, _ = url.Parse("https://downloads.arduino.cc/packages/package_index.json") - -// NewConfiguration returns a new Configuration with the default values -func NewConfiguration() (*Configuration, error) { - dataDir, err := getDefaultArduinoDataDir() - if err != nil { - return nil, fmt.Errorf("getting default arduino data dir: %s", err) - } - sketchbookDir, err := getDefaultSketchbookDir() - if err != nil { - return nil, fmt.Errorf("getting default sketchbook dir: %s", err) - } - - return &Configuration{ - ConfigFile: getDefaultConfigFilePath(), - DataDir: dataDir, - SketchbookDir: sketchbookDir, - BoardManagerAdditionalUrls: []*url.URL{defaultPackageIndexURL}, - ProxyType: "auto", - }, nil -} - -// LibrariesDir returns the directory for installed libraries. -func (config *Configuration) LibrariesDir() *paths.Path { - return config.SketchbookDir.Join("libraries") -} - -// PackagesDir return the directory for installed packages. -func (config *Configuration) PackagesDir() *paths.Path { - return config.DataDir.Join("packages") -} - -// DownloadsDir returns the directory for archive downloads. -func (config *Configuration) DownloadsDir() *paths.Path { - if config.ArduinoDownloadsDir != nil { - return config.ArduinoDownloadsDir - } - return config.DataDir.Join("staging") -} - -// IndexesDir returns the directory for the indexes -func (config *Configuration) IndexesDir() *paths.Path { - return config.DataDir -} diff --git a/configs/directories.go b/configs/directories.go deleted file mode 100644 index 60c47bd3308..00000000000 --- a/configs/directories.go +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ - -package configs - -import ( - "fmt" - "os" - "runtime" - - "github.com/arduino/go-paths-helper" - "github.com/arduino/go-win32-utils" -) - -// getDefaultConfigFilePath returns the default path for arduino-cli.yaml -func getDefaultConfigFilePath() *paths.Path { - arduinoDataDir, err := getDefaultArduinoDataDir() - if err != nil { - panic(err) - } - return arduinoDataDir.Join("arduino-cli.yaml") -} - -func getDefaultArduinoDataDir() (*paths.Path, error) { - userHomeDir, err := os.UserHomeDir() - if err != nil { - return nil, err - } - switch runtime.GOOS { - case "linux": - return paths.New(userHomeDir).Join(".arduino15"), nil - case "darwin": - return paths.New(userHomeDir).Join("Library", "Arduino15"), nil - case "windows": - localAppDataPath, err := win32.GetLocalAppDataFolder() - if err != nil { - return nil, fmt.Errorf("getting LocalAppData path: %s", err) - } - return paths.New(localAppDataPath).Join("Arduino15"), nil - default: - return nil, fmt.Errorf("unsupported OS: %s", runtime.GOOS) - } -} - -func getDefaultSketchbookDir() (*paths.Path, error) { - userHomeDir, err := os.UserHomeDir() - if err != nil { - return nil, err - } - switch runtime.GOOS { - case "linux": - return paths.New(userHomeDir).Join("Arduino"), nil - case "darwin": - return paths.New(userHomeDir).Join("Documents", "Arduino"), nil - case "windows": - documentsPath, err := win32.GetDocumentsFolder() - if err != nil { - return nil, fmt.Errorf("getting Documents path: %s", err) - } - return paths.New(documentsPath).Join("Arduino"), nil - default: - return nil, fmt.Errorf("unsupported OS: %s", runtime.GOOS) - } -} diff --git a/configs/env_vars_serializer.go b/configs/env_vars_serializer.go deleted file mode 100644 index de8227eea46..00000000000 --- a/configs/env_vars_serializer.go +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ - -package configs - -import ( - "os" - - paths "github.com/arduino/go-paths-helper" -) - -// LoadFromEnv read configurations from the environment variables -func (config *Configuration) LoadFromEnv() { - if p, has := os.LookupEnv("PROXY_TYPE"); has { - config.ProxyType = p - } - if dir, has := os.LookupEnv("ARDUINO_SKETCHBOOK_DIR"); has { - config.SketchbookDir = paths.New(dir) - } - if dir, has := os.LookupEnv("ARDUINO_DATA_DIR"); has { - config.DataDir = paths.New(dir) - } - if dir, has := os.LookupEnv("ARDUINO_DOWNLOADS_DIR"); has { - config.ArduinoDownloadsDir = paths.New(dir) - } -} diff --git a/configs/hardware_directories.go b/configs/hardware_directories.go deleted file mode 100644 index 96ce3d207b1..00000000000 --- a/configs/hardware_directories.go +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ - -package configs - -import ( - "github.com/arduino/go-paths-helper" -) - -// HardwareDirectories returns all paths that may contains hardware packages. -func (config *Configuration) HardwareDirectories() (paths.PathList, error) { - res := paths.PathList{} - - if config.IsBundledInDesktopIDE() { - bundledHardwareDir := config.ArduinoIDEDirectory.Join("hardware") - if bundledHardwareDir.IsDir() { - res.Add(bundledHardwareDir) - } - } - - if dir := config.PackagesDir(); dir.IsDir() { - res.Add(dir) - } - - if config.SketchbookDir != nil { - if dir := config.SketchbookDir.Join("hardware"); dir.IsDir() { - res.Add(dir) - } - } - - return res, nil -} - -// BundleToolsDirectories returns all paths that may contains bundled-tools. -func (config *Configuration) BundleToolsDirectories() (paths.PathList, error) { - res := paths.PathList{} - - if config.IsBundledInDesktopIDE() { - bundledToolsDir := config.ArduinoIDEDirectory.Join("hardware", "tools") - if bundledToolsDir.IsDir() { - res = append(res, bundledToolsDir) - } - } - - return res, nil -} diff --git a/configs/navigate.go b/configs/navigate.go deleted file mode 100644 index 9a4d5f5b17d..00000000000 --- a/configs/navigate.go +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ - -package configs - -import ( - paths "github.com/arduino/go-paths-helper" - "github.com/sirupsen/logrus" -) - -// Navigate FIXMEDOC -func (c *Configuration) Navigate(pwd *paths.Path) { - parents := pwd.Clean().Parents() - - // From the root to the current folder, search for arduino-cli.yaml files - for i := range parents { - path := parents[len(parents)-i-1].Join("arduino-cli.yaml") - logrus.Info("Checking for config in: " + path.String()) - if err := c.LoadFromYAML(path); err != nil { - logrus.WithError(err).Infof("error loading") - } - } -} diff --git a/configs/navigate_test.go b/configs/navigate_test.go deleted file mode 100644 index 817885e7566..00000000000 --- a/configs/navigate_test.go +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ - -package configs_test - -import ( - "io/ioutil" - "path/filepath" - "runtime" - "strings" - "testing" - - "github.com/arduino/arduino-cli/configs" - paths "github.com/arduino/go-paths-helper" - homedir "github.com/mitchellh/go-homedir" - "github.com/sergi/go-diff/diffmatchpatch" -) - -func TestNavigate(t *testing.T) { - if runtime.GOOS != "linux" { - t.Skip("Test only runs on Linux") - } - - tests := []string{ - "noconfig", - "local", - "inheritance", - } - for _, tt := range tests { - _tt := tt - t.Run(_tt, func(t *testing.T) { - pwd := paths.New("testdata", "navigate", _tt, "first", "second") - golden := filepath.Join("testdata", "navigate", _tt, "golden.yaml") - - config, _ := configs.NewConfiguration() - - config.Navigate(pwd) - data, _ := config.SerializeToYAML() - - diff(t, data, golden) - }) - } -} - -func diff(t *testing.T, data []byte, goldenFile string) { - golden, err := ioutil.ReadFile(goldenFile) - if err != nil { - t.Error(err) - return - } - - dataStr := strings.TrimSpace(string(data)) - goldenStr := strings.TrimSpace(string(golden)) - - // Substitute home folder - homedir, _ := homedir.Dir() - dataStr = strings.Replace(dataStr, homedir, "$HOME", -1) - - if dataStr != goldenStr { - dmp := diffmatchpatch.New() - diffs := dmp.DiffMain(goldenStr, dataStr, false) - t.Errorf(dmp.DiffPrettyText(diffs)) - } -} diff --git a/configs/preferences_txt_serializer.go b/configs/preferences_txt_serializer.go deleted file mode 100644 index d8242b29a2a..00000000000 --- a/configs/preferences_txt_serializer.go +++ /dev/null @@ -1,147 +0,0 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ - -package configs - -import ( - "errors" - "net/url" - "os" - "strings" - - "github.com/arduino/go-paths-helper" - - "github.com/arduino/go-properties-orderedmap" - "github.com/sirupsen/logrus" -) - -// IsBundledInDesktopIDE returns true if the CLI is bundled with the Arduino IDE. -func (config *Configuration) IsBundledInDesktopIDE() bool { - if config.IDEBundledCheckResult != nil { - return *config.IDEBundledCheckResult - } - - res := false - config.IDEBundledCheckResult = &res - - logrus.Info("Checking if CLI is Bundled into the IDE") - executable, err := os.Executable() - if err != nil { - logrus.WithError(err).Warn("Cannot get executable path") - return false - } - executablePath := paths.New(executable) - if err := executablePath.FollowSymLink(); err != nil { - logrus.WithError(err).Warn("Cannot get executable path") - return false - } - ideDir := executablePath.Parent() - logrus.Info("Candidate IDE Directory: ", ideDir) - - tests := []string{ - "tools-builder", - "examples/01.Basics/Blink", - } - for _, test := range tests { - if !ideDir.Join(test).Exist() { - return false - } - } - - portable := "portable" - if ideDir.Join(portable).Exist() { - logrus.Info("IDE is portable") - config.IsPortable = true - } - - config.ArduinoIDEDirectory = ideDir - res = true - return true -} - -// LoadFromDesktopIDEPreferences loads the config from the Desktop IDE preferences.txt file -func (config *Configuration) LoadFromDesktopIDEPreferences() error { - logrus.Info("Unserializing from IDE preferences") - if config.IsPortable { - config.DataDir = config.ArduinoIDEDirectory.Join("portable") - config.SketchbookDir = config.ArduinoIDEDirectory.Join("portable").Join("sketchbook") - } - preferenceTxtPath := config.DataDir.Join("preferences.txt") - props, err := properties.LoadFromPath(preferenceTxtPath) - if err != nil { - logrus.WithError(err).Warn("Error during unserialize from IDE preferences") - return err - } - err = config.proxyConfigsFromIDEPrefs(props) - if err != nil { - logrus.WithError(err).Warn("Error loading proxy settings from IDE preferences") - } - if dir, has := props.GetOk("sketchbook.path"); has { - config.SketchbookDir = paths.New(dir) - } - if URLs, has := props.GetOk("boardsmanager.additional.urls"); has { - for _, URL := range strings.Split(URLs, ",") { - if newURL, err := url.Parse(URL); err == nil { - config.BoardManagerAdditionalUrls = append(config.BoardManagerAdditionalUrls, newURL) - } - } - } - return nil -} - -func (config *Configuration) proxyConfigsFromIDEPrefs(props *properties.Map) error { - proxy := props.SubTree("proxy") - switch proxy.Get("type") { - case "auto": - // Automatic proxy - break - case "manual": - // Manual proxy configuration - manualConfig := proxy.SubTree("manual") - hostname, exists := manualConfig.GetOk("hostname") - if !exists { - return errors.New("proxy hostname not found in preferences.txt") - } - username := manualConfig.Get("username") - password := manualConfig.Get("password") - - config.ProxyType = "manual" - config.ProxyHostname = hostname - config.ProxyUsername = username - config.ProxyPassword = password - break - case "none": - // No proxy - break - default: - return errors.New("unsupported proxy config") - } - return nil -} - -// IDEBundledLibrariesDir returns the libraries directory bundled in -// the Arduino IDE. If there is no Arduino IDE or the directory doesn't -// exists then nil is returned -func (config *Configuration) IDEBundledLibrariesDir() *paths.Path { - if config.IsBundledInDesktopIDE() { - libDir := config.ArduinoIDEDirectory.Join("libraries") - if libDir.IsDir() { - return libDir - } - } - return nil -} diff --git a/configs/testdata/navigate/inheritance/first/arduino-cli.yaml b/configs/testdata/navigate/inheritance/first/arduino-cli.yaml deleted file mode 100644 index 82fd1a30bf6..00000000000 --- a/configs/testdata/navigate/inheritance/first/arduino-cli.yaml +++ /dev/null @@ -1,5 +0,0 @@ -sketchbook_path: /tmp -board_manager: - additional_urls: - - https://downloads.arduino.cc/package_index_mraa.json - \ No newline at end of file diff --git a/configs/testdata/navigate/inheritance/first/second/.gitkeep b/configs/testdata/navigate/inheritance/first/second/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/configs/testdata/navigate/inheritance/first/second/arduino-cli.yaml b/configs/testdata/navigate/inheritance/first/second/arduino-cli.yaml deleted file mode 100644 index d122d502b1f..00000000000 --- a/configs/testdata/navigate/inheritance/first/second/arduino-cli.yaml +++ /dev/null @@ -1,4 +0,0 @@ -board_manager: - additional_urls: - - https://downloads.arduino.cc/package_index_mraa.json - \ No newline at end of file diff --git a/configs/testdata/navigate/inheritance/golden.yaml b/configs/testdata/navigate/inheritance/golden.yaml deleted file mode 100644 index d960a3ea97f..00000000000 --- a/configs/testdata/navigate/inheritance/golden.yaml +++ /dev/null @@ -1,6 +0,0 @@ -proxy_type: auto -sketchbook_path: /tmp -arduino_data: $HOME/.arduino15 -board_manager: - additional_urls: - - https://downloads.arduino.cc/package_index_mraa.json \ No newline at end of file diff --git a/configs/testdata/navigate/local/first/second/.gitkeep b/configs/testdata/navigate/local/first/second/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/configs/testdata/navigate/local/first/second/arduino-cli.yaml b/configs/testdata/navigate/local/first/second/arduino-cli.yaml deleted file mode 100644 index d122d502b1f..00000000000 --- a/configs/testdata/navigate/local/first/second/arduino-cli.yaml +++ /dev/null @@ -1,4 +0,0 @@ -board_manager: - additional_urls: - - https://downloads.arduino.cc/package_index_mraa.json - \ No newline at end of file diff --git a/configs/testdata/navigate/local/golden.yaml b/configs/testdata/navigate/local/golden.yaml deleted file mode 100644 index 8f29908b0a8..00000000000 --- a/configs/testdata/navigate/local/golden.yaml +++ /dev/null @@ -1,6 +0,0 @@ -proxy_type: auto -sketchbook_path: $HOME/Arduino -arduino_data: $HOME/.arduino15 -board_manager: - additional_urls: - - https://downloads.arduino.cc/package_index_mraa.json \ No newline at end of file diff --git a/configs/testdata/navigate/noconfig/first/second/.gitkeep b/configs/testdata/navigate/noconfig/first/second/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/configs/testdata/navigate/noconfig/golden.yaml b/configs/testdata/navigate/noconfig/golden.yaml deleted file mode 100644 index cbcb7e96dcb..00000000000 --- a/configs/testdata/navigate/noconfig/golden.yaml +++ /dev/null @@ -1,4 +0,0 @@ -proxy_type: auto -sketchbook_path: $HOME/Arduino -arduino_data: $HOME/.arduino15 -board_manager: {} \ No newline at end of file diff --git a/configs/yaml_serializer.go b/configs/yaml_serializer.go deleted file mode 100644 index 173af607bf5..00000000000 --- a/configs/yaml_serializer.go +++ /dev/null @@ -1,144 +0,0 @@ -/* - * This file is part of arduino-cli. - * - * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) - * - * This software is released under the GNU General Public License version 3, - * which covers the main part of arduino-cli. - * The terms of this license can be found at: - * https://www.gnu.org/licenses/gpl-3.0.en.html - * - * You can be released from the requirements of the above licenses by purchasing - * a commercial license. Buying such a license is mandatory if you want to modify or - * otherwise use the software for commercial activities involving the Arduino - * software without disclosing the source code of your own applications. To purchase - * a commercial license, send an email to license@arduino.cc. - */ - -package configs - -import ( - "fmt" - "io/ioutil" - "net/url" - - paths "github.com/arduino/go-paths-helper" - yaml "gopkg.in/yaml.v2" -) - -type yamlConfig struct { - ProxyType string `yaml:"proxy_type"` - ProxyManualConfig *yamlProxyConfig `yaml:"manual_configs,omitempty"` - SketchbookPath string `yaml:"sketchbook_path,omitempty"` - ArduinoDataDir string `yaml:"arduino_data,omitempty"` - ArduinoDownloadsDir string `yaml:"arduino_downloads_dir,omitempty"` - BoardsManager *yamlBoardsManagerConfig `yaml:"board_manager"` -} - -type yamlBoardsManagerConfig struct { - AdditionalURLS []string `yaml:"additional_urls,omitempty"` -} - -type yamlProxyConfig struct { - Hostname string `yaml:"hostname"` - Username string `yaml:"username,omitempty"` - Password string `yaml:"password,omitempty"` // can be encrypted, see issue #71 -} - -// LoadFromYAML loads the configs from a yaml file. -func (config *Configuration) LoadFromYAML(path *paths.Path) error { - content, err := path.ReadFile() - if err != nil { - return err - } - var ret yamlConfig - err = yaml.Unmarshal(content, &ret) - if err != nil { - return err - } - - if ret.ArduinoDataDir != "" { - config.DataDir = paths.New(ret.ArduinoDataDir) - } - if ret.SketchbookPath != "" { - config.SketchbookDir = paths.New(ret.SketchbookPath) - } - if ret.ArduinoDownloadsDir != "" { - config.ArduinoDownloadsDir = paths.New(ret.ArduinoDownloadsDir) - } else { - config.ArduinoDownloadsDir = nil - } - if ret.ProxyType != "" { - config.ProxyType = ret.ProxyType - if ret.ProxyManualConfig != nil { - config.ProxyHostname = ret.ProxyManualConfig.Hostname - config.ProxyUsername = ret.ProxyManualConfig.Username - config.ProxyPassword = ret.ProxyManualConfig.Password - } - } - if ret.BoardsManager != nil { - if len(config.BoardManagerAdditionalUrls) > 1 { - config.BoardManagerAdditionalUrls = config.BoardManagerAdditionalUrls[:1] - } - for _, rawurl := range ret.BoardsManager.AdditionalURLS { - url, err := url.Parse(rawurl) - if err != nil { - continue - } - config.BoardManagerAdditionalUrls = append(config.BoardManagerAdditionalUrls, url) - } - } - - return nil -} - -// SerializeToYAML encodes the current configuration as YAML -func (config *Configuration) SerializeToYAML() ([]byte, error) { - c := &yamlConfig{} - if config.SketchbookDir != nil { - c.SketchbookPath = config.SketchbookDir.String() - } - if config.DataDir != nil { - c.ArduinoDataDir = config.DataDir.String() - } - if config.ArduinoDownloadsDir != nil { - c.ArduinoDownloadsDir = config.ArduinoDownloadsDir.String() - } - c.ProxyType = config.ProxyType - if config.ProxyType == "manual" { - c.ProxyManualConfig = &yamlProxyConfig{ - Hostname: config.ProxyHostname, - Username: config.ProxyUsername, - Password: config.ProxyPassword, - } - } - c.BoardsManager = &yamlBoardsManagerConfig{AdditionalURLS: []string{}} - if len(config.BoardManagerAdditionalUrls) > 1 { - for _, URL := range config.BoardManagerAdditionalUrls[1:] { - c.BoardsManager.AdditionalURLS = appendIfMissing(c.BoardsManager.AdditionalURLS, URL.String()) - } - } - return yaml.Marshal(c) -} - -// SaveToYAML the current configuration to a YAML file -func (config *Configuration) SaveToYAML(path string) error { - content, err := config.SerializeToYAML() - if err != nil { - return fmt.Errorf("econding configuration to YAML: %s", err) - } - - if err = ioutil.WriteFile(path, content, 0666); err != nil { - return fmt.Errorf("writing configuration to %s: %s", path, err) - } - return nil -} - -func appendIfMissing(slice []string, i string) []string { - for _, ele := range slice { - if ele == i { - return slice - } - } - return append(slice, i) -} diff --git a/configuration/configuration.go b/configuration/configuration.go new file mode 100644 index 00000000000..23b1bb4c139 --- /dev/null +++ b/configuration/configuration.go @@ -0,0 +1,179 @@ +// This file is part of arduino-cli. +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +package configuration + +import ( + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/go-win32-utils" + "github.com/sirupsen/logrus" + jww "github.com/spf13/jwalterweatherman" + "github.com/spf13/viper" +) + +// 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(configPath string) { + // Config file metadata + jww.SetStdoutThreshold(jww.LevelFatal) + viper.SetConfigName("arduino-cli") + + // Get default data path if none was provided + if configPath == "" { + configPath = getDefaultArduinoDataDir() + } + + // Add paths where to search for a config file + viper.AddConfigPath(configPath) + + // Bind env vars + viper.SetEnvPrefix("ARDUINO") + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.AutomaticEnv() + + // Bind env aliases to keep backward compatibility + viper.BindEnv("directories.Sketchbook", "ARDUINO_SKETCHBOOK_DIR") + viper.BindEnv("directories.Downloads", "ARDUINO_DOWNLOADS_DIR") + viper.BindEnv("directories.Data", "ARDUINO_DATA_DIR") + + // Early access directories.Data and directories.Sketchbook in case + // those were set through env vars or cli flags + dataDir := viper.GetString("directories.Data") + if dataDir == "" { + dataDir = getDefaultArduinoDataDir() + } + sketchbookDir := viper.GetString("directories.Sketchbook") + if sketchbookDir == "" { + sketchbookDir = getDefaultSketchbookDir() + } + + // Set default values for all the settings + setDefaults(dataDir, sketchbookDir) + + // Attempt to read config file + if err := viper.ReadInConfig(); err != nil { + // ConfigFileNotFoundError is acceptable, anything else + // should be reported to the user + if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + feedback.Errorf("Error reading config file: %v", err) + } + } +} + +// getDefaultArduinoDataDir returns the full path to the default arduino folder +func getDefaultArduinoDataDir() string { + userHomeDir, err := os.UserHomeDir() + if err != nil { + feedback.Errorf("Unable to get user home dir: %v", err) + return "." + } + + switch runtime.GOOS { + case "linux": + return filepath.Join(userHomeDir, ".arduino15") + case "darwin": + return filepath.Join(userHomeDir, "Library", "Arduino15") + case "windows": + localAppDataPath, err := win32.GetLocalAppDataFolder() + if err != nil { + feedback.Errorf("Unable to get Local App Data Folder: %v", err) + return "." + } + return filepath.Join(localAppDataPath, "Arduino15") + default: + return "." + } +} + +// getDefaultSketchbookDir returns the full path to the default sketchbook folder +func getDefaultSketchbookDir() string { + userHomeDir, err := os.UserHomeDir() + if err != nil { + feedback.Errorf("Unable to get user home dir: %v", err) + return "." + } + + switch runtime.GOOS { + case "linux": + return filepath.Join(userHomeDir, "Arduino") + case "darwin": + return filepath.Join(userHomeDir, "Documents", "Arduino") + case "windows": + documentsPath, err := win32.GetDocumentsFolder() + if err != nil { + feedback.Errorf("Unable to get Documents Folder: %v", err) + return "." + } + return filepath.Join(documentsPath, "Arduino") + default: + return "." + } +} + +// IsBundledInDesktopIDE returns true if the CLI is bundled with the Arduino IDE. +func IsBundledInDesktopIDE() bool { + // value is cached the first time we run the check + if viper.IsSet("IDE.Bundled") { + return viper.GetBool("IDE.Bundled") + } + + viper.Set("IDE.Bundled", false) + viper.Set("IDE.Portable", false) + + logrus.Info("Checking if CLI is Bundled into the IDE") + executable, err := os.Executable() + if err != nil { + feedback.Errorf("Cannot get executable path: %v", err) + return viper.GetBool("IDE.Bundled") + } + + executablePath, err := filepath.EvalSymlinks(executable) + if err != nil { + feedback.Errorf("Cannot get executable path: %v", err) + return viper.GetBool("IDE.Bundled") + } + + ideDir := filepath.Dir(executablePath) + + // To determine if the CLI is bundled with an IDE, We check an arbitrary + // number of folders that are part of the IDE install tree + tests := []string{ + "tools-builder", + "examples/01.Basics/Blink", + } + + for _, test := range tests { + if _, err := os.Stat(filepath.Join(ideDir, test)); err != nil { + // the test folder doesn't exist or is not accessible + return viper.GetBool("IDE.Bundled") + } + } + + // Check whether this is a portable install + if _, err := os.Stat(filepath.Join(ideDir, "portable")); err != nil { + viper.Set("IDE.Portable", true) + } + + // Persist IDE-related config settings and return true + viper.Set("IDE.Bundled", false) + viper.Set("IDE.Directory", ideDir) + return true +} diff --git a/configuration/defaults.go b/configuration/defaults.go new file mode 100644 index 00000000000..bdae807fa1a --- /dev/null +++ b/configuration/defaults.go @@ -0,0 +1,37 @@ +// This file is part of arduino-cli. +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +package configuration + +import ( + "path/filepath" + + "github.com/spf13/viper" +) + +func setDefaults(dataDir, sketchBookDir string) { + // logging + viper.SetDefault("logging.level", "info") + viper.SetDefault("logging.format", "text") + // board manager + viper.SetDefault("board_manager.additional_urls", []string{}) + + // arduino directories + viper.SetDefault("directories.Data", dataDir) + viper.SetDefault("directories.Downloads", filepath.Join(dataDir, "staging")) + viper.SetDefault("directories.Packages", filepath.Join(dataDir, "packages")) + viper.SetDefault("directories.SketchBook", sketchBookDir) + viper.SetDefault("directories.Libraries", filepath.Join(sketchBookDir, "libraries")) +} diff --git a/configuration/directories.go b/configuration/directories.go new file mode 100644 index 00000000000..72c6bec5fdc --- /dev/null +++ b/configuration/directories.go @@ -0,0 +1,78 @@ +// This file is part of arduino-cli. +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +package configuration + +import ( + "github.com/arduino/go-paths-helper" + "github.com/spf13/viper" +) + +// HardwareDirectories returns all paths that may contains hardware packages. +func HardwareDirectories() paths.PathList { + res := paths.PathList{} + + if IsBundledInDesktopIDE() { + ideDir := paths.New(viper.GetString("IDE.Directory")) + bundledHardwareDir := ideDir.Join("hardware") + if bundledHardwareDir.IsDir() { + res.Add(bundledHardwareDir) + } + } + + if viper.IsSet("directories.Packages") { + res.Add(paths.New(viper.GetString("directories.Packages"))) + } + + if viper.IsSet("directories.Sketchbook") { + skDir := paths.New(viper.GetString("directories.Sketchbook")) + hwDir := skDir.Join("hardware") + if hwDir.IsDir() { + res.Add(hwDir) + } + } + + return res +} + +// BundleToolsDirectories returns all paths that may contains bundled-tools. +func BundleToolsDirectories() paths.PathList { + res := paths.PathList{} + + if IsBundledInDesktopIDE() { + ideDir := paths.New(viper.GetString("IDE.Directory")) + bundledToolsDir := ideDir.Join("hardware", "tools") + if bundledToolsDir.IsDir() { + res = append(res, bundledToolsDir) + } + } + + return res +} + +// IDEBundledLibrariesDir returns the libraries directory bundled in +// the Arduino IDE. If there is no Arduino IDE or the directory doesn't +// exists then nil is returned +func IDEBundledLibrariesDir() *paths.Path { + if IsBundledInDesktopIDE() { + ideDir := paths.New(viper.GetString("IDE.Directory")) + libDir := ideDir.Join("libraries") + if libDir.IsDir() { + return libDir + } + } + + return nil +} diff --git a/go.mod b/go.mod index d62b0a23f79..3dbb38d7db1 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,9 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect github.com/spf13/cobra v0.0.5 + github.com/spf13/jwalterweatherman v1.0.0 + github.com/spf13/viper v1.3.2 + github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/testify v1.4.0 go.bug.st/cleanup v1.0.0 go.bug.st/downloader v1.1.0 diff --git a/go.sum b/go.sum index 88cf885a016..01ded9ff60e 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/goselect v0.0.0-20180328191401-176c667f75aa h1:tIUhmTixVssck4oJ9jKcqaNOF8ua4YxN3UHP0ymBHuQ= +github.com/creack/goselect v0.0.0-20180328191401-176c667f75aa/go.mod h1:gHrIcH/9UZDn2qgeTUeW5K9eZsVYCH6/60J/FHysWyE= github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0= github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -51,6 +53,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/h2non/filetype v1.0.8 h1:le8gpf+FQA0/DlDABbtisA1KiTS0Xi+YSC/E8yY3Y14= github.com/h2non/filetype v1.0.8/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -69,6 +72,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -80,9 +84,11 @@ github.com/miekg/dns v1.0.5 h1:MQBGf2JEJDu0rg9WOpQZzeO+zW8UKwgkvP3R1dUU1Yw= github.com/miekg/dns v1.0.5/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228 h1:Cvfd2dOlXIPTeEkOT/h8PyK4phBngOM4at9/jlgy7d4= github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228/go.mod h1:MGuVJ1+5TX1SCoO2Sx0eAnjpdRytYla2uC1YIZfkC9c= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -104,16 +110,21 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/test/conftest.py b/test/conftest.py index a11e46e2fd0..1a781f74f31 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -62,11 +62,14 @@ def run_command(pytestconfig, data_dir, downloads_dir, working_dir): "ARDUINO_DOWNLOADS_DIR": downloads_dir, "ARDUINO_SKETCHBOOK_DIR": data_dir, } + os.makedirs(os.path.join(data_dir, "packages")) def _run(cmd_string): cli_full_line = "{} {}".format(cli_path, cmd_string) run_context = Context() with run_context.cd(working_dir): - return run_context.run(cli_full_line, echo=False, hide=True, warn=True, env=env) + return run_context.run( + cli_full_line, echo=False, hide=True, warn=True, env=env + ) return _run diff --git a/test/test_core.py b/test/test_core.py index 86eda27ea6e..30a4eb33b6b 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -24,11 +24,11 @@ def test_core_search(run_command): # list all result = run_command("core search") assert result.ok - assert 3 < len(result.stdout.splitlines()) + # filter out empty lines and subtract 1 for the header line + platforms_count = len([l for l in result.stdout.splitlines() if l]) - 1 result = run_command("core search --format json") assert result.ok - data = json.loads(result.stdout) - assert 1 < len(data) + assert len(json.loads(result.stdout)) == platforms_count # search a specific core result = run_command("core search avr") assert result.ok