Skip to content

Commit cd1d472

Browse files
committed
Fix config init command reading existing config file
1 parent ea72158 commit cd1d472

File tree

6 files changed

+339
-47
lines changed

6 files changed

+339
-47
lines changed

Diff for: cli/cli.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,12 @@ func createCliCommandTree(cmd *cobra.Command) {
9999

100100
cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Print the logs on the standard output.")
101101
cmd.PersistentFlags().String("log-level", "", "Messages with this level and above will be logged. Valid levels are: trace, debug, info, warn, error, fatal, panic")
102-
configuration.Settings.BindPFlag("logging.level", cmd.PersistentFlags().Lookup("log-level"))
103102
cmd.PersistentFlags().String("log-file", "", "Path to the file where logs will be written.")
104-
configuration.Settings.BindPFlag("logging.file", cmd.PersistentFlags().Lookup("log-file"))
105103
cmd.PersistentFlags().String("log-format", "", "The output format for the logs, can be {text|json}.")
106-
configuration.Settings.BindPFlag("logging.format", cmd.PersistentFlags().Lookup("log-format"))
107104
cmd.PersistentFlags().StringVar(&outputFormat, "format", "text", "The output format, can be {text|json}.")
108105
cmd.PersistentFlags().StringVar(&configFile, "config-file", "", "The custom config file (if not specified the default will be used).")
109106
cmd.PersistentFlags().StringSlice("additional-urls", []string{}, "Comma-separated list of additional URLs for the Boards Manager.")
110-
configuration.Settings.BindPFlag("board_manager.additional_urls", cmd.PersistentFlags().Lookup("additional-urls"))
107+
configuration.BindFlags(cmd, configuration.Settings)
111108
}
112109

113110
// convert the string passed to the `--log-level` option to the corresponding

Diff for: cli/config/init.go

+48-11
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,21 @@ package config
1717

1818
import (
1919
"os"
20-
"path/filepath"
2120

2221
"github.com/arduino/arduino-cli/cli/errorcodes"
2322
"github.com/arduino/arduino-cli/cli/feedback"
2423
"github.com/arduino/arduino-cli/configuration"
24+
"github.com/arduino/go-paths-helper"
2525
"github.com/sirupsen/logrus"
2626
"github.com/spf13/cobra"
27+
"github.com/spf13/viper"
2728
)
2829

29-
var destDir string
30+
var (
31+
destDir string
32+
destFile string
33+
overwrite bool
34+
)
3035

3136
const defaultFileName = "arduino-cli.yaml"
3237

@@ -37,39 +42,71 @@ func initInitCommand() *cobra.Command {
3742
Long: "Creates or updates the configuration file in the data directory or custom directory with the current configuration settings.",
3843
Example: "" +
3944
" # Writes current configuration to the configuration file in the data directory.\n" +
40-
" " + os.Args[0] + " config init",
45+
" " + os.Args[0] + " config init" +
46+
" " + os.Args[0] + " config init --dest-dir /home/user/MyDirectory" +
47+
" " + os.Args[0] + " config init --dest-file /home/user/MyDirectory/my_settings.yaml",
4148
Args: cobra.NoArgs,
4249
Run: runInitCommand,
4350
}
4451
initCommand.Flags().StringVar(&destDir, "dest-dir", "", "Sets where to save the configuration file.")
52+
initCommand.Flags().StringVar(&destFile, "dest-file", "", "Sets where to save the configuration file.")
53+
initCommand.Flags().BoolVar(&overwrite, "overwrite", false, "Overwrite existing config file.")
4554
return initCommand
4655
}
4756

4857
func runInitCommand(cmd *cobra.Command, args []string) {
49-
if destDir == "" {
58+
if destFile != "" && destDir != "" {
59+
feedback.Errorf("Can't use both --dest-file and --dest-dir flags at the same time.")
60+
os.Exit(errorcodes.ErrGeneric)
61+
}
62+
63+
var configFileAbsPath *paths.Path
64+
var absPath *paths.Path
65+
var err error
66+
67+
switch {
68+
case destFile != "":
69+
configFileAbsPath, err = paths.New(destFile).Abs()
70+
if err != nil {
71+
feedback.Errorf("Cannot find absolute path: %v", err)
72+
os.Exit(errorcodes.ErrGeneric)
73+
}
74+
75+
absPath = configFileAbsPath.Parent()
76+
case destDir == "":
5077
destDir = configuration.Settings.GetString("directories.Data")
78+
fallthrough
79+
default:
80+
absPath, err = paths.New(destDir).Abs()
81+
if err != nil {
82+
feedback.Errorf("Cannot find absolute path: %v", err)
83+
os.Exit(errorcodes.ErrGeneric)
84+
}
85+
configFileAbsPath = absPath.Join(defaultFileName)
5186
}
5287

53-
absPath, err := filepath.Abs(destDir)
54-
if err != nil {
55-
feedback.Errorf("Cannot find absolute path: %v", err)
88+
if !overwrite && configFileAbsPath.Exist() {
89+
feedback.Error("Config file already exists, use --overwrite to discard the existing one.")
5690
os.Exit(errorcodes.ErrGeneric)
5791
}
58-
configFileAbsPath := filepath.Join(absPath, defaultFileName)
5992

6093
logrus.Infof("Writing config file to: %s", absPath)
6194

62-
if err := os.MkdirAll(absPath, os.FileMode(0755)); err != nil {
95+
if err := absPath.MkdirAll(); err != nil {
6396
feedback.Errorf("Cannot create config file directory: %v", err)
6497
os.Exit(errorcodes.ErrGeneric)
6598
}
6699

67-
if err := configuration.Settings.WriteConfigAs(configFileAbsPath); err != nil {
100+
newSettings := viper.New()
101+
configuration.SetDefaults(newSettings)
102+
configuration.BindFlags(cmd, newSettings)
103+
104+
if err := newSettings.WriteConfigAs(configFileAbsPath.String()); err != nil {
68105
feedback.Errorf("Cannot create config file: %v", err)
69106
os.Exit(errorcodes.ErrGeneric)
70107
}
71108

72-
msg := "Config file written to: " + configFileAbsPath
109+
msg := "Config file written to: " + configFileAbsPath.String()
73110
logrus.Info(msg)
74111
feedback.Print(msg)
75112
}

Diff for: configuration/configuration.go

+11-24
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
paths "github.com/arduino/go-paths-helper"
2626
"github.com/arduino/go-win32-utils"
2727
"github.com/sirupsen/logrus"
28+
"github.com/spf13/cobra"
2829
jww "github.com/spf13/jwalterweatherman"
2930
"github.com/spf13/viper"
3031
)
@@ -39,6 +40,8 @@ func Init(configPath string) *viper.Viper {
3940
// Config file metadata
4041
jww.SetStdoutThreshold(jww.LevelFatal)
4142
settings := viper.New()
43+
// Set default values for all the settings
44+
SetDefaults(settings)
4245

4346
configDir := paths.New(configPath)
4447
if configDir != nil && !configDir.IsDir() {
@@ -47,16 +50,6 @@ func Init(configPath string) *viper.Viper {
4750
settings.SetConfigName("arduino-cli")
4851
}
4952

50-
// Bind env vars
51-
settings.SetEnvPrefix("ARDUINO")
52-
settings.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
53-
settings.AutomaticEnv()
54-
55-
// Bind env aliases to keep backward compatibility
56-
settings.BindEnv("directories.User", "ARDUINO_SKETCHBOOK_DIR")
57-
settings.BindEnv("directories.Downloads", "ARDUINO_DOWNLOADS_DIR")
58-
settings.BindEnv("directories.Data", "ARDUINO_DATA_DIR")
59-
6053
if configPath == "" {
6154
// Get default data path if none was provided
6255
if configPath = settings.GetString("directories.Data"); configPath != "" {
@@ -69,20 +62,6 @@ func Init(configPath string) *viper.Viper {
6962
settings.AddConfigPath(filepath.Dir(configPath))
7063
}
7164

72-
// Early access directories.Data and directories.User in case
73-
// those were set through env vars or cli flags
74-
dataDir := settings.GetString("directories.Data")
75-
if dataDir == "" {
76-
dataDir = getDefaultArduinoDataDir()
77-
}
78-
userDir := settings.GetString("directories.User")
79-
if userDir == "" {
80-
userDir = getDefaultUserDir()
81-
}
82-
83-
// Set default values for all the settings
84-
setDefaults(settings, dataDir, userDir)
85-
8665
// Attempt to read config file
8766
if err := settings.ReadInConfig(); err != nil {
8867
// ConfigFileNotFoundError is acceptable, anything else
@@ -95,6 +74,14 @@ func Init(configPath string) *viper.Viper {
9574
return settings
9675
}
9776

77+
// BindFlags creates all the flags binding between the cobra Command and the instance of viper
78+
func BindFlags(cmd *cobra.Command, settings *viper.Viper) {
79+
settings.BindPFlag("logging.level", cmd.Flag("log-level"))
80+
settings.BindPFlag("logging.file", cmd.Flag("log-file"))
81+
settings.BindPFlag("logging.format", cmd.Flag("log-format"))
82+
settings.BindPFlag("board_manager.additional_urls", cmd.Flag("additional-urls"))
83+
}
84+
9885
// getDefaultArduinoDataDir returns the full path to the default arduino folder
9986
func getDefaultArduinoDataDir() string {
10087
userHomeDir, err := os.UserHomeDir()

Diff for: configuration/configuration_test.go

+60-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ func tmpDirOrDie() string {
3030
if err != nil {
3131
panic(fmt.Sprintf("error creating tmp dir: %v", err))
3232
}
33+
// Symlinks are evaluated becase the temp folder on Mac OS is inside /var, it's not writable
34+
// and is a symlink to /private/var, we want the full path so we do this
35+
dir, err = filepath.EvalSymlinks(dir)
36+
if err != nil {
37+
panic(fmt.Sprintf("error evaluating tmp dir symlink: %v", err))
38+
}
3339
return dir
3440
}
3541

@@ -73,6 +79,59 @@ func BenchmarkSearchConfigTree(b *testing.B) {
7379
}
7480

7581
func TestInit(t *testing.T) {
76-
settings := Init("")
82+
tmp := tmpDirOrDie()
83+
defer os.RemoveAll(tmp)
84+
settings := Init(filepath.Join(tmp, "arduino-cli.yaml"))
7785
require.NotNil(t, settings)
86+
87+
require.Equal(t, "info", settings.GetString("logging.level"))
88+
require.Equal(t, "text", settings.GetString("logging.format"))
89+
90+
require.Empty(t, settings.GetStringSlice("board_manager.additional_urls"))
91+
92+
require.NotEmpty(t, settings.GetString("directories.Data"))
93+
require.NotEmpty(t, settings.GetString("directories.Downloads"))
94+
require.NotEmpty(t, settings.GetString("directories.User"))
95+
96+
require.Equal(t, "50051", settings.GetString("daemon.port"))
97+
98+
require.Equal(t, true, settings.GetBool("telemetry.enabled"))
99+
require.Equal(t, ":9090", settings.GetString("telemetry.addr"))
100+
}
101+
102+
func TestFindConfigFile(t *testing.T) {
103+
configFile := FindConfigFile([]string{"--config-file"})
104+
require.Equal(t, "", configFile)
105+
106+
configFile = FindConfigFile([]string{"--config-file", "some/path/to/config"})
107+
require.Equal(t, "some/path/to/config", configFile)
108+
109+
configFile = FindConfigFile([]string{"--config-file", "some/path/to/config/arduino-cli.yaml"})
110+
require.Equal(t, "some/path/to/config/arduino-cli.yaml", configFile)
111+
112+
configFile = FindConfigFile([]string{})
113+
require.Equal(t, "", configFile)
114+
115+
// Create temporary directories
116+
tmp := tmpDirOrDie()
117+
defer os.RemoveAll(tmp)
118+
target := filepath.Join(tmp, "foo", "bar", "baz")
119+
os.MkdirAll(target, os.ModePerm)
120+
require.Nil(t, os.Chdir(target))
121+
122+
// Create a config file
123+
f, err := os.Create(filepath.Join(target, "..", "..", "arduino-cli.yaml"))
124+
require.Nil(t, err)
125+
f.Close()
126+
127+
configFile = FindConfigFile([]string{})
128+
require.Equal(t, filepath.Join(tmp, "foo", "arduino-cli.yaml"), configFile)
129+
130+
// Create another config file
131+
f, err = os.Create(filepath.Join(target, "arduino-cli.yaml"))
132+
require.Nil(t, err)
133+
f.Close()
134+
135+
configFile = FindConfigFile([]string{})
136+
require.Equal(t, filepath.Join(target, "arduino-cli.yaml"), configFile)
78137
}

Diff for: configuration/defaults.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ package configuration
1717

1818
import (
1919
"path/filepath"
20+
"strings"
2021

2122
"github.com/spf13/viper"
2223
)
2324

24-
func setDefaults(settings *viper.Viper, dataDir, userDir string) {
25+
// SetDefaults sets the default values for certain keys
26+
func SetDefaults(settings *viper.Viper) {
2527
// logging
2628
settings.SetDefault("logging.level", "info")
2729
settings.SetDefault("logging.format", "text")
@@ -30,14 +32,24 @@ func setDefaults(settings *viper.Viper, dataDir, userDir string) {
3032
settings.SetDefault("board_manager.additional_urls", []string{})
3133

3234
// arduino directories
33-
settings.SetDefault("directories.Data", dataDir)
34-
settings.SetDefault("directories.Downloads", filepath.Join(dataDir, "staging"))
35-
settings.SetDefault("directories.User", userDir)
35+
settings.SetDefault("directories.Data", getDefaultArduinoDataDir())
36+
settings.SetDefault("directories.Downloads", filepath.Join(getDefaultArduinoDataDir(), "staging"))
37+
settings.SetDefault("directories.User", getDefaultUserDir())
3638

3739
// daemon settings
3840
settings.SetDefault("daemon.port", "50051")
3941

4042
//telemetry settings
4143
settings.SetDefault("telemetry.enabled", true)
4244
settings.SetDefault("telemetry.addr", ":9090")
45+
46+
// Bind env vars
47+
settings.SetEnvPrefix("ARDUINO")
48+
settings.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
49+
settings.AutomaticEnv()
50+
51+
// Bind env aliases to keep backward compatibility
52+
settings.BindEnv("directories.User", "ARDUINO_SKETCHBOOK_DIR")
53+
settings.BindEnv("directories.Downloads", "ARDUINO_DOWNLOADS_DIR")
54+
settings.BindEnv("directories.Data", "ARDUINO_DATA_DIR")
4355
}

0 commit comments

Comments
 (0)