From f4b59de07497e9615ed005137f7221fdac0f1238 Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Tue, 3 Oct 2023 13:54:54 -0700 Subject: [PATCH 01/11] chore: refactor logging environment notice --- pkg/cmd/root.go | 4 ++-- pkg/cmd/run.go | 2 +- pkg/cmd/setup.go | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index a8844455..4dd9ce15 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -99,7 +99,7 @@ func loadFlags(cmd *cobra.Command) { if configuration.CanReadEnv { userConfigDir := os.Getenv("DOPPLER_CONFIG_DIR") if userConfigDir != "" { - utils.Log(valueFromEnvironmentNotice("DOPPLER_CONFIG_DIR")) + logValueFromEnvironmentNotice("DOPPLER_CONFIG_DIR") configuration.SetConfigDir(userConfigDir) } } @@ -126,7 +126,7 @@ func loadFlags(cmd *cobra.Command) { if configuration.CanReadEnv { enable := os.Getenv("DOPPLER_ENABLE_VERSION_CHECK") if enable == "false" { - utils.Log(valueFromEnvironmentNotice("DOPPLER_ENABLE_VERSION_CHECK")) + logValueFromEnvironmentNotice("DOPPLER_ENABLE_VERSION_CHECK") version.PerformVersionCheck = false } } diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go index 13fed4e6..40c2e0b4 100644 --- a/pkg/cmd/run.go +++ b/pkg/cmd/run.go @@ -504,7 +504,7 @@ func getPassphrase(cmd *cobra.Command, flag string, config models.ScopedOptions) if configuration.CanReadEnv { passphrase := os.Getenv("DOPPLER_PASSPHRASE") if passphrase != "" { - utils.Log(valueFromEnvironmentNotice("DOPPLER_PASSPHRASE")) + logValueFromEnvironmentNotice("DOPPLER_PASSPHRASE") return passphrase } } diff --git a/pkg/cmd/setup.go b/pkg/cmd/setup.go index 7429da26..409ff45d 100644 --- a/pkg/cmd/setup.go +++ b/pkg/cmd/setup.go @@ -53,7 +53,7 @@ func setup(cmd *cobra.Command, args []string) { case models.FlagSource.String(): saveToken = true case models.EnvironmentSource.String(): - utils.Log(valueFromEnvironmentNotice("DOPPLER_TOKEN")) + logValueFromEnvironmentNotice("DOPPLER_TOKEN") saveToken = true } } @@ -94,7 +94,7 @@ func setup(cmd *cobra.Command, args []string) { case models.FlagSource.String(): selectedProject = localConfig.EnclaveProject.Value case models.EnvironmentSource.String(): - utils.Log(valueFromEnvironmentNotice("DOPPLER_PROJECT")) + logValueFromEnvironmentNotice("DOPPLER_PROJECT") selectedProject = localConfig.EnclaveProject.Value default: if useRepoConfig && repo.Project != "" { @@ -129,7 +129,7 @@ func setup(cmd *cobra.Command, args []string) { case models.FlagSource.String(): selectedConfig = localConfig.EnclaveConfig.Value case models.EnvironmentSource.String(): - utils.Log(valueFromEnvironmentNotice("DOPPLER_CONFIG")) + logValueFromEnvironmentNotice("DOPPLER_CONFIG") selectedConfig = localConfig.EnclaveConfig.Value default: if useRepoConfig && repo.Config != "" { @@ -246,8 +246,8 @@ func selectConfig(configs []models.ConfigInfo, selectedConfiguredProject bool, p return selectedConfig } -func valueFromEnvironmentNotice(name string) string { - return fmt.Sprintf("Using %s from the environment. To disable this, use --no-read-env.", name) +func logValueFromEnvironmentNotice(name string) { + utils.Log(fmt.Sprintf("Using %s from the environment. To disable this, use --no-read-env.", name)) } // we're looking for duplicate paths and more than one repo being defined without a path. From d2603bb6cc7f837e591c35dee48bf36f11d1caef Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Mon, 9 Oct 2023 22:13:57 -0700 Subject: [PATCH 02/11] Add `configure flags` command for configuring CLI behavior --- pkg/cmd/configure_flags.go | 151 +++++++++++++++++++++++++++++++++++++ pkg/configuration/flags.go | 43 +++++++++++ pkg/models/config.go | 1 + pkg/models/flags.go | 25 ++++++ pkg/printer/config.go | 36 +++++++++ 5 files changed, 256 insertions(+) create mode 100644 pkg/cmd/configure_flags.go create mode 100644 pkg/configuration/flags.go create mode 100644 pkg/models/flags.go diff --git a/pkg/cmd/configure_flags.go b/pkg/cmd/configure_flags.go new file mode 100644 index 00000000..ebafb6a1 --- /dev/null +++ b/pkg/cmd/configure_flags.go @@ -0,0 +1,151 @@ +/* +Copyright © 2023 Doppler + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "errors" + "fmt" + + "github.com/DopplerHQ/cli/pkg/configuration" + "github.com/DopplerHQ/cli/pkg/models" + "github.com/DopplerHQ/cli/pkg/printer" + "github.com/DopplerHQ/cli/pkg/utils" + "github.com/spf13/cobra" +) + +var configureFlagsCmd = &cobra.Command{ + Use: "flags", + Short: "View current flags", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + values := map[string]bool{} + flags := models.GetFlags() + for _, flag := range flags { + value := configuration.GetFlag(flag) + values[flag] = value + } + + printer.Flags(values, utils.OutputJSON) + }, +} + +var configureFlagsGetCmd = &cobra.Command{ + Use: "get [flag]", + Short: "Get the value of a flag", + ValidArgsFunction: FlagsValidArgs, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + plain := utils.GetBoolFlag(cmd, "plain") + + flag := args[0] + if !configuration.IsValidFlag(flag) { + utils.HandleError(errors.New("invalid flag " + flag)) + } + + enabled := configuration.GetFlag(flag) + + printer.Flag(flag, enabled, utils.OutputJSON, plain, false) + }, +} + +var configureFlagsEnableCmd = &cobra.Command{ + Use: "enable [flag]", + Short: "Enable a flag", + ValidArgsFunction: FlagsValidArgs, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + flag := args[0] + if !configuration.IsValidFlag(flag) { + utils.HandleError(errors.New("invalid flag " + flag)) + } + + const value = true + configuration.SetFlag(flag, value) + + if !utils.Silent { + printer.Flag(flag, value, utils.OutputJSON, false, false) + } + }, +} + +var configureFlagsDisableCmd = &cobra.Command{ + Use: "disable [flag]", + Short: "Disable a flag", + ValidArgsFunction: FlagsValidArgs, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + flag := args[0] + if !configuration.IsValidFlag(flag) { + utils.HandleError(errors.New("invalid flag " + flag)) + } + + const value = false + configuration.SetFlag(flag, value) + + if !utils.Silent { + printer.Flag(flag, value, utils.OutputJSON, false, false) + } + }, +} + +var configureFlagsResetCmd = &cobra.Command{ + Use: "reset [flag]", + Short: "Reset a flag to its default", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + + flag := args[0] + if !configuration.IsValidFlag(flag) { + utils.HandleError(errors.New("invalid flag " + flag)) + } + + yes := utils.GetBoolFlag(cmd, "yes") + defaultValue := configuration.GetFlagDefault(flag) + + if !yes { + utils.PrintWarning(fmt.Sprintf("This will reset the %s flag to %t", flag, defaultValue)) + if !utils.ConfirmationPrompt("Continue?", false) { + utils.Log("Aborting") + return + } + } + + configuration.SetFlag(flag, defaultValue) + printer.Flag(flag, defaultValue, utils.OutputJSON, false, false) + }, +} + +func FlagsValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + persistentValidArgsFunction(cmd) + + return models.GetFlags(), cobra.ShellCompDirectiveNoFileComp +} + +func init() { + configureCmd.AddCommand(configureFlagsCmd) + + configureFlagsGetCmd.Flags().Bool("plain", false, "print value without formatting") + configureFlagsCmd.AddCommand(configureFlagsGetCmd) + + configureFlagsCmd.AddCommand(configureFlagsEnableCmd) + + configureFlagsCmd.AddCommand(configureFlagsDisableCmd) + + configureFlagsResetCmd.Flags().BoolP("yes", "y", false, "proceed without confirmation") + configureFlagsCmd.AddCommand(configureFlagsResetCmd) + + rootCmd.AddCommand(configureFlagsCmd) +} diff --git a/pkg/configuration/flags.go b/pkg/configuration/flags.go new file mode 100644 index 00000000..0015e682 --- /dev/null +++ b/pkg/configuration/flags.go @@ -0,0 +1,43 @@ +/* +Copyright © 2023 Doppler + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package configuration + +import ( + "slices" + + "github.com/DopplerHQ/cli/pkg/models" +) + +func GetFlag(flag string) bool { + return false +} + +func SetFlag(flag string, enable bool) { + writeConfig(configContents) +} + +func GetFlagDefault(flag string) bool { + return false +} + +func IsValidFlag(flag string) bool { + flags := models.GetFlags() + return slices.Contains(flags, flag) +} + +func IsAnalyticsEnabled() bool { + return GetFlag(models.FlagAnalytics) +} diff --git a/pkg/models/config.go b/pkg/models/config.go index c7d21b28..735ae594 100644 --- a/pkg/models/config.go +++ b/pkg/models/config.go @@ -25,6 +25,7 @@ type ConfigFile struct { VersionCheck VersionCheck `yaml:"version-check"` Analytics AnalyticsOptions `yaml:"analytics"` TUI TUIOptions `yaml:"tui"` + Flags Flags `yaml:"flags,omitempty"` } // FileScopedOptions config options diff --git a/pkg/models/flags.go b/pkg/models/flags.go new file mode 100644 index 00000000..b26b8e2e --- /dev/null +++ b/pkg/models/flags.go @@ -0,0 +1,25 @@ +/* +Copyright © 2023 Doppler + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package models + +type Flags struct { +} + +var flags = []string{} + +func GetFlags() []string { + return flags +} diff --git a/pkg/printer/config.go b/pkg/printer/config.go index e940afca..bd8b35b2 100644 --- a/pkg/printer/config.go +++ b/pkg/printer/config.go @@ -18,6 +18,7 @@ package printer import ( "fmt" "sort" + "strconv" "strings" "github.com/DopplerHQ/cli/pkg/configuration" @@ -181,3 +182,38 @@ func ConfigOptionNames(options []string, jsonFlag bool) { } Table([]string{"name"}, rows, TableOptions()) } + +func Flags(flags map[string]bool, jsonFlag bool) { + if jsonFlag { + JSON(flags) + return + } + + var rows [][]string + for flag, value := range flags { + rows = append(rows, []string{flag, strconv.FormatBool(value)}) + } + Table([]string{"flag", "value"}, rows, TableOptions()) +} + +func Flag(flag string, value bool, jsonFlag bool, plain bool, copy bool) { + if plain || copy { + if copy { + if err := utils.CopyToClipboard(strconv.FormatBool(value)); err != nil { + utils.HandleError(err, "Unable to copy to clipboard") + } + } + + if plain { + fmt.Println(strconv.FormatBool(value)) + return + } + } + + if jsonFlag { + JSON(map[string]bool{flag: value}) + return + } + + Table([]string{"flag", "value"}, [][]string{{flag, strconv.FormatBool(value)}}, TableOptions()) +} From f55280c0659ea303aa8378a3bacee66c38c30ab1 Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Mon, 9 Oct 2023 22:16:59 -0700 Subject: [PATCH 03/11] chore: support managing analytics via flags --- pkg/cmd/analytics.go | 18 +++++++++++++----- pkg/configuration/analytics.go | 30 ------------------------------ pkg/configuration/config.go | 12 ++++++++++++ pkg/configuration/flags.go | 18 ++++++++++++++++++ pkg/models/config.go | 2 +- pkg/models/flags.go | 9 ++++++++- 6 files changed, 52 insertions(+), 37 deletions(-) delete mode 100644 pkg/configuration/analytics.go diff --git a/pkg/cmd/analytics.go b/pkg/cmd/analytics.go index 84a130cb..a6e31ac2 100644 --- a/pkg/cmd/analytics.go +++ b/pkg/cmd/analytics.go @@ -19,15 +19,17 @@ import ( "fmt" "github.com/DopplerHQ/cli/pkg/configuration" + "github.com/DopplerHQ/cli/pkg/models" "github.com/DopplerHQ/cli/pkg/printer" "github.com/DopplerHQ/cli/pkg/utils" "github.com/spf13/cobra" ) var analyticsCmd = &cobra.Command{ - Use: "analytics", - Short: "Manage anonymous analytics", - Args: cobra.NoArgs, + Use: "analytics", + Short: "Manage anonymous analytics", + Hidden: true, + Args: cobra.NoArgs, } var analyticsStatusCmd = &cobra.Command{ @@ -35,6 +37,8 @@ var analyticsStatusCmd = &cobra.Command{ Short: "Check whether anonymous analytics are enabled", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { + deprecatedCommand("configure flags get analytics") + if utils.OutputJSON { printer.JSON(map[string]bool{"enabled": configuration.IsAnalyticsEnabled()}) } else { @@ -52,7 +56,9 @@ var analyticsEnableCmd = &cobra.Command{ Short: "Enable anonymous analytics", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - configuration.EnableAnalytics() + deprecatedCommand("configure flags enable analytics") + + configuration.SetFlag(models.FlagAnalytics, true) if utils.OutputJSON { printer.JSON(map[string]bool{"enabled": true}) @@ -67,7 +73,9 @@ var analyticsDisableCmd = &cobra.Command{ Short: "Disable anonymous analytics", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - configuration.DisableAnalytics() + deprecatedCommand("configure flags disable analytics") + + configuration.SetFlag(models.FlagAnalytics, false) if utils.OutputJSON { printer.JSON(map[string]bool{"enabled": false}) diff --git a/pkg/configuration/analytics.go b/pkg/configuration/analytics.go deleted file mode 100644 index 791da2c3..00000000 --- a/pkg/configuration/analytics.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright © 2022 Doppler - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package configuration - -func IsAnalyticsEnabled() bool { - return !configContents.Analytics.Disable -} - -func EnableAnalytics() { - configContents.Analytics.Disable = false - writeConfig(configContents) -} - -func DisableAnalytics() { - configContents.Analytics.Disable = true - writeConfig(configContents) -} diff --git a/pkg/configuration/config.go b/pkg/configuration/config.go index 48d71796..a5bec098 100644 --- a/pkg/configuration/config.go +++ b/pkg/configuration/config.go @@ -403,6 +403,11 @@ func ClearConfig() { // Write config to filesystem func writeConfig(config models.ConfigFile) { + // keep both analytics properties up-to-date + if config.Flags.Analytics != nil { + config.Analytics.Disable = !*config.Flags.Analytics + } + bytes, err := yaml.Marshal(config) if err != nil { utils.HandleError(err) @@ -481,6 +486,13 @@ func readConfig() (models.ConfigFile, int, int) { } config.Scoped = normalizedOptions + + // support legacy analytics property when new property isn't set + if config.Flags.Analytics == nil && config.Analytics.Disable { + b := false + config.Flags.Analytics = &b + } + return config, uid, gid } diff --git a/pkg/configuration/flags.go b/pkg/configuration/flags.go index 0015e682..ae49f84a 100644 --- a/pkg/configuration/flags.go +++ b/pkg/configuration/flags.go @@ -22,14 +22,32 @@ import ( ) func GetFlag(flag string) bool { + flags := configContents.Flags + switch flag { + case models.FlagAnalytics: + if flags.Analytics != nil { + return *flags.Analytics + } + return GetFlagDefault(models.FlagAnalytics) + } + return false } func SetFlag(flag string, enable bool) { + switch flag { + case models.FlagAnalytics: + configContents.Flags.Analytics = &enable + } writeConfig(configContents) } func GetFlagDefault(flag string) bool { + switch flag { + case models.FlagAnalytics: + return true + } + return false } diff --git a/pkg/models/config.go b/pkg/models/config.go index 735ae594..5c414315 100644 --- a/pkg/models/config.go +++ b/pkg/models/config.go @@ -23,7 +23,7 @@ import ( type ConfigFile struct { Scoped map[string]FileScopedOptions `yaml:"scoped"` VersionCheck VersionCheck `yaml:"version-check"` - Analytics AnalyticsOptions `yaml:"analytics"` + Analytics AnalyticsOptions `yaml:"analytics,omitempty"` TUI TUIOptions `yaml:"tui"` Flags Flags `yaml:"flags,omitempty"` } diff --git a/pkg/models/flags.go b/pkg/models/flags.go index b26b8e2e..e041a996 100644 --- a/pkg/models/flags.go +++ b/pkg/models/flags.go @@ -15,10 +15,17 @@ limitations under the License. */ package models +const ( + FlagAnalytics string = "analytics" +) + type Flags struct { + Analytics *bool `yaml:"analytics,omitempty"` } -var flags = []string{} +var flags = []string{ + FlagAnalytics, +} func GetFlags() []string { return flags From 152110c1ef7f93e1b2c3e7bdb7d177a8228073c1 Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Mon, 9 Oct 2023 22:22:43 -0700 Subject: [PATCH 04/11] chore: support managing environment warning via flags --- pkg/cmd/root.go | 1 + pkg/cmd/setup.go | 4 +++- pkg/configuration/flags.go | 9 +++++++++ pkg/models/flags.go | 3 +++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 4dd9ce15..333ddfd6 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -99,6 +99,7 @@ func loadFlags(cmd *cobra.Command) { if configuration.CanReadEnv { userConfigDir := os.Getenv("DOPPLER_CONFIG_DIR") if userConfigDir != "" { + // this warning will always be printed since the config file's flags haven't been loaded yet logValueFromEnvironmentNotice("DOPPLER_CONFIG_DIR") configuration.SetConfigDir(userConfigDir) } diff --git a/pkg/cmd/setup.go b/pkg/cmd/setup.go index 409ff45d..72b86922 100644 --- a/pkg/cmd/setup.go +++ b/pkg/cmd/setup.go @@ -247,7 +247,9 @@ func selectConfig(configs []models.ConfigInfo, selectedConfiguredProject bool, p } func logValueFromEnvironmentNotice(name string) { - utils.Log(fmt.Sprintf("Using %s from the environment. To disable this, use --no-read-env.", name)) + if configuration.GetFlag(models.FlagEnvWarning) { + utils.Log(fmt.Sprintf("Using %s from the environment. To disable this, use --no-read-env.", name)) + } } // we're looking for duplicate paths and more than one repo being defined without a path. diff --git a/pkg/configuration/flags.go b/pkg/configuration/flags.go index ae49f84a..7f8cc718 100644 --- a/pkg/configuration/flags.go +++ b/pkg/configuration/flags.go @@ -29,6 +29,11 @@ func GetFlag(flag string) bool { return *flags.Analytics } return GetFlagDefault(models.FlagAnalytics) + case models.FlagEnvWarning: + if flags.EnvWarning != nil { + return *flags.EnvWarning + } + return GetFlagDefault(models.FlagEnvWarning) } return false @@ -38,6 +43,8 @@ func SetFlag(flag string, enable bool) { switch flag { case models.FlagAnalytics: configContents.Flags.Analytics = &enable + case models.FlagEnvWarning: + configContents.Flags.EnvWarning = &enable } writeConfig(configContents) } @@ -46,6 +53,8 @@ func GetFlagDefault(flag string) bool { switch flag { case models.FlagAnalytics: return true + case models.FlagEnvWarning: + return true } return false diff --git a/pkg/models/flags.go b/pkg/models/flags.go index e041a996..0fc072fe 100644 --- a/pkg/models/flags.go +++ b/pkg/models/flags.go @@ -17,14 +17,17 @@ package models const ( FlagAnalytics string = "analytics" + FlagEnvWarning string = "env-warning" ) type Flags struct { Analytics *bool `yaml:"analytics,omitempty"` + EnvWarning *bool `yaml:"env-warning,omitempty"` } var flags = []string{ FlagAnalytics, + FlagEnvWarning, } func GetFlags() []string { From 322fd327c8aca7468a52b5054e45dd2730f10967 Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Mon, 9 Oct 2023 22:24:38 -0700 Subject: [PATCH 05/11] chore: support disabling checking for updates via flags --- pkg/cmd/root.go | 27 +++++++++++++++++---------- pkg/configuration/flags.go | 9 +++++++++ pkg/models/flags.go | 3 +++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 333ddfd6..9e651a20 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -24,6 +24,7 @@ import ( "github.com/DopplerHQ/cli/pkg/controllers" "github.com/DopplerHQ/cli/pkg/global" "github.com/DopplerHQ/cli/pkg/http" + "github.com/DopplerHQ/cli/pkg/models" "github.com/DopplerHQ/cli/pkg/printer" "github.com/DopplerHQ/cli/pkg/utils" "github.com/DopplerHQ/cli/pkg/version" @@ -61,6 +62,21 @@ var rootCmd = &cobra.Command{ // tty is required to accept user input, otherwise the update can't be accepted/declined isTTY := isatty.IsTerminal(os.Stdout.Fd()) + // version check + if !configuration.GetFlag(models.FlagUpdateCheck) { + version.PerformVersionCheck = false + } + if version.PerformVersionCheck && configuration.CanReadEnv { + enable := os.Getenv("DOPPLER_ENABLE_VERSION_CHECK") + if enable == "false" { + logValueFromEnvironmentNotice("DOPPLER_ENABLE_VERSION_CHECK") + version.PerformVersionCheck = false + } + } + if version.PerformVersionCheck { + version.PerformVersionCheck = !utils.GetBoolFlagIfChanged(cmd, "no-check-version", !version.PerformVersionCheck) + } + // only run version check if we can print the results // --plain doesn't normally affect logging output, but due to legacy reasons it does here // also don't want to display updates if user doesn't want to be prompted (--no-prompt/--no-interactive) @@ -84,6 +100,7 @@ func persistentValidArgsFunction(cmd *cobra.Command) { loadFlags(cmd) } +// this function runs before the config file has been loaded, so flags will not be honored func loadFlags(cmd *cobra.Command) { var err error var normalizedScope string @@ -122,16 +139,6 @@ func loadFlags(cmd *cobra.Command) { // no-file is used by the 'secrets download' command to output secrets to stdout utils.Silent = utils.GetBoolFlagIfChanged(cmd, "no-file", utils.Silent) - - // version check - if configuration.CanReadEnv { - enable := os.Getenv("DOPPLER_ENABLE_VERSION_CHECK") - if enable == "false" { - logValueFromEnvironmentNotice("DOPPLER_ENABLE_VERSION_CHECK") - version.PerformVersionCheck = false - } - } - version.PerformVersionCheck = !utils.GetBoolFlagIfChanged(cmd, "no-check-version", !version.PerformVersionCheck) } func deprecatedCommand(newCommand string) { diff --git a/pkg/configuration/flags.go b/pkg/configuration/flags.go index 7f8cc718..9d914098 100644 --- a/pkg/configuration/flags.go +++ b/pkg/configuration/flags.go @@ -34,6 +34,11 @@ func GetFlag(flag string) bool { return *flags.EnvWarning } return GetFlagDefault(models.FlagEnvWarning) + case models.FlagUpdateCheck: + if flags.UpdateCheck != nil { + return *flags.UpdateCheck + } + return GetFlagDefault(models.FlagUpdateCheck) } return false @@ -45,6 +50,8 @@ func SetFlag(flag string, enable bool) { configContents.Flags.Analytics = &enable case models.FlagEnvWarning: configContents.Flags.EnvWarning = &enable + case models.FlagUpdateCheck: + configContents.Flags.UpdateCheck = &enable } writeConfig(configContents) } @@ -55,6 +62,8 @@ func GetFlagDefault(flag string) bool { return true case models.FlagEnvWarning: return true + case models.FlagUpdateCheck: + return true } return false diff --git a/pkg/models/flags.go b/pkg/models/flags.go index 0fc072fe..d84b529a 100644 --- a/pkg/models/flags.go +++ b/pkg/models/flags.go @@ -18,16 +18,19 @@ package models const ( FlagAnalytics string = "analytics" FlagEnvWarning string = "env-warning" + FlagUpdateCheck string = "update-check" ) type Flags struct { Analytics *bool `yaml:"analytics,omitempty"` EnvWarning *bool `yaml:"env-warning,omitempty"` + UpdateCheck *bool `yaml:"update-check,omitempty"` } var flags = []string{ FlagAnalytics, FlagEnvWarning, + FlagUpdateCheck, } func GetFlags() []string { From 183ad81610eface37a1300edf0790404af0153e0 Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Mon, 9 Oct 2023 22:26:42 -0700 Subject: [PATCH 06/11] chore: support configuring flags via doppler.yaml --- pkg/cmd/setup.go | 40 +++++++++++++++++++++++++++++++++++++++ pkg/models/repo_config.go | 2 ++ tests/e2e/setup.sh | 21 ++++++++++++++++++-- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/setup.go b/pkg/cmd/setup.go index 72b86922..12c9f549 100644 --- a/pkg/cmd/setup.go +++ b/pkg/cmd/setup.go @@ -177,6 +177,46 @@ func setup(cmd *cobra.Command, args []string) { printer.ScopedConfigValues(conf, valuesToPrint, models.ScopedOptionsMap(&conf), utils.OutputJSON, false, false) } } + + if repoConfig.Flags.Analytics != nil { + flag := models.FlagAnalytics + value := *repoConfig.Flags.Analytics + + if utils.CanLogInfo() { + verb := "Enabling" + if !value { + verb = "Disabling" + } + utils.Log(fmt.Sprintf("%s %s", verb, flag)) + } + configuration.SetFlag(flag, value) + } + if repoConfig.Flags.EnvWarning != nil { + flag := models.FlagEnvWarning + value := *repoConfig.Flags.EnvWarning + + if utils.CanLogInfo() { + verb := "Enabling" + if !value { + verb = "Disabling" + } + utils.Log(fmt.Sprintf("%s %s", verb, flag)) + } + configuration.SetFlag(flag, value) + } + if repoConfig.Flags.UpdateCheck != nil { + flag := models.FlagUpdateCheck + value := *repoConfig.Flags.UpdateCheck + + if utils.CanLogInfo() { + verb := "Enabling" + if !value { + verb = "Disabling" + } + utils.Log(fmt.Sprintf("%s %s", verb, flag)) + } + configuration.SetFlag(flag, value) + } } func selectProject(projects []models.ProjectInfo, prevConfiguredProject string, canPromptUser bool) string { diff --git a/pkg/models/repo_config.go b/pkg/models/repo_config.go index 68ede074..9fd8d3d4 100644 --- a/pkg/models/repo_config.go +++ b/pkg/models/repo_config.go @@ -27,10 +27,12 @@ type ProjectConfig struct { // that only supported a single project and config type RepoConfig struct { Setup ProjectConfig `yaml:"setup"` + Flags Flags `yaml:"flags"` } // MultiRepoConfig struct supports doppler.yaml files containing multiple // project and config combos type MultiRepoConfig struct { Setup []ProjectConfig `yaml:"setup"` + Flags Flags `yaml:"flags"` } diff --git a/tests/e2e/setup.sh b/tests/e2e/setup.sh index f97c4548..0470bff7 100755 --- a/tests/e2e/setup.sh +++ b/tests/e2e/setup.sh @@ -4,8 +4,6 @@ set -euo pipefail TEST_NAME="setup file" TEST_CONFIG_DIR="./temp-config-dir" -DOPPLER_PROJECT="" -DOPPLER_CONFIG="" cleanup() { exit_code=$? @@ -260,4 +258,23 @@ afterEach ###################################################################### +name="test doppler.yaml setup file with flags" + +beforeEach + +cat << EOF > doppler.yaml +flags: + analytics: false + env-warning: false + update-check: false +EOF +"$DOPPLER_BINARY" setup --config-dir=$TEST_CONFIG_DIR --no-interactive +[[ "$("$DOPPLER_BINARY" configure flags get analytics --plain)" == 'false' ]] || error "ERROR: setup not setting disabled value for analytics" +[[ "$("$DOPPLER_BINARY" configure flags get env-warning --plain)" == 'false' ]] || error "ERROR: setup not setting disabled value for env-warning" +[[ "$("$DOPPLER_BINARY" configure flags get update-check --plain)" == 'false' ]] || error "ERROR: setup not setting disabled value for update-check" + +afterEach + +###################################################################### + afterAll From 8eb10e02406cf155256e3d4f19a25dcd299c39fe Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Mon, 9 Oct 2023 22:27:15 -0700 Subject: [PATCH 07/11] chore: add e2e tests for flags --- tests/e2e.sh | 1 + tests/e2e/flags.sh | 96 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100755 tests/e2e/flags.sh diff --git a/tests/e2e.sh b/tests/e2e.sh index 6032c2e9..34ecefae 100755 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -24,6 +24,7 @@ export DOPPLER_CONFIG="e2e" "$DIR/e2e/me.sh" "$DIR/e2e/global-flags.sh" "$DIR/e2e/update.sh" +"$DIR/e2e/flags.sh" echo -e "\nAll tests completed successfully!" exit 0 diff --git a/tests/e2e/flags.sh b/tests/e2e/flags.sh new file mode 100755 index 00000000..f2f28d9c --- /dev/null +++ b/tests/e2e/flags.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +set -euo pipefail + +TEST_NAME="flags" +TEST_CONFIG_DIR="./temp-config-dir" + +cleanup() { + exit_code=$? + if [ "$exit_code" -ne 0 ]; then + echo "ERROR: '$TEST_NAME' tests failed during execution" + afterAll || echo "ERROR: Cleanup failed" + fi + + exit "$exit_code" +} +trap cleanup EXIT +trap cleanup INT + +beforeAll() { + echo "INFO: Executing '$TEST_NAME' tests" +} + +beforeEach() { + rm -rf $TEST_CONFIG_DIR +} + +afterAll() { + echo "INFO: Completed '$TEST_NAME' tests" + beforeEach +} + +error() { + message=$1 + echo "$message" + exit 1 +} + +flags=('analytics' 'env-warning' 'update-check') + +beforeAll + +beforeEach + +# verify defaults +for flag in "${flags[@]}"; do + [[ "$("$DOPPLER_BINARY" configure flags get "$flag" --plain --config-dir=$TEST_CONFIG_DIR)" == 'true' ]] || error "ERROR: incorrect default for $flag" +done + +beforeEach + +# verify set/get +for flag in "${flags[@]}"; do + "$DOPPLER_BINARY" configure flags disable "$flag" --config-dir=$TEST_CONFIG_DIR >/dev/null 2>/dev/null + [[ "$("$DOPPLER_BINARY" configure flags get "$flag" --plain --config-dir=$TEST_CONFIG_DIR)" == 'false' ]] || error "ERROR: incorrect value for $flag after disabling" + "$DOPPLER_BINARY" configure flags enable "$flag" --config-dir=$TEST_CONFIG_DIR >/dev/null 2>/dev/null + [[ "$("$DOPPLER_BINARY" configure flags get "$flag" --plain --config-dir=$TEST_CONFIG_DIR)" == 'true' ]] || error "ERROR: incorrect value for $flag after enabling" +done + +# beforeEach + +# verify reset +for flag in "${flags[@]}"; do + "$DOPPLER_BINARY" configure flags disable "$flag" --config-dir=$TEST_CONFIG_DIR >/dev/null 2>/dev/null + "$DOPPLER_BINARY" configure flags reset -y "$flag" --config-dir=$TEST_CONFIG_DIR >/dev/null 2>/dev/null + [[ "$("$DOPPLER_BINARY" configure flags get "$flag" --plain --config-dir=$TEST_CONFIG_DIR)" == 'true' ]] || error "ERROR: incorrect value for $flag after reset" +done + +beforeEach + +# verify interoperability between 'flags' command and legacy 'analytics' command +[[ "$("$DOPPLER_BINARY" configure flags get analytics --plain --config-dir=$TEST_CONFIG_DIR)" == 'true' ]] || error "ERROR: incorrect initial value for analytics" +"$DOPPLER_BINARY" analytics disable --config-dir=$TEST_CONFIG_DIR >/dev/null 2>&1 +[[ "$("$DOPPLER_BINARY" configure flags get analytics --plain --config-dir=$TEST_CONFIG_DIR)" == 'false' ]] || error "ERROR: incorrect disabled value for analytics" +"$DOPPLER_BINARY" analytics enable --config-dir=$TEST_CONFIG_DIR >/dev/null 2>&1 +[[ "$("$DOPPLER_BINARY" configure flags get analytics --plain --config-dir=$TEST_CONFIG_DIR)" == 'true' ]] || error "ERROR: incorrect enabled value for analytics" + +beforeEach + +# parse legacy analytics field from config file +mkdir ./temp-config-dir +cat << EOF > ./temp-config-dir/.doppler.yaml +analytics: + disable: true +EOF + +[[ "$("$DOPPLER_BINARY" configure flags get analytics --plain --config-dir=$TEST_CONFIG_DIR)" == 'false' ]] || error "ERROR: incorrect value read when parsing legacy analytics field in config file" + +cat << EOF > ./temp-config-dir/.doppler.yaml +analytics: + disable: false +EOF + +[[ "$("$DOPPLER_BINARY" configure flags get analytics --plain --config-dir=$TEST_CONFIG_DIR)" == 'true' ]] || error "ERROR: incorrect value read when parsing legacy analytics field in config file" + +afterAll From 55a0f0c1d0293134bfc560bd6dc01398dd603aa7 Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Mon, 9 Oct 2023 22:27:27 -0700 Subject: [PATCH 08/11] chore: disabling checking for updates when running tests --- tests/e2e.sh | 2 ++ tests/e2e/setup.sh | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/e2e.sh b/tests/e2e.sh index 34ecefae..83137135 100755 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -9,6 +9,8 @@ export DOPPLER_SCRIPTS_DIR="$DIR/../scripts" export DOPPLER_PROJECT="cli" export DOPPLER_CONFIG="e2e" +export DOPPLER_ENABLE_VERSION_CHECK=false + # Run tests "$DIR/e2e/secrets-download-fallback.sh" "$DIR/e2e/secrets-substitute.sh" diff --git a/tests/e2e/setup.sh b/tests/e2e/setup.sh index 0470bff7..c14255cb 100755 --- a/tests/e2e/setup.sh +++ b/tests/e2e/setup.sh @@ -4,6 +4,8 @@ set -euo pipefail TEST_NAME="setup file" TEST_CONFIG_DIR="./temp-config-dir" +unset DOPPLER_PROJECT +unset DOPPLER_CONFIG cleanup() { exit_code=$? @@ -269,9 +271,9 @@ flags: update-check: false EOF "$DOPPLER_BINARY" setup --config-dir=$TEST_CONFIG_DIR --no-interactive -[[ "$("$DOPPLER_BINARY" configure flags get analytics --plain)" == 'false' ]] || error "ERROR: setup not setting disabled value for analytics" -[[ "$("$DOPPLER_BINARY" configure flags get env-warning --plain)" == 'false' ]] || error "ERROR: setup not setting disabled value for env-warning" -[[ "$("$DOPPLER_BINARY" configure flags get update-check --plain)" == 'false' ]] || error "ERROR: setup not setting disabled value for update-check" +[[ "$("$DOPPLER_BINARY" configure flags get analytics --config-dir=$TEST_CONFIG_DIR --plain)" == 'false' ]] || error "ERROR: setup not setting disabled value for analytics" +[[ "$("$DOPPLER_BINARY" configure flags get env-warning --config-dir=$TEST_CONFIG_DIR --plain)" == 'false' ]] || error "ERROR: setup not setting disabled value for env-warning" +[[ "$("$DOPPLER_BINARY" configure flags get update-check --config-dir=$TEST_CONFIG_DIR --plain)" == 'false' ]] || error "ERROR: setup not setting disabled value for update-check" afterEach From 2ae9ab9d4d7b480871d89dfe829ea796194f92b7 Mon Sep 17 00:00:00 2001 From: Nic Manoogian Date: Tue, 13 Feb 2024 17:25:58 -0500 Subject: [PATCH 09/11] chore: replace md5 with sha in tests --- tests/e2e/install-sh-update-in-place.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e/install-sh-update-in-place.sh b/tests/e2e/install-sh-update-in-place.sh index de0c7b73..b148ebf3 100755 --- a/tests/e2e/install-sh-update-in-place.sh +++ b/tests/e2e/install-sh-update-in-place.sh @@ -59,8 +59,8 @@ afterAll() { fi } -md5hash() { - md5 -rq $1 || md5sum $1 | awk '{print $1}' +filehash() { + sha256sum "$1" | awk '{print $1}' } ###################################################################### @@ -132,12 +132,12 @@ set +e mkdir "$TEMP_INSTALL_DIR" touch "$TEMP_INSTALL_DIR/doppler" && chmod +x "$TEMP_INSTALL_DIR/doppler" -empty_hash="$(md5hash "$TEMP_INSTALL_DIR/doppler")" +empty_hash="$(filehash "$TEMP_INSTALL_DIR/doppler")" output="$("$DOPPLER_SCRIPTS_DIR/install.sh" --no-package-manager 2>&1)" exit_code=$? set -e -updated_hash="$(md5hash "$TEMP_INSTALL_DIR/doppler")" +updated_hash="$(filehash "$TEMP_INSTALL_DIR/doppler")" installed_at="$(command -v doppler || true)" actual_dir="$(dirname "$installed_at")" From dde6f0ee0b36383ae7e2dc8085fdbdf680e2c0ca Mon Sep 17 00:00:00 2001 From: Nic Manoogian Date: Wed, 14 Feb 2024 19:15:08 -0500 Subject: [PATCH 10/11] chore: replace slices.Contains with utils.Contains We were getting the following gosec error when using `slices.Contains`: ``` could not import slices (invalid package name: "") ``` This is because: - `slices` was added in Go 1.21 - Support for Go 1.21 was added in gosec 2.17 - We are currently using gosec 2.15.0 and via salus 3.2.5 via federacy/scan-action which pulls `coinbase/salus:latest` Salus 3.2.6 has been released with the gosec version bump but it isn't tagged `latest`. I've filed https://github.com/coinbase/salus/issues/880 to address. For now, the easiest thing to do was just to not use the `slices` module, which was easy because we already have a util which does the same thing. --- pkg/configuration/flags.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/configuration/flags.go b/pkg/configuration/flags.go index 9d914098..53bc87cc 100644 --- a/pkg/configuration/flags.go +++ b/pkg/configuration/flags.go @@ -16,9 +16,8 @@ limitations under the License. package configuration import ( - "slices" - "github.com/DopplerHQ/cli/pkg/models" + "github.com/DopplerHQ/cli/pkg/utils" ) func GetFlag(flag string) bool { @@ -71,7 +70,7 @@ func GetFlagDefault(flag string) bool { func IsValidFlag(flag string) bool { flags := models.GetFlags() - return slices.Contains(flags, flag) + return utils.Contains(flags, flag) } func IsAnalyticsEnabled() bool { From e093993a40374c57d1b8cc6e14ea89b349b97273 Mon Sep 17 00:00:00 2001 From: Nic Manoogian Date: Wed, 14 Feb 2024 12:57:24 -0500 Subject: [PATCH 11/11] chore: update federacy/scan-action to 0.1.5 Updated initially to fix the gosec issue described in the previous commit but it didn't help. Kept the change anyway. --- .github/workflows/salus.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/salus.yaml b/.github/workflows/salus.yaml index 195966cb..c836f77f 100644 --- a/.github/workflows/salus.yaml +++ b/.github/workflows/salus.yaml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v2 - name: Salus Scan id: salus_scan - uses: federacy/scan-action@0.1.4 + uses: federacy/scan-action@0.1.5 env: SALUS_CONFIGURATION: "file://salus-config.yaml" with: