From 95753fccb927e9730fac57f1039d5bb0af911321 Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Thu, 4 Jan 2024 15:45:18 +0100 Subject: [PATCH] Allow to specify the config file through `ARDUINO_CONFIG_FILE` env (#2488) --- docs/configuration.md | 1 + internal/cli/configuration/configuration.go | 9 +++-- .../cli/configuration/configuration_test.go | 16 ++++++-- internal/docsgen/main.go | 2 +- .../integrationtest/config/config_test.go | 39 +++++++++++++++++++ .../monitor/monitor_grpc_test.go | 4 +- main.go | 2 +- 7 files changed, 61 insertions(+), 12 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index cc8ec0e4d15..57bda320635 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -127,6 +127,7 @@ The configuration file must be named `arduino-cli`, with the appropriate file ex Configuration files in the following locations are recognized by Arduino CLI: 1. Location specified by the [`--config-file`][arduino cli command reference] command line flag +1. Location specified by the `ARDUINO_CONFIG_FILE` environment variable 1. Arduino CLI data directory (as configured by `directories.data`) If multiple configuration files are present, the one highest on the above list is used. Configuration files are not diff --git a/internal/cli/configuration/configuration.go b/internal/cli/configuration/configuration.go index 858390894bc..e251b80c514 100644 --- a/internal/cli/configuration/configuration.go +++ b/internal/cli/configuration/configuration.go @@ -133,9 +133,10 @@ func GetDefaultBuiltinLibrariesDir() string { return filepath.Join(getDefaultArduinoDataDir(), "libraries") } -// FindConfigFileInArgs returns the config file path using the -// argument '--config-file' (if specified) or looking in the current working dir -func FindConfigFileInArgs(args []string) string { +// FindConfigFileInArgsFallbackOnEnv returns the config file path using the +// argument '--config-file' (if specified), if empty looks for the ARDUINO_CONFIG_FILE env, +// or looking in the current working dir +func FindConfigFileInArgsFallbackOnEnv(args []string) string { // Look for '--config-file' argument for i, arg := range args { if arg == "--config-file" { @@ -144,5 +145,5 @@ func FindConfigFileInArgs(args []string) string { } } } - return "" + return os.Getenv("ARDUINO_CONFIG_FILE") } diff --git a/internal/cli/configuration/configuration_test.go b/internal/cli/configuration/configuration_test.go index ce879e0c8d3..8b0fd3938cc 100644 --- a/internal/cli/configuration/configuration_test.go +++ b/internal/cli/configuration/configuration_test.go @@ -60,15 +60,23 @@ func TestInit(t *testing.T) { } func TestFindConfigFile(t *testing.T) { - configFile := FindConfigFileInArgs([]string{"--config-file"}) + configFile := FindConfigFileInArgsFallbackOnEnv([]string{"--config-file"}) require.Equal(t, "", configFile) - configFile = FindConfigFileInArgs([]string{"--config-file", "some/path/to/config"}) + configFile = FindConfigFileInArgsFallbackOnEnv([]string{"--config-file", "some/path/to/config"}) require.Equal(t, "some/path/to/config", configFile) - configFile = FindConfigFileInArgs([]string{"--config-file", "some/path/to/config/arduino-cli.yaml"}) + configFile = FindConfigFileInArgsFallbackOnEnv([]string{"--config-file", "some/path/to/config/arduino-cli.yaml"}) require.Equal(t, "some/path/to/config/arduino-cli.yaml", configFile) - configFile = FindConfigFileInArgs([]string{}) + configFile = FindConfigFileInArgsFallbackOnEnv([]string{}) require.Equal(t, "", configFile) + + t.Setenv("ARDUINO_CONFIG_FILE", "some/path/to/config") + configFile = FindConfigFileInArgsFallbackOnEnv([]string{}) + require.Equal(t, "some/path/to/config", configFile) + + // when both env and flag are specified flag takes precedence + configFile = FindConfigFileInArgsFallbackOnEnv([]string{"--config-file", "flag/path"}) + require.Equal(t, "flag/path", configFile) } diff --git a/internal/docsgen/main.go b/internal/docsgen/main.go index 47a62f14a04..3448c9f5f62 100644 --- a/internal/docsgen/main.go +++ b/internal/docsgen/main.go @@ -31,7 +31,7 @@ func main() { os.MkdirAll(os.Args[1], 0755) // Create the output folder if it doesn't already exist - configuration.Settings = configuration.Init(configuration.FindConfigFileInArgs(os.Args)) + configuration.Settings = configuration.Init(configuration.FindConfigFileInArgsFallbackOnEnv(os.Args)) cli := cli.NewCommand() cli.DisableAutoGenTag = true // Disable addition of auto-generated date stamp err := doc.GenMarkdownTree(cli, os.Args[1]) diff --git a/internal/integrationtest/config/config_test.go b/internal/integrationtest/config/config_test.go index 4ccb5460acc..99544f5d0b3 100644 --- a/internal/integrationtest/config/config_test.go +++ b/internal/integrationtest/config/config_test.go @@ -16,9 +16,11 @@ package config_test import ( + "path/filepath" "testing" "github.com/arduino/arduino-cli/internal/integrationtest" + "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" "go.bug.st/testifyjson/requirejson" "gopkg.in/yaml.v3" @@ -815,3 +817,40 @@ func TestDelete(t *testing.T) { require.NotContains(t, configLines, "additional_urls") require.NotContains(t, configLines, "board_manager") } + +func TestInitializationOrderOfConfigThroughFlagAndEnv(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + createConfig := func(path *paths.Path, content string) { + f, err := path.Create() + require.NoError(t, err) + _, err = f.WriteString(content) + require.NoError(t, err) + } + tmp := t.TempDir() + cliConfig, envConfig := paths.New(filepath.Join(tmp, "cli.yaml")), paths.New(filepath.Join(tmp, "env.yaml")) + createConfig(cliConfig, `cli-test: "test"`) + createConfig(envConfig, `env-test: "test"`) + + // No flag nor env specified. + stdout, _, err := cli.Run("config", "dump", "--format", "json") + require.NoError(t, err) + requirejson.NotEmpty(t, stdout) + + // Flag specified + stdout, _, err = cli.Run("config", "dump", "--config-file", cliConfig.String(), "--format", "json") + require.NoError(t, err) + requirejson.Contains(t, stdout, `{"config":{ "cli-test": "test" }}`) + + // Env specified + customEnv := map[string]string{"ARDUINO_CONFIG_FILE": envConfig.String()} + stdout, _, err = cli.RunWithCustomEnv(customEnv, "config", "dump", "--format", "json") + require.NoError(t, err) + requirejson.Contains(t, stdout, `{"config":{ "env-test": "test" }}`) + + // Flag and env specified, flag takes precedence + stdout, _, err = cli.RunWithCustomEnv(customEnv, "config", "dump", "--config-file", cliConfig.String(), "--format", "json") + require.NoError(t, err) + requirejson.Contains(t, stdout, `{"config":{ "cli-test": "test" }}`) +} diff --git a/internal/integrationtest/monitor/monitor_grpc_test.go b/internal/integrationtest/monitor/monitor_grpc_test.go index c96dc36d304..046774d3e82 100644 --- a/internal/integrationtest/monitor/monitor_grpc_test.go +++ b/internal/integrationtest/monitor/monitor_grpc_test.go @@ -58,7 +58,7 @@ func TestMonitorGRPCClose(t *testing.T) { tmpFileMatcher := regexp.MustCompile("Tmpfile: (.*)\n") { ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) - mon, err := grpcInst.Monitor(ctx, ports[0].Port) + mon, err := grpcInst.Monitor(ctx, ports[0].GetPort()) var tmpFile *paths.Path for { monResp, err := mon.Recv() @@ -85,7 +85,7 @@ func TestMonitorGRPCClose(t *testing.T) { { // Keep a timeout to allow the test to exit in any case ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) - mon, err := grpcInst.Monitor(ctx, ports[0].Port) + mon, err := grpcInst.Monitor(ctx, ports[0].GetPort()) var tmpFile *paths.Path for { monResp, err := mon.Recv() diff --git a/main.go b/main.go index 751e81a32cc..db1baad2bee 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ import ( ) func main() { - configuration.Settings = configuration.Init(configuration.FindConfigFileInArgs(os.Args)) + configuration.Settings = configuration.Init(configuration.FindConfigFileInArgsFallbackOnEnv(os.Args)) i18n.Init(configuration.Settings.GetString("locale")) arduinoCmd := cli.NewCommand() if err := arduinoCmd.Execute(); err != nil {