diff --git a/docs/stackit_config.md b/docs/stackit_config.md index 7a5d45296..a20ee0235 100644 --- a/docs/stackit_config.md +++ b/docs/stackit_config.md @@ -4,12 +4,12 @@ Provides functionality for CLI configuration options ### Synopsis -Provides functionality for CLI configuration options. -You can set and unset different configuration options via the "stackit config set" and "stackit config unset" commands. - -Additionally, you can configure the CLI to use different profiles, each with its own configuration. -Additional profiles can be configured via the "STACKIT_CLI_PROFILE" environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands. -The environment variable takes precedence over what is set via the commands. +Provides functionality for CLI configuration options +The configuration is stored in a file in the user's config directory, which is OS dependent. +Windows: %APPDATA%\stackit +Linux: $XDG_CONFIG_HOME/stackit +macOS: $HOME/Library/Application Support/stackit +The configuration file is named `cli-config.json` and is created automatically in your first CLI run. ``` stackit config [flags] @@ -35,7 +35,6 @@ stackit config [flags] * [stackit](./stackit.md) - Manage STACKIT resources using the command line * [stackit config list](./stackit_config_list.md) - Lists the current CLI configuration values -* [stackit config profile](./stackit_config_profile.md) - Manage the CLI configuration profiles * [stackit config set](./stackit_config_set.md) - Sets CLI configuration options * [stackit config unset](./stackit_config_unset.md) - Unsets CLI configuration options diff --git a/docs/stackit_config_profile.md b/docs/stackit_config_profile.md deleted file mode 100644 index 1c46aeb73..000000000 --- a/docs/stackit_config_profile.md +++ /dev/null @@ -1,37 +0,0 @@ -## stackit config profile - -Manage the CLI configuration profiles - -### Synopsis - -Manage the CLI configuration profiles. -The profile to be used can be managed via the "STACKIT_CLI_PROFILE" environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands. -The environment variable takes precedence over what is set via the commands. -When no profile is set, the default profile is used. - -``` -stackit config profile [flags] -``` - -### Options - -``` - -h, --help Help for "stackit config profile" -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, one of ["json" "pretty" "none"] - -p, --project-id string Project ID - --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") -``` - -### SEE ALSO - -* [stackit config](./stackit_config.md) - Provides functionality for CLI configuration options -* [stackit config profile set](./stackit_config_profile_set.md) - Set a CLI configuration profile -* [stackit config profile unset](./stackit_config_profile_unset.md) - Unset the current active CLI configuration profile - diff --git a/docs/stackit_config_profile_set.md b/docs/stackit_config_profile_set.md deleted file mode 100644 index bcc9725a6..000000000 --- a/docs/stackit_config_profile_set.md +++ /dev/null @@ -1,43 +0,0 @@ -## stackit config profile set - -Set a CLI configuration profile - -### Synopsis - -Set a CLI configuration profile as the active profile. -The profile to be used can be managed via the STACKIT_CLI_PROFILE environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands. -The environment variable takes precedence over what is set via the commands. -A new profile is created automatically if it does not exist. -When no profile is set, the default profile is used. - -``` -stackit config profile set PROFILE [flags] -``` - -### Examples - -``` - Set the configuration profile "my-profile" as the active profile - $ stackit config profile set my-profile -``` - -### Options - -``` - -h, --help Help for "stackit config profile set" -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, one of ["json" "pretty" "none"] - -p, --project-id string Project ID - --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") -``` - -### SEE ALSO - -* [stackit config profile](./stackit_config_profile.md) - Manage the CLI configuration profiles - diff --git a/docs/stackit_config_profile_unset.md b/docs/stackit_config_profile_unset.md deleted file mode 100644 index 0beda0f67..000000000 --- a/docs/stackit_config_profile_unset.md +++ /dev/null @@ -1,40 +0,0 @@ -## stackit config profile unset - -Unset the current active CLI configuration profile - -### Synopsis - -Unset the current active CLI configuration profile. -When no profile is set, the default profile will be used. - -``` -stackit config profile unset [flags] -``` - -### Examples - -``` - Unset the currently active configuration profile. The default profile will be used. - $ stackit config profile unset -``` - -### Options - -``` - -h, --help Help for "stackit config profile unset" -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, one of ["json" "pretty" "none"] - -p, --project-id string Project ID - --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") -``` - -### SEE ALSO - -* [stackit config profile](./stackit_config_profile.md) - Manage the CLI configuration profiles - diff --git a/docs/stackit_load-balancer_observability-credentials_cleanup.md b/docs/stackit_load-balancer_observability-credentials_cleanup.md index 6f72a76c2..f22f88994 100644 --- a/docs/stackit_load-balancer_observability-credentials_cleanup.md +++ b/docs/stackit_load-balancer_observability-credentials_cleanup.md @@ -35,4 +35,5 @@ stackit load-balancer observability-credentials cleanup [flags] ### SEE ALSO -- [stackit load-balancer observability-credentials](./stackit_load-balancer_observability-credentials.md) - Provides functionality for Load Balancer observability credentials +* [stackit load-balancer observability-credentials](./stackit_load-balancer_observability-credentials.md) - Provides functionality for Load Balancer observability credentials + diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index a96355a54..7c3fea56f 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/stackitcloud/stackit-cli/internal/cmd/config/list" - "github.com/stackitcloud/stackit-cli/internal/cmd/config/profile" "github.com/stackitcloud/stackit-cli/internal/cmd/config/set" "github.com/stackitcloud/stackit-cli/internal/cmd/config/unset" "github.com/stackitcloud/stackit-cli/internal/pkg/args" @@ -18,12 +17,12 @@ func NewCmd(p *print.Printer) *cobra.Command { cmd := &cobra.Command{ Use: "config", Short: "Provides functionality for CLI configuration options", - Long: fmt.Sprintf("%s\n%s\n\n%s\n%s\n%s", - "Provides functionality for CLI configuration options.", - `You can set and unset different configuration options via the "stackit config set" and "stackit config unset" commands.`, - "Additionally, you can configure the CLI to use different profiles, each with its own configuration.", - `Additional profiles can be configured via the "STACKIT_CLI_PROFILE" environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands.`, - "The environment variable takes precedence over what is set via the commands.", + Long: fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", "Provides functionality for CLI configuration options", + "The configuration is stored in a file in the user's config directory, which is OS dependent.", + "Windows: %APPDATA%\\stackit", + "Linux: $XDG_CONFIG_HOME/stackit", + "macOS: $HOME/Library/Application Support/stackit", + "The configuration file is named `cli-config.json` and is created automatically in your first CLI run.", ), Args: args.NoArgs, Run: utils.CmdHelp, @@ -36,5 +35,4 @@ func addSubcommands(cmd *cobra.Command, p *print.Printer) { cmd.AddCommand(list.NewCmd(p)) cmd.AddCommand(set.NewCmd(p)) cmd.AddCommand(unset.NewCmd(p)) - cmd.AddCommand(profile.NewCmd(p)) } diff --git a/internal/cmd/config/list/list.go b/internal/cmd/config/list/list.go index 8ba6160f4..560e1dd89 100644 --- a/internal/cmd/config/list/list.go +++ b/internal/cmd/config/list/list.go @@ -51,13 +51,7 @@ func NewCmd(p *print.Printer) *cobra.Command { configData := viper.AllSettings() model := parseInput(p, cmd) - - activeProfile, err := config.GetProfile() - if err != nil { - return fmt.Errorf("get profile: %w", err) - } - - return outputResult(p, model.OutputFormat, configData, activeProfile) + return outputResult(p, model.OutputFormat, configData) }, } return cmd @@ -71,12 +65,9 @@ func parseInput(p *print.Printer, cmd *cobra.Command) *inputModel { } } -func outputResult(p *print.Printer, outputFormat string, configData map[string]any, activeProfile string) error { +func outputResult(p *print.Printer, outputFormat string, configData map[string]any) error { switch outputFormat { case print.JSONOutputFormat: - if activeProfile != "" { - configData["profile"] = activeProfile - } details, err := json.MarshalIndent(configData, "", " ") if err != nil { return fmt.Errorf("marshal config list: %w", err) @@ -92,7 +83,6 @@ func outputResult(p *print.Printer, outputFormat string, configData map[string]a return nil default: - // Sort the config options by key configKeys := make([]string, 0, len(configData)) for k := range configData { @@ -101,9 +91,6 @@ func outputResult(p *print.Printer, outputFormat string, configData map[string]a sort.Strings(configKeys) table := tables.NewTable() - if activeProfile != "" { - table.SetTitle(fmt.Sprintf("Profile: %q", activeProfile)) - } table.SetHeader("NAME", "VALUE") for _, key := range configKeys { value := configData[key] diff --git a/internal/cmd/config/profile/profile.go b/internal/cmd/config/profile/profile.go deleted file mode 100644 index 42bea8e58..000000000 --- a/internal/cmd/config/profile/profile.go +++ /dev/null @@ -1,35 +0,0 @@ -package profile - -import ( - "fmt" - - "github.com/stackitcloud/stackit-cli/internal/cmd/config/profile/set" - "github.com/stackitcloud/stackit-cli/internal/cmd/config/profile/unset" - "github.com/stackitcloud/stackit-cli/internal/pkg/args" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/utils" - - "github.com/spf13/cobra" -) - -func NewCmd(p *print.Printer) *cobra.Command { - cmd := &cobra.Command{ - Use: "profile", - Short: "Manage the CLI configuration profiles", - Long: fmt.Sprintf("%s\n%s\n%s\n%s", - "Manage the CLI configuration profiles.", - `The profile to be used can be managed via the "STACKIT_CLI_PROFILE" environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands.`, - "The environment variable takes precedence over what is set via the commands.", - "When no profile is set, the default profile is used.", - ), - Args: args.NoArgs, - Run: utils.CmdHelp, - } - addSubcommands(cmd, p) - return cmd -} - -func addSubcommands(cmd *cobra.Command, p *print.Printer) { - cmd.AddCommand(set.NewCmd(p)) - cmd.AddCommand(unset.NewCmd(p)) -} diff --git a/internal/cmd/config/profile/set/set.go b/internal/cmd/config/profile/set/set.go deleted file mode 100644 index 3594883fe..000000000 --- a/internal/cmd/config/profile/set/set.go +++ /dev/null @@ -1,94 +0,0 @@ -package set - -import ( - "fmt" - - "github.com/stackitcloud/stackit-cli/internal/pkg/args" - "github.com/stackitcloud/stackit-cli/internal/pkg/auth" - "github.com/stackitcloud/stackit-cli/internal/pkg/config" - "github.com/stackitcloud/stackit-cli/internal/pkg/examples" - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - - "github.com/spf13/cobra" -) - -const ( - profileArg = "PROFILE" -) - -type inputModel struct { - *globalflags.GlobalFlagModel - Profile string -} - -func NewCmd(p *print.Printer) *cobra.Command { - cmd := &cobra.Command{ - Use: fmt.Sprintf("set %s", profileArg), - Short: "Set a CLI configuration profile", - Long: fmt.Sprintf("%s\n%s\n%s\n%s\n%s", - "Set a CLI configuration profile as the active profile.", - `The profile to be used can be managed via the STACKIT_CLI_PROFILE environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands.`, - "The environment variable takes precedence over what is set via the commands.", - "A new profile is created automatically if it does not exist.", - "When no profile is set, the default profile is used.", - ), - Args: args.SingleArg(profileArg, nil), - Example: examples.Build( - examples.NewExample( - `Set the configuration profile "my-profile" as the active profile`, - "$ stackit config profile set my-profile"), - ), - RunE: func(cmd *cobra.Command, args []string) error { - model, err := parseInput(p, cmd, args) - if err != nil { - return err - } - - err = config.SetProfile(p, model.Profile) - if err != nil { - return fmt.Errorf("set profile: %w", err) - } - - p.Info("Successfully set active profile to %q\n", model.Profile) - - flow, err := auth.GetAuthFlow() - if err != nil { - p.Debug(print.WarningLevel, "both keyring and text file storage failed to find a valid authentication flow for the active profile") - p.Warn("The active profile %q is not authenticated, please login using the 'stackit auth login' command.\n", model.Profile) - return nil - } - p.Debug(print.DebugLevel, "found valid authentication flow for active profile: %s", flow) - - return nil - }, - } - return cmd -} - -func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { - profile := inputArgs[0] - - err := config.ValidateProfile(profile) - if err != nil { - return nil, err - } - - globalFlags := globalflags.Parse(p, cmd) - - model := inputModel{ - GlobalFlagModel: globalFlags, - Profile: profile, - } - - if p.IsVerbosityDebug() { - modelStr, err := print.BuildDebugStrFromInputModel(model) - if err != nil { - p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) - } else { - p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) - } - } - - return &model, nil -} diff --git a/internal/cmd/config/profile/set/set_test.go b/internal/cmd/config/profile/set/set_test.go deleted file mode 100644 index 47f56ca0b..000000000 --- a/internal/cmd/config/profile/set/set_test.go +++ /dev/null @@ -1,132 +0,0 @@ -package set - -import ( - "testing" - - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - - "github.com/google/go-cmp/cmp" -) - -const testProfile = "test-profile" - -func fixtureArgValues(mods ...func(argValues []string)) []string { - argValues := []string{ - testProfile, - } - for _, mod := range mods { - mod(argValues) - } - return argValues -} - -func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { - model := &inputModel{ - GlobalFlagModel: &globalflags.GlobalFlagModel{ - Verbosity: globalflags.VerbosityDefault, - }, - Profile: testProfile, - } - for _, mod := range mods { - mod(model) - } - return model -} - -func TestParseInput(t *testing.T) { - tests := []struct { - description string - argValues []string - flagValues map[string]string - isValid bool - expectedModel *inputModel - }{ - { - description: "base", - argValues: fixtureArgValues(), - isValid: true, - expectedModel: fixtureInputModel(), - }, - { - description: "no values", - argValues: []string{}, - flagValues: map[string]string{}, - isValid: false, - }, - { - description: "no arg values", - argValues: []string{}, - isValid: false, - }, - { - description: "some global flag", - argValues: fixtureArgValues(), - flagValues: map[string]string{ - globalflags.VerbosityFlag: globalflags.DebugVerbosity, - }, - isValid: true, - expectedModel: fixtureInputModel(func(model *inputModel) { - model.GlobalFlagModel.Verbosity = globalflags.DebugVerbosity - }), - }, - { - description: "invalid profile", - argValues: []string{"invalid-profile-&"}, - isValid: false, - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - p := print.NewPrinter() - cmd := NewCmd(p) - err := globalflags.Configure(cmd.Flags()) - if err != nil { - t.Fatalf("configure global flags: %v", err) - } - - for flag, value := range tt.flagValues { - err := cmd.Flags().Set(flag, value) - if err != nil { - if !tt.isValid { - return - } - t.Fatalf("setting flag --%s=%s: %v", flag, value, err) - } - } - - err = cmd.ValidateArgs(tt.argValues) - if err != nil { - if !tt.isValid { - return - } - t.Fatalf("error validating args: %v", err) - } - - err = cmd.ValidateRequiredFlags() - if err != nil { - if !tt.isValid { - return - } - t.Fatalf("error validating flags: %v", err) - } - - model, err := parseInput(p, cmd, tt.argValues) - if err != nil { - if !tt.isValid { - return - } - t.Fatalf("error parsing input: %v", err) - } - - if !tt.isValid { - t.Fatalf("did not fail on invalid input") - } - diff := cmp.Diff(model, tt.expectedModel) - if diff != "" { - t.Fatalf("Data does not match: %s", diff) - } - }) - } -} diff --git a/internal/cmd/config/profile/unset/unset.go b/internal/cmd/config/profile/unset/unset.go deleted file mode 100644 index f767cbfe4..000000000 --- a/internal/cmd/config/profile/unset/unset.go +++ /dev/null @@ -1,49 +0,0 @@ -package unset - -import ( - "fmt" - - "github.com/stackitcloud/stackit-cli/internal/pkg/args" - "github.com/stackitcloud/stackit-cli/internal/pkg/auth" - "github.com/stackitcloud/stackit-cli/internal/pkg/config" - "github.com/stackitcloud/stackit-cli/internal/pkg/examples" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - - "github.com/spf13/cobra" -) - -func NewCmd(p *print.Printer) *cobra.Command { - cmd := &cobra.Command{ - Use: "unset", - Short: "Unset the current active CLI configuration profile", - Long: fmt.Sprintf("%s\n%s", - "Unset the current active CLI configuration profile.", - "When no profile is set, the default profile will be used.", - ), - Args: args.NoArgs, - Example: examples.Build( - examples.NewExample( - `Unset the currently active configuration profile. The default profile will be used.`, - "$ stackit config profile unset"), - ), - RunE: func(cmd *cobra.Command, args []string) error { - err := config.UnsetProfile(p) - if err != nil { - return fmt.Errorf("unset profile: %w", err) - } - - p.Info("Profile unset successfully. The default profile will be used.\n") - - flow, err := auth.GetAuthFlow() - if err != nil { - p.Debug(print.WarningLevel, "both keyring and text file storage failed to find a valid authentication flow for the active profile") - p.Warn("The default profile is not authenticated, please login using the 'stackit auth login' command.\n") - return nil - } - p.Debug(print.DebugLevel, "found valid authentication flow for active profile: %s", flow) - - return nil - }, - } - return cmd -} diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 8f4193142..5845d0c80 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -8,7 +8,7 @@ import ( "github.com/stackitcloud/stackit-cli/internal/cmd/argus" "github.com/stackitcloud/stackit-cli/internal/cmd/auth" - configCmd "github.com/stackitcloud/stackit-cli/internal/cmd/config" + "github.com/stackitcloud/stackit-cli/internal/cmd/config" "github.com/stackitcloud/stackit-cli/internal/cmd/curl" "github.com/stackitcloud/stackit-cli/internal/cmd/dns" loadbalancer "github.com/stackitcloud/stackit-cli/internal/cmd/load-balancer" @@ -26,7 +26,6 @@ import ( serviceaccount "github.com/stackitcloud/stackit-cli/internal/cmd/service-account" "github.com/stackitcloud/stackit-cli/internal/cmd/ske" "github.com/stackitcloud/stackit-cli/internal/pkg/args" - "github.com/stackitcloud/stackit-cli/internal/pkg/config" "github.com/stackitcloud/stackit-cli/internal/pkg/errors" "github.com/stackitcloud/stackit-cli/internal/pkg/flags" "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" @@ -45,7 +44,7 @@ func NewRootCmd(version, date string, p *print.Printer) *cobra.Command { SilenceErrors: true, // Error is beautified in a custom way before being printed SilenceUsage: true, DisableAutoGenTag: true, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + PersistentPreRun: func(cmd *cobra.Command, args []string) { p.Cmd = cmd p.Verbosity = print.Level(globalflags.Parse(p, cmd).Verbosity) @@ -53,22 +52,11 @@ func NewRootCmd(version, date string, p *print.Printer) *cobra.Command { p.Debug(print.DebugLevel, "arguments: %s", argsString) configFilePath := viper.ConfigFileUsed() - p.Debug(print.DebugLevel, "configuration is persisted and read from: %s", configFilePath) - - activeProfile, err := config.GetProfile() - if err != nil { - return fmt.Errorf("get profile: %w", err) - } - if activeProfile == "" { - activeProfile = "(no active profile, the default profile configuration will be used)" - } - p.Debug(print.DebugLevel, "active configuration profile: %s", activeProfile) + p.Debug(print.DebugLevel, "using config file: %s", configFilePath) configKeys := viper.AllSettings() configKeysStr := print.BuildDebugStrFromMap(configKeys) - p.Debug(print.DebugLevel, "configuration keys: %s", configKeysStr) - - return nil + p.Debug(print.DebugLevel, "config keys: %s", configKeysStr) }, RunE: func(cmd *cobra.Command, args []string) error { if flags.FlagToBoolValue(p, cmd, "version") { @@ -116,7 +104,7 @@ func configureFlags(cmd *cobra.Command) error { func addSubcommands(cmd *cobra.Command, p *print.Printer) { cmd.AddCommand(argus.NewCmd(p)) cmd.AddCommand(auth.NewCmd(p)) - cmd.AddCommand(configCmd.NewCmd(p)) + cmd.AddCommand(config.NewCmd(p)) cmd.AddCommand(curl.NewCmd(p)) cmd.AddCommand(dns.NewCmd(p)) cmd.AddCommand(loadbalancer.NewCmd(p)) diff --git a/internal/pkg/auth/storage.go b/internal/pkg/auth/storage.go index 73bef1189..9d08ca070 100644 --- a/internal/pkg/auth/storage.go +++ b/internal/pkg/auth/storage.go @@ -7,8 +7,6 @@ import ( "os" "path/filepath" - "github.com/stackitcloud/stackit-cli/internal/pkg/config" - "github.com/zalando/go-keyring" ) @@ -60,14 +58,9 @@ func SetAuthFieldMap(keyMap map[authFieldKey]string) error { } func SetAuthField(key authFieldKey, value string) error { - activeProfile, err := config.GetProfile() + err := setAuthFieldInKeyring(key, value) if err != nil { - return fmt.Errorf("get profile: %w", err) - } - - err = setAuthFieldInKeyring(activeProfile, key, value) - if err != nil { - errFallback := setAuthFieldInEncodedTextFile(activeProfile, key, value) + errFallback := setAuthFieldInEncodedTextFile(key, value) if errFallback != nil { return fmt.Errorf("write to keyring failed (%w), try writing to encoded text file: %w", err, errFallback) } @@ -75,16 +68,12 @@ func SetAuthField(key authFieldKey, value string) error { return nil } -func setAuthFieldInKeyring(activeProfile string, key authFieldKey, value string) error { - if activeProfile != "" { - activeProfileKeyring := filepath.Join(keyringService, activeProfile) - return keyring.Set(activeProfileKeyring, string(key), value) - } +func setAuthFieldInKeyring(key authFieldKey, value string) error { return keyring.Set(keyringService, string(key), value) } -func setAuthFieldInEncodedTextFile(activeProfile string, key authFieldKey, value string) error { - err := createEncodedTextFile(activeProfile) +func setAuthFieldInEncodedTextFile(key authFieldKey, value string) error { + err := createEncodedTextFile() if err != nil { return err } @@ -93,13 +82,7 @@ func setAuthFieldInEncodedTextFile(activeProfile string, key authFieldKey, value if err != nil { return fmt.Errorf("get config dir: %w", err) } - - profileTextFileFolderName := textFileFolderName - if activeProfile != "" { - profileTextFileFolderName = filepath.Join(textFileFolderName, activeProfile) - } - - textFileDir := filepath.Join(configDir, profileTextFileFolderName) + textFileDir := filepath.Join(configDir, textFileFolderName) textFilePath := filepath.Join(textFileDir, textFileName) contentEncoded, err := os.ReadFile(textFilePath) @@ -148,15 +131,10 @@ func GetAuthFlow() (AuthFlow, error) { } func GetAuthField(key authFieldKey) (string, error) { - activeProfile, err := config.GetProfile() - if err != nil { - return "", fmt.Errorf("get profile: %w", err) - } - - value, err := getAuthFieldFromKeyring(activeProfile, key) + value, err := getAuthFieldFromKeyring(key) if err != nil { var errFallback error - value, errFallback = getAuthFieldFromEncodedTextFile(activeProfile, key) + value, errFallback = getAuthFieldFromEncodedTextFile(key) if errFallback != nil { return "", fmt.Errorf("read from keyring: %w, read from encoded file as fallback: %w", err, errFallback) } @@ -164,16 +142,12 @@ func GetAuthField(key authFieldKey) (string, error) { return value, nil } -func getAuthFieldFromKeyring(activeProfile string, key authFieldKey) (string, error) { - if activeProfile != "" { - activeProfileKeyring := filepath.Join(keyringService, activeProfile) - return keyring.Get(activeProfileKeyring, string(key)) - } +func getAuthFieldFromKeyring(key authFieldKey) (string, error) { return keyring.Get(keyringService, string(key)) } -func getAuthFieldFromEncodedTextFile(activeProfile string, key authFieldKey) (string, error) { - err := createEncodedTextFile(activeProfile) +func getAuthFieldFromEncodedTextFile(key authFieldKey) (string, error) { + err := createEncodedTextFile() if err != nil { return "", err } @@ -182,13 +156,7 @@ func getAuthFieldFromEncodedTextFile(activeProfile string, key authFieldKey) (st if err != nil { return "", fmt.Errorf("get config dir: %w", err) } - - profileTextFileFolderName := textFileFolderName - if activeProfile != "" { - profileTextFileFolderName = filepath.Join(textFileFolderName, activeProfile) - } - - textFileDir := filepath.Join(configDir, profileTextFileFolderName) + textFileDir := filepath.Join(configDir, textFileFolderName) textFilePath := filepath.Join(textFileDir, textFileName) contentEncoded, err := os.ReadFile(textFilePath) @@ -214,18 +182,12 @@ func getAuthFieldFromEncodedTextFile(activeProfile string, key authFieldKey) (st // Checks if the encoded text file exist. // If it doesn't, creates it with the content "{}" encoded. // If it does, does nothing (and returns nil). -func createEncodedTextFile(activeProfile string) error { +func createEncodedTextFile() error { configDir, err := os.UserConfigDir() if err != nil { return fmt.Errorf("get config dir: %w", err) } - - profileTextFileFolderName := textFileFolderName - if activeProfile != "" { - profileTextFileFolderName = filepath.Join(textFileFolderName, activeProfile) - } - - textFileDir := filepath.Join(configDir, profileTextFileFolderName) + textFileDir := filepath.Join(configDir, textFileFolderName) textFilePath := filepath.Join(textFileDir, textFileName) err = os.MkdirAll(textFileDir, os.ModePerm) diff --git a/internal/pkg/auth/storage_test.go b/internal/pkg/auth/storage_test.go index 4d1593020..1d3d4dab9 100644 --- a/internal/pkg/auth/storage_test.go +++ b/internal/pkg/auth/storage_test.go @@ -10,8 +10,6 @@ import ( "time" "github.com/zalando/go-keyring" - - "github.com/stackitcloud/stackit-cli/internal/pkg/config" ) func TestSetGetAuthField(t *testing.T) { @@ -115,11 +113,6 @@ func TestSetGetAuthField(t *testing.T) { for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - activeProfile, err := config.GetProfile() - if err != nil { - t.Errorf("get profile: %v", err) - } - if !tt.keyringFails { keyring.MockInit() } else { @@ -147,12 +140,12 @@ func TestSetGetAuthField(t *testing.T) { } if !tt.keyringFails { - err = deleteAuthFieldInKeyring(activeProfile, key) + err = deleteAuthFieldInKeyring(key) if err != nil { t.Errorf("Post-test cleanup failed: remove field \"%s\" from keyring: %v. Please remove it manually", key, err) } } else { - err = deleteAuthFieldInEncodedTextFile(activeProfile, key) + err = deleteAuthFieldInEncodedTextFile(key) if err != nil { t.Errorf("Post-test cleanup failed: remove field \"%s\" from text file: %v. Please remove it manually", key, err) } @@ -179,51 +172,9 @@ func TestSetGetAuthFieldKeyring(t *testing.T) { description string valueAssignments []valueAssignment expectedValues map[authFieldKey]string - activeProfile string }{ { - description: "simple assignments with default profile", - activeProfile: "", - valueAssignments: []valueAssignment{ - { - key: testField1, - value: testValue1, - }, - { - key: testField2, - value: testValue2, - }, - }, - expectedValues: map[authFieldKey]string{ - testField1: testValue1, - testField2: testValue2, - }, - }, - { - description: "overlapping assignments with default profile", - activeProfile: "", - valueAssignments: []valueAssignment{ - { - key: testField1, - value: testValue1, - }, - { - key: testField2, - value: testValue2, - }, - { - key: testField1, - value: testValue3, - }, - }, - expectedValues: map[authFieldKey]string{ - testField1: testValue3, - testField2: testValue2, - }, - }, - { - description: "simple assignments with testProfile", - activeProfile: "testProfile", + description: "simple assignments", valueAssignments: []valueAssignment{ { key: testField1, @@ -240,8 +191,7 @@ func TestSetGetAuthFieldKeyring(t *testing.T) { }, }, { - description: "overlapping assignments with testProfile", - activeProfile: "testProfile", + description: "overlapping assignments", valueAssignments: []valueAssignment{ { key: testField1, @@ -268,7 +218,7 @@ func TestSetGetAuthFieldKeyring(t *testing.T) { keyring.MockInit() for _, assignment := range tt.valueAssignments { - err := setAuthFieldInKeyring(tt.activeProfile, assignment.key, assignment.value) + err := setAuthFieldInKeyring(assignment.key, assignment.value) if err != nil { t.Fatalf("Failed to set \"%s\" as \"%s\": %v", assignment.key, assignment.value, err) } @@ -279,7 +229,7 @@ func TestSetGetAuthFieldKeyring(t *testing.T) { } for key, valueExpected := range tt.expectedValues { - value, err := getAuthFieldFromKeyring(tt.activeProfile, key) + value, err := getAuthFieldFromKeyring(key) if err != nil { t.Errorf("Failed to get value of \"%s\": %v", key, err) continue @@ -287,7 +237,7 @@ func TestSetGetAuthFieldKeyring(t *testing.T) { t.Errorf("Value of field \"%s\" is wrong: expected \"%s\", got \"%s\"", key, valueExpected, value) } - err = deleteAuthFieldInKeyring(tt.activeProfile, key) + err = deleteAuthFieldInKeyring(key) if err != nil { t.Errorf("Post-test cleanup failed: remove field \"%s\" from keyring: %v. Please remove it manually", key, err) } @@ -311,53 +261,11 @@ func TestSetGetAuthFieldEncodedTextFile(t *testing.T) { tests := []struct { description string - activeProfile string valueAssignments []valueAssignment expectedValues map[authFieldKey]string }{ { - description: "simple assignments with default profile", - activeProfile: "", - valueAssignments: []valueAssignment{ - { - key: testField1, - value: testValue1, - }, - { - key: testField2, - value: testValue2, - }, - }, - expectedValues: map[authFieldKey]string{ - testField1: testValue1, - testField2: testValue2, - }, - }, - { - description: "overlapping assignments with default profile", - activeProfile: "", - valueAssignments: []valueAssignment{ - { - key: testField1, - value: testValue1, - }, - { - key: testField2, - value: testValue2, - }, - { - key: testField1, - value: testValue3, - }, - }, - expectedValues: map[authFieldKey]string{ - testField1: testValue3, - testField2: testValue2, - }, - }, - { - description: "simple assignments with testProfile", - activeProfile: "testProfile", + description: "simple assignments", valueAssignments: []valueAssignment{ { key: testField1, @@ -374,8 +282,7 @@ func TestSetGetAuthFieldEncodedTextFile(t *testing.T) { }, }, { - description: "overlapping assignments with testProfile", - activeProfile: "testProfile", + description: "overlapping assignments", valueAssignments: []valueAssignment{ { key: testField1, @@ -400,7 +307,7 @@ func TestSetGetAuthFieldEncodedTextFile(t *testing.T) { for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { for _, assignment := range tt.valueAssignments { - err := setAuthFieldInEncodedTextFile(tt.activeProfile, assignment.key, assignment.value) + err := setAuthFieldInEncodedTextFile(assignment.key, assignment.value) if err != nil { t.Fatalf("Failed to set \"%s\" as \"%s\": %v", assignment.key, assignment.value, err) } @@ -411,7 +318,7 @@ func TestSetGetAuthFieldEncodedTextFile(t *testing.T) { } for key, valueExpected := range tt.expectedValues { - value, err := getAuthFieldFromEncodedTextFile(tt.activeProfile, key) + value, err := getAuthFieldFromEncodedTextFile(key) if err != nil { t.Errorf("Failed to get value of \"%s\": %v", key, err) continue @@ -419,7 +326,7 @@ func TestSetGetAuthFieldEncodedTextFile(t *testing.T) { t.Errorf("Value of field \"%s\" is wrong: expected \"%s\", got \"%s\"", key, valueExpected, value) } - err = deleteAuthFieldInEncodedTextFile(tt.activeProfile, key) + err = deleteAuthFieldInEncodedTextFile(key) if err != nil { t.Errorf("Post-test cleanup failed: remove field \"%s\" from text file: %v. Please remove it manually", key, err) } @@ -428,17 +335,12 @@ func TestSetGetAuthFieldEncodedTextFile(t *testing.T) { } } -func deleteAuthFieldInKeyring(activeProfile string, key authFieldKey) error { - if activeProfile != "" { - activeProfileKeyring := filepath.Join(keyringService, activeProfile) - return keyring.Delete(activeProfileKeyring, string(key)) - } - +func deleteAuthFieldInKeyring(key authFieldKey) error { return keyring.Delete(keyringService, string(key)) } -func deleteAuthFieldInEncodedTextFile(activeProfile string, key authFieldKey) error { - err := createEncodedTextFile(activeProfile) +func deleteAuthFieldInEncodedTextFile(key authFieldKey) error { + err := createEncodedTextFile() if err != nil { return err } @@ -447,13 +349,7 @@ func deleteAuthFieldInEncodedTextFile(activeProfile string, key authFieldKey) er if err != nil { return fmt.Errorf("get config dir: %w", err) } - - profileTextFileFolderName := textFileFolderName - if activeProfile != "" { - profileTextFileFolderName = filepath.Join(textFileFolderName, activeProfile) - } - - textFileDir := filepath.Join(configDir, profileTextFileFolderName) + textFileDir := filepath.Join(configDir, textFileFolderName) textFilePath := filepath.Join(textFileDir, textFileName) contentEncoded, err := os.ReadFile(textFilePath) diff --git a/internal/pkg/cache/cache.go b/internal/pkg/cache/cache.go index d52670fd4..abc81787d 100644 --- a/internal/pkg/cache/cache.go +++ b/internal/pkg/cache/cache.go @@ -43,7 +43,7 @@ func PutObject(identifier string, data []byte) error { return ErrorInvalidCacheIdentifier } - err := os.MkdirAll(cacheFolderPath, os.ModePerm) + err := createFolderIfNotExists(cacheFolderPath) if err != nil { return err } @@ -65,6 +65,19 @@ func DeleteObject(identifier string) error { return nil } +func createFolderIfNotExists(folderPath string) error { + _, err := os.Stat(folderPath) + if os.IsNotExist(err) { + err := os.MkdirAll(folderPath, os.ModePerm) + if err != nil { + return err + } + } else if err != nil { + return err + } + return nil +} + func validateCacheFolderPath() error { if cacheFolderPath == "" { return errors.New("cacheFolderPath not set. Forgot to call Init()?") diff --git a/internal/pkg/cache/cache_test.go b/internal/pkg/cache/cache_test.go index 4ea003116..9cdf34ce4 100644 --- a/internal/pkg/cache/cache_test.go +++ b/internal/pkg/cache/cache_test.go @@ -46,7 +46,7 @@ func TestGetObject(t *testing.T) { // setup if tt.expectFile { - err := os.MkdirAll(cacheFolderPath, os.ModePerm) + err := createFolderIfNotExists(cacheFolderPath) if err != nil { t.Fatalf("create cache folder: %s", err.Error()) } diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index b9abf9b31..fa367857d 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -34,20 +34,16 @@ const ( ServiceAccountCustomEndpointKey = "service_account_custom_endpoint" SKECustomEndpointKey = "ske_custom_endpoint" - ProjectNameKey = "project_name" - AsyncDefault = false SessionTimeLimitDefault = "2h" ) +// Backend config keys const ( - configFolder = "stackit" - + configFolder = "stackit" configFileName = "cli-config" configFileExtension = "json" - - profileFileName = "cli-profile" - profileFileExtension = "txt" + ProjectNameKey = "project_name" ) var ConfigKeys = []string{ @@ -76,27 +72,17 @@ var ConfigKeys = []string{ SKECustomEndpointKey, } -var defaultConfigFolderPath string -var configFolderPath string -var profileFilePath string +var folderPath string func InitConfig() { configDir, err := os.UserConfigDir() cobra.CheckErr(err) - - defaultConfigFolderPath = filepath.Join(configDir, configFolder) - profileFilePath = filepath.Join(defaultConfigFolderPath, fmt.Sprintf("%s.%s", profileFileName, profileFileExtension)) // Profile file path is in the default config folder - - configProfile, err := GetProfile() - cobra.CheckErr(err) - - configFolderPath = defaultConfigFolderPath - if configProfile != "" { - configFolderPath = filepath.Join(configFolderPath, configProfile) // If a profile is set, use the profile config folder - } - + configFolderPath := filepath.Join(configDir, configFolder) configFilePath := filepath.Join(configFolderPath, fmt.Sprintf("%s.%s", configFileName, configFileExtension)) + // Write config dir path to global variable + folderPath = configFolderPath + // This hack is required to allow creating the config file with `viper.WriteConfig` // see https://github.com/spf13/viper/issues/851#issuecomment-789393451 viper.SetConfigFile(configFilePath) @@ -121,10 +107,22 @@ func InitConfig() { viper.SetEnvPrefix("stackit") } +func createFolderIfNotExists() error { + _, err := os.Stat(folderPath) + if os.IsNotExist(err) { + err := os.MkdirAll(folderPath, os.ModePerm) + if err != nil { + return err + } + } else if err != nil { + return err + } + return nil +} + // Write saves the config file (wrapping `viper.WriteConfig`) and ensures that its directory exists func Write() error { - err := os.MkdirAll(configFolderPath, os.ModePerm) - if err != nil { + if err := createFolderIfNotExists(); err != nil { return fmt.Errorf("create config directory: %w", err) } return viper.WriteConfig() diff --git a/internal/pkg/config/file_utils.go b/internal/pkg/config/file_utils.go deleted file mode 100644 index 0895a9dc5..000000000 --- a/internal/pkg/config/file_utils.go +++ /dev/null @@ -1,26 +0,0 @@ -package config - -import ( - "fmt" - "os" -) - -// readFileIfExists reads the contents of a file and returns it as a string, along with a boolean indicating if the file exists. -// If the file does not exist, it returns an empty string and no error. -// If the file exists but cannot be read, it returns an error. -func readFileIfExists(filePath string) (contents string, exists bool, err error) { - _, err = os.Stat(filePath) - if err != nil { - if os.IsNotExist(err) { - return "", false, nil - } - return "", true, err - } - - content, err := os.ReadFile(filePath) - if err != nil { - return "", true, fmt.Errorf("read file: %w", err) - } - - return string(content), true, nil -} diff --git a/internal/pkg/config/file_utils_test.go b/internal/pkg/config/file_utils_test.go deleted file mode 100644 index d7f299cb6..000000000 --- a/internal/pkg/config/file_utils_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package config - -import ( - "testing" -) - -func TestReadFileIfExists(t *testing.T) { - tests := []struct { - description string - filePath string - exists bool - content string - }{ - { - description: "file exists", - filePath: "test-data/file-with-content.txt", - exists: true, - content: "my-content", - }, - { - description: "file does not exist", - filePath: "test-data/file-does-not-exist.txt", - content: "", - }, - { - description: "empty file", - filePath: "test-data/empty-file.txt", - exists: true, - content: "", - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - content, exists, err := readFileIfExists(tt.filePath) - if err != nil { - t.Errorf("read file: %v", err) - } - if exists != tt.exists { - t.Errorf("expected exists to be %t but got %t", tt.exists, exists) - } - if content != tt.content { - t.Errorf("expected content to be %q but got %q", tt.content, content) - } - }) - } -} diff --git a/internal/pkg/config/profiles.go b/internal/pkg/config/profiles.go deleted file mode 100644 index ff07fbd7b..000000000 --- a/internal/pkg/config/profiles.go +++ /dev/null @@ -1,90 +0,0 @@ -package config - -import ( - "fmt" - "os" - "path/filepath" - "regexp" - - "github.com/stackitcloud/stackit-cli/internal/pkg/errors" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" -) - -// GetProfile returns the current profile to be used by the CLI. -// -// The profile is determined by the value of the STACKIT_CLI_PROFILE environment variable, or, if not set, -// by the contents of the profile file in the CLI config folder. -// -// If the environment variable is not set and the profile file does not exist, it returns an empty string. -// -// If the profile is not valid, it returns an error. -func GetProfile() (string, error) { - profile, profileSet := os.LookupEnv("STACKIT_CLI_PROFILE") - if !profileSet { - contents, exists, err := readFileIfExists(profileFilePath) - if err != nil { - return "", fmt.Errorf("read profile from file: %w", err) - } - if !exists { - return "", nil - } - profile = contents - } - - err := ValidateProfile(profile) - if err != nil { - return "", fmt.Errorf("validate profile: %w", err) - } - return profile, nil -} - -// SetProfile sets the profile to be used by the CLI. -func SetProfile(p *print.Printer, profile string) error { - err := ValidateProfile(profile) - if err != nil { - return fmt.Errorf("validate profile: %w", err) - } - - err = os.WriteFile(profileFilePath, []byte(profile), os.ModePerm) - if err != nil { - return fmt.Errorf("write profile to file: %w", err) - } - p.Debug(print.DebugLevel, "persisted new active profile in: %s", profileFilePath) - - configFolderPath = filepath.Join(defaultConfigFolderPath, profile) - err = os.MkdirAll(configFolderPath, os.ModePerm) - if err != nil { - return fmt.Errorf("create config folder: %w", err) - } - p.Debug(print.DebugLevel, "created folder for the new profile: %s", configFolderPath) - p.Debug(print.DebugLevel, "profile %q is now active", profile) - - return nil -} - -// UnsetProfile removes the profile file. -// If the profile file does not exist, it does nothing. -func UnsetProfile(p *print.Printer) error { - err := os.Remove(profileFilePath) - if err != nil && !os.IsNotExist(err) { - return fmt.Errorf("remove profile file: %w", err) - } - p.Debug(print.DebugLevel, "removed active profile file: %s", profileFilePath) - return nil -} - -// ValidateProfile validates the profile name. -// It can only use letters, numbers, or "-" and cannot be empty. -// If the profile is invalid, it returns an error. -func ValidateProfile(profile string) error { - match, err := regexp.MatchString("^[a-zA-Z0-9-]+$", profile) - if err != nil { - return fmt.Errorf("match string regex: %w", err) - } - if !match { - return &errors.InvalidProfileNameError{ - Profile: profile, - } - } - return nil -} diff --git a/internal/pkg/config/profiles_test.go b/internal/pkg/config/profiles_test.go deleted file mode 100644 index e97451aeb..000000000 --- a/internal/pkg/config/profiles_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package config - -import "testing" - -func TestValidateProfile(t *testing.T) { - tests := []struct { - description string - profile string - isValid bool - }{ - { - description: "valid profile with letters", - profile: "myprofile", - isValid: true, - }, - { - description: "valid profile with uppercase letters", - profile: "myProfile", - isValid: true, - }, - { - description: "valid with letters and hyphen", - profile: "my-profile", - isValid: true, - }, - { - description: "valid with letters, numbers, and hyphen", - profile: "my-profile-123", - isValid: true, - }, - { - description: "invalid empty", - profile: "", - isValid: false, - }, - { - description: "invalid with special characters", - profile: "my_profile", - isValid: false, - }, - { - description: "invalid with spaces", - profile: "my profile", - isValid: false, - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - err := ValidateProfile(tt.profile) - if tt.isValid && err != nil { - t.Errorf("expected profile to be valid but got error: %v", err) - } - if !tt.isValid && err == nil { - t.Errorf("expected profile to be invalid but got no error") - } - }) - } -} diff --git a/internal/pkg/config/test-data/empty-file.txt b/internal/pkg/config/test-data/empty-file.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/pkg/config/test-data/file-with-content.txt b/internal/pkg/config/test-data/file-with-content.txt deleted file mode 100644 index d89fbd214..000000000 --- a/internal/pkg/config/test-data/file-with-content.txt +++ /dev/null @@ -1 +0,0 @@ -my-content \ No newline at end of file diff --git a/internal/pkg/config/test-data/folder-exists/dummy.txt b/internal/pkg/config/test-data/folder-exists/dummy.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/pkg/errors/errors.go b/internal/pkg/errors/errors.go index 1cfe6bce8..6b127bf67 100644 --- a/internal/pkg/errors/errors.go +++ b/internal/pkg/errors/errors.go @@ -115,10 +115,6 @@ For more details on the available storages for the configured flavor (%[3]s), ru SUBCOMMAND_MISSING = `missing subcommand` - INVALID_PROFILE_NAME = `the profile name %q is invalid. - -The profile name can only contain letters, numbers, and "-" and cannot be empty.` - USAGE_TIP = `For usage help, run: $ %s --help` ) @@ -318,11 +314,3 @@ func AppendUsageTip(err error, cmd *cobra.Command) error { tip := fmt.Sprintf(USAGE_TIP, cmd.CommandPath()) return fmt.Errorf("%w.\n\n%s", err, tip) } - -type InvalidProfileNameError struct { - Profile string -} - -func (e *InvalidProfileNameError) Error() string { - return fmt.Sprintf(INVALID_PROFILE_NAME, e.Profile) -} diff --git a/internal/pkg/print/print.go b/internal/pkg/print/print.go index 649590c7c..205836393 100644 --- a/internal/pkg/print/print.go +++ b/internal/pkg/print/print.go @@ -15,7 +15,7 @@ import ( "github.com/mattn/go-colorable" "github.com/spf13/cobra" "github.com/spf13/viper" - + "github.com/stackitcloud/stackit-cli/internal/pkg/config" "golang.org/x/term" ) @@ -27,10 +27,6 @@ const ( WarningLevel Level = "warning" ErrorLevel Level = "error" - // Needed to avoid import cycle - // Originally defined in "internal/pkg/config/config.go" - outputFormatKey = "output-format" - JSONOutputFormat = "json" PrettyOutputFormat = "pretty" NoneOutputFormat = "none" @@ -58,7 +54,7 @@ func NewPrinter() *Printer { // Print an output using Printf to the defined output (falling back to Stderr if not set). // If output format is set to none, it does nothing func (p *Printer) Outputf(msg string, args ...any) { - outputFormat := viper.GetString(outputFormatKey) + outputFormat := viper.GetString(config.OutputFormatKey) if outputFormat == NoneOutputFormat { return } @@ -68,7 +64,7 @@ func (p *Printer) Outputf(msg string, args ...any) { // Print an output using Println to the defined output (falling back to Stderr if not set). // If output format is set to none, it does nothing func (p *Printer) Outputln(msg string) { - outputFormat := viper.GetString(outputFormatKey) + outputFormat := viper.GetString(config.OutputFormatKey) if outputFormat == NoneOutputFormat { return } @@ -172,7 +168,7 @@ func (p *Printer) PromptForPassword(prompt string) (string, error) { // Shows the content in the command's stdout using the "less" command // If output format is set to none, it does nothing func (p *Printer) PagerDisplay(content string) error { - outputFormat := viper.GetString(outputFormatKey) + outputFormat := viper.GetString(config.OutputFormatKey) if outputFormat == NoneOutputFormat { return nil } diff --git a/internal/pkg/print/print_test.go b/internal/pkg/print/print_test.go index f5841de37..712288639 100644 --- a/internal/pkg/print/print_test.go +++ b/internal/pkg/print/print_test.go @@ -10,6 +10,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/stackitcloud/stackit-cli/internal/pkg/config" ) func TestOutputf(t *testing.T) { @@ -65,7 +66,7 @@ func TestOutputf(t *testing.T) { viper.Reset() if tt.outputFormatNone { - viper.Set(outputFormatKey, NoneOutputFormat) + viper.Set(config.OutputFormatKey, NoneOutputFormat) } if len(tt.args) == 0 { @@ -136,7 +137,7 @@ func TestOutputln(t *testing.T) { viper.Reset() if tt.outputFormatNone { - viper.Set(outputFormatKey, NoneOutputFormat) + viper.Set(config.OutputFormatKey, NoneOutputFormat) } p.Outputln(tt.message) @@ -200,7 +201,7 @@ func TestPagerDisplay(t *testing.T) { viper.Reset() if tt.outputFormatNone { - viper.Set(outputFormatKey, NoneOutputFormat) + viper.Set(config.OutputFormatKey, NoneOutputFormat) } err := p.PagerDisplay(tt.content) diff --git a/main.go b/main.go index 5286783ac..37689d889 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ import ( "github.com/stackitcloud/stackit-cli/internal/pkg/config" ) -// These values are overwritten by GoReleaser at build time +// These values are dynamically overridden by GoReleaser var ( version = "DEV" date = "UNKNOWN"