diff --git a/docs/commands/openfeature_init.md b/docs/commands/openfeature_init.md index a3f9e8f..f966a0c 100644 --- a/docs/commands/openfeature_init.md +++ b/docs/commands/openfeature_init.md @@ -15,9 +15,9 @@ openfeature init [flags] ### Options ``` - --flag-source-url string The URL of the flag source - -h, --help help for init - --override Override an existing configuration + -h, --help help for init + --override Override an existing configuration + --provider-url string The URL of the flag provider ``` ### Options inherited from parent commands diff --git a/docs/commands/openfeature_pull.md b/docs/commands/openfeature_pull.md index 9f4adaa..39820a1 100644 --- a/docs/commands/openfeature_pull.md +++ b/docs/commands/openfeature_pull.md @@ -36,10 +36,10 @@ openfeature pull [flags] ### Options ``` - --auth-token string The auth token for the flag source - --flag-source-url string The URL of the flag source - -h, --help help for pull - --no-prompt Disable interactive prompts for missing default values + --auth-token string The auth token for the flag provider + -h, --help help for pull + --no-prompt Disable interactive prompts for missing default values + --provider-url string The URL of the flag provider ``` ### Options inherited from parent commands diff --git a/docs/commands/openfeature_push.md b/docs/commands/openfeature_push.md index a767c69..c64e042 100644 --- a/docs/commands/openfeature_push.md +++ b/docs/commands/openfeature_push.md @@ -40,25 +40,25 @@ openfeature push [flags] ``` # Push flags to a remote HTTPS endpoint (smart push: creates and updates as needed) - openfeature push --flag-source-url https://api.example.com --auth-token secret-token + openfeature push --provider-url https://api.example.com --auth-token secret-token # Push flags to an HTTP endpoint (development) - openfeature push --flag-source-url http://localhost:8080 + openfeature push --provider-url http://localhost:8080 # Dry run to preview what would be sent - openfeature push --flag-source-url https://api.example.com --dry-run + openfeature push --provider-url https://api.example.com --dry-run ``` ### Options ``` - --auth-token string The auth token for the flag destination - --debug Enable debug logging - --dry-run Preview changes without pushing - --flag-source-url string The URL of the flag destination - -h, --help help for push - -m, --manifest string Path to the flag manifest (default "flags.json") - --no-input Disable interactive prompts + --auth-token string The auth token for the flag provider + --debug Enable debug logging + --dry-run Preview changes without pushing + -h, --help help for push + -m, --manifest string Path to the flag manifest (default "flags.json") + --no-input Disable interactive prompts + --provider-url string The URL of the flag provider ``` ### SEE ALSO diff --git a/internal/cmd/init.go b/internal/cmd/init.go index e98c47d..ce0e3d0 100644 --- a/internal/cmd/init.go +++ b/internal/cmd/init.go @@ -24,13 +24,13 @@ func GetInitCmd() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { manifestPath := config.GetManifestPath(cmd) override := config.GetOverride(cmd) - flagSourceUrl := config.GetFlagSourceUrl(cmd) + providerUrl := config.GetFlagSourceUrl(cmd) if err := handleManifestCreation(manifestPath, override); err != nil { return err } - if err := handleConfigFile(flagSourceUrl, override); err != nil { + if err := handleConfigFile(providerUrl, override); err != nil { return err } @@ -84,7 +84,7 @@ func handleManifestCreation(manifestPath string, override bool) error { return nil } -func handleConfigFile(flagSourceUrl string, override bool) error { +func handleConfigFile(providerURL string, override bool) error { configPath := ".openfeature.yaml" configExists, err := filesystem.Exists(configPath) if err != nil { @@ -92,15 +92,15 @@ func handleConfigFile(flagSourceUrl string, override bool) error { } if !configExists { - return writeConfigFile(flagSourceUrl, "Creating .openfeature.yaml configuration file") + return writeConfigFile(providerURL, "Creating .openfeature.yaml configuration file") } - if flagSourceUrl == "" { + if providerURL == "" { return nil // no config to write } if override { - return writeConfigFile(flagSourceUrl, "Updating flag source URL in .openfeature.yaml") + return writeConfigFile(providerURL, "Updating provider URL in .openfeature.yaml") } shouldOverride, err := confirmOverride("configuration file", configPath) @@ -108,22 +108,22 @@ func handleConfigFile(flagSourceUrl string, override bool) error { return fmt.Errorf("failed to get user confirmation: %w", err) } if shouldOverride { - return writeConfigFile(flagSourceUrl, "Updating flag source URL in .openfeature.yaml") + return writeConfigFile(providerURL, "Updating provider URL in .openfeature.yaml") } logger.Default.Info("Configuration file was not modified.") return nil } -func writeConfigFile(flagSourceUrl, message string) error { - pterm.Info.Println(message, pterm.LightWhite(flagSourceUrl)) - template := getConfigTemplate(flagSourceUrl) +func writeConfigFile(providerURL, message string) error { + pterm.Info.Println(message, pterm.LightWhite(providerURL)) + template := getConfigTemplate(providerURL) return filesystem.WriteFile(".openfeature.yaml", []byte(template)) } type configTemplateData struct { - FlagSourceURL string - HasFlagSourceURL bool + ProviderURL string + HasProviderURL bool } const configTemplateText = `# OpenFeature CLI Configuration @@ -134,11 +134,11 @@ const configTemplateText = `# OpenFeature CLI Configuration # Path to your flag manifest file (default: "flags.json") # manifest: "flags.json" -# URL of your flag source for the 'pull' command -# Supports http://, https://, and file:// protocols -{{if .HasFlagSourceURL}}flagSourceURL: {{.FlagSourceURL}}{{else}}# flagSourceUrl: "https://your-flag-service.com/api/flags"{{end}} +# URL of your flag provider for the 'pull' and 'push' commands +# Supports http://, https://, and file:// protocols (file:// only for pull) +{{if .HasProviderURL}}provider: {{.ProviderURL}}{{else}}# provider: "https://your-flag-service.com/api/flags"{{end}} -# Authentication token for remote flag sources (if required) +# Authentication token for remote flag providers (if required) # authToken: "your-bearer-token" # Enable debug logging (default: false) @@ -151,36 +151,41 @@ const configTemplateText = `# OpenFeature CLI Configuration # Override global settings for specific commands # pull: -# flag-source-url: "https://api.example.com/flags" +# provider: "https://api.example.com/flags" # auth-token: "pull-specific-token" # no-prompt: false +# push: +# provider: "https://api.example.com/flags" +# auth-token: "push-specific-token" +# dry-run: false + # generate: # output: "generated" -# +# # # Language-specific generator options # go: # output: "go/flags" # package-name: "openfeature" -# +# # typescript: # output: "ts/flags" -# +# # csharp: # output: "csharp/flags" # namespace: "OpenFeature" -# +# # java: # output: "java/flags" # package-name: "com.example.openfeature" ` -func getConfigTemplate(flagSourceUrl string) string { +func getConfigTemplate(providerURL string) string { tmpl := template.Must(template.New("config").Parse(configTemplateText)) data := configTemplateData{ - FlagSourceURL: flagSourceUrl, - HasFlagSourceURL: flagSourceUrl != "", + ProviderURL: providerURL, + HasProviderURL: providerURL != "", } var buf bytes.Buffer diff --git a/internal/cmd/pull.go b/internal/cmd/pull.go index 1652e21..c15f85e 100644 --- a/internal/cmd/pull.go +++ b/internal/cmd/pull.go @@ -42,17 +42,17 @@ Why pull from a remote source: return initializeConfig(cmd, "pull") }, RunE: func(cmd *cobra.Command, args []string) error { - flagSourceUrl := config.GetFlagSourceUrl(cmd) + providerURL := config.GetFlagSourceUrl(cmd) manifestPath := config.GetManifestPath(cmd) authToken := config.GetAuthToken(cmd) noPrompt := config.GetNoPrompt(cmd) - if flagSourceUrl == "" { - return fmt.Errorf("flagSourceUrl not set in config") + if providerURL == "" { + return fmt.Errorf("provider URL not set in config. Please provide --provider-url or set 'provider' in .openfeature.yaml") } // fetch the flags from the remote source - parsedURL, err := url.Parse(flagSourceUrl) + parsedURL, err := url.Parse(providerURL) if err != nil { return fmt.Errorf("invalid URL: %w", err) } @@ -69,14 +69,14 @@ Why pull from a remote source: urlContainsAFileExtension := manifest.URLLooksLikeAFile(parsedURL.String()) if urlContainsAFileExtension { // Use direct HTTP requests for pulling flags from file-like URLs - loadedFlags, err := manifest.LoadFromRemote(flagSourceUrl, authToken) + loadedFlags, err := manifest.LoadFromRemote(providerURL, authToken) if err != nil { return fmt.Errorf("error fetching flags from remote source: %w", err) } flags = loadedFlags } else { // Use the sync API client for pulling flags - loadedFlags, err := manifest.LoadFromSyncAPI(flagSourceUrl, authToken) + loadedFlags, err := manifest.LoadFromSyncAPI(providerURL, authToken) if err != nil { return fmt.Errorf("error fetching flags from remote source: %w", err) } @@ -101,7 +101,7 @@ Why pull from a remote source: } } - pterm.Success.Printfln("Successfully fetched flags from %s", flagSourceUrl) + pterm.Success.Printfln("Successfully fetched flags from %s", providerURL) if err := manifest.Write(manifestPath, *flags); err != nil { return fmt.Errorf("error writing manifest: %w", err) } diff --git a/internal/cmd/pull_test.go b/internal/cmd/pull_test.go index 0680415..8495f8a 100644 --- a/internal/cmd/pull_test.go +++ b/internal/cmd/pull_test.go @@ -21,7 +21,7 @@ func setupTest(t *testing.T) afero.Fs { } func TestPull(t *testing.T) { - t.Run("pull no flag source url", func(t *testing.T) { + t.Run("pull no provider url", func(t *testing.T) { setupTest(t) cmd := GetPullCmd() @@ -35,10 +35,10 @@ func TestPull(t *testing.T) { // Run command err := cmd.Execute() assert.Error(t, err) - assert.Contains(t, err.Error(), "flagSourceUrl not set in config") + assert.Contains(t, err.Error(), "provider URL not set in config") }) - t.Run("pull with flag source url", func(t *testing.T) { + t.Run("pull with provider url", func(t *testing.T) { fs := setupTest(t) defer gock.Off() @@ -74,7 +74,7 @@ func TestPull(t *testing.T) { // Prepare command arguments - use base URL only args := []string{ "pull", - "--flag-source-url", "https://example.com", + "--provider-url", "https://example.com", "--manifest", "manifest/path.json", } @@ -124,7 +124,7 @@ func TestPull(t *testing.T) { // Prepare command arguments - use base URL only args := []string{ "pull", - "--flag-source-url", "https://example.com", + "--provider-url", "https://example.com", "--manifest", "manifest/path.json", } @@ -165,7 +165,7 @@ func TestPull(t *testing.T) { // Prepare command arguments - URL with .json extension args := []string{ "pull", - "--flag-source-url", "https://example.com/flags.json", + "--provider-url", "https://example.com/flags.json", "--manifest", "manifest/path.json", } @@ -218,7 +218,7 @@ func TestPull(t *testing.T) { // Prepare command arguments - URL with .yaml extension args := []string{ "pull", - "--flag-source-url", "https://example.com/flags.yaml", + "--provider-url", "https://example.com/flags.yaml", "--manifest", "manifest/path.json", } @@ -271,7 +271,7 @@ func TestPull(t *testing.T) { // Prepare command arguments - URL with .yml extension args := []string{ "pull", - "--flag-source-url", "https://example.com/config.yml", + "--provider-url", "https://example.com/config.yml", "--manifest", "manifest/path.json", } @@ -325,7 +325,7 @@ func TestPull(t *testing.T) { // Prepare command arguments - base URL without file extension args := []string{ "pull", - "--flag-source-url", "https://api.example.com", + "--provider-url", "https://api.example.com", "--manifest", "manifest/path.json", } @@ -348,4 +348,58 @@ func TestPull(t *testing.T) { _, exists := flags["syncApiFlag"] assert.True(t, exists, "Flag syncApiFlag should exist in manifest") }) + + t.Run("backward compatibility with deprecated --flag-source-url", func(t *testing.T) { + fs := setupTest(t) + defer gock.Off() + + // Mock response in OpenAPI ManifestEnvelope format + manifestResponse := map[string]any{ + "flags": []map[string]any{ + { + "key": "backwardCompatFlag", + "type": "boolean", + "defaultValue": true, + "description": "Test backward compatibility", + }, + }, + } + + // Mock the sync API endpoint + gock.New("https://example.com"). + Get("/openfeature/v0/manifest"). + Reply(200). + JSON(manifestResponse) + + cmd := GetPullCmd() + + // global flag exists on root only. + config.AddRootFlags(cmd) + + // Use the deprecated flag to test backward compatibility + args := []string{ + "pull", + "--flag-source-url", "https://example.com", + "--manifest", "manifest/path.json", + } + + cmd.SetArgs(args) + + // Run command - should work but show deprecation warning + err := cmd.Execute() + assert.NoError(t, err) + + // Verify the manifest was written correctly + content, err := afero.ReadFile(fs, "manifest/path.json") + assert.NoError(t, err) + + var manifestFlags map[string]interface{} + err = json.Unmarshal(content, &manifestFlags) + assert.NoError(t, err) + + // Verify the flag exists in the manifest + flags := manifestFlags["flags"].(map[string]interface{}) + _, exists := flags["backwardCompatFlag"] + assert.True(t, exists, "Flag backwardCompatFlag should exist in manifest") + }) } diff --git a/internal/cmd/push.go b/internal/cmd/push.go index 72c7b7c..bfa30e4 100644 --- a/internal/cmd/push.go +++ b/internal/cmd/push.go @@ -43,30 +43,30 @@ specified by the OpenFeature flag manifest schema. Note: The file:// scheme is not supported for push operations. For local file operations, use standard shell commands like cp or mv.`, Example: ` # Push flags to a remote HTTPS endpoint (smart push: creates and updates as needed) - openfeature push --flag-source-url https://api.example.com --auth-token secret-token + openfeature push --provider-url https://api.example.com --auth-token secret-token # Push flags to an HTTP endpoint (development) - openfeature push --flag-source-url http://localhost:8080 + openfeature push --provider-url http://localhost:8080 # Dry run to preview what would be sent - openfeature push --flag-source-url https://api.example.com --dry-run`, + openfeature push --provider-url https://api.example.com --dry-run`, PreRunE: func(cmd *cobra.Command, args []string) error { return initializeConfig(cmd, "push") }, RunE: func(cmd *cobra.Command, args []string) error { // Get configuration values - flagSourceUrl := config.GetFlagSourceUrl(cmd) + providerURL := config.GetFlagSourceUrl(cmd) manifestPath := config.GetManifestPath(cmd) authToken := config.GetAuthToken(cmd) dryRun := config.GetDryRun(cmd) // Validate destination URL is provided - if flagSourceUrl == "" { - return fmt.Errorf("flag source URL is required. Please provide --flag-source-url") + if providerURL == "" { + return fmt.Errorf("provider URL is required. Please provide --provider-url") } // Parse and validate URL - parsedURL, err := url.Parse(flagSourceUrl) + parsedURL, err := url.Parse(providerURL) if err != nil { return fmt.Errorf("invalid source URL: %w", err) } @@ -86,13 +86,13 @@ For local file operations, use standard shell commands like cp or mv.`, case "http", "https": // Perform smart push (fetches remote, compares, and creates/updates as needed) // In dry run mode, performs comparison but skips actual API calls - result, err := manifest.SaveToRemote(flagSourceUrl, flags, authToken, dryRun) + result, err := manifest.SaveToRemote(providerURL, flags, authToken, dryRun) if err != nil { return fmt.Errorf("error pushing flags to remote destination: %w", err) } // Display the results - displayPushResults(result, flagSourceUrl, dryRun) + displayPushResults(result, providerURL, dryRun) default: return fmt.Errorf("unsupported URL scheme: %s. Supported schemes are http:// and https://", parsedURL.Scheme) } @@ -189,7 +189,7 @@ func displayPushResults(result *sync.PushResult, destination string, dryRun bool fmt.Println() // Show flag details - flagJSON, _ := json.MarshalIndent(map[string]interface{}{ + flagJSON, _ := json.MarshalIndent(map[string]any{ "type": flag.Type.String(), "defaultValue": flag.DefaultValue, }, " ", " ") diff --git a/internal/cmd/push_test.go b/internal/cmd/push_test.go index 9013db7..44831c0 100644 --- a/internal/cmd/push_test.go +++ b/internal/cmd/push_test.go @@ -34,7 +34,7 @@ func TestPush(t *testing.T) { err := cmd.Execute() assert.Error(t, err) - assert.Contains(t, err.Error(), "flag source URL is required") + assert.Contains(t, err.Error(), "provider URL is required") }) t.Run("smart push creates new flags", func(t *testing.T) { @@ -70,7 +70,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "http://localhost:8080/openfeature/v0/manifest", + "--provider-url", "http://localhost:8080/openfeature/v0/manifest", "--manifest", "flags.json", } cmd.SetArgs(args) @@ -121,7 +121,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "https://api.example.com/openfeature/v0/manifest", + "--provider-url", "https://api.example.com/openfeature/v0/manifest", "--manifest", "flags.json", } cmd.SetArgs(args) @@ -188,7 +188,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "https://api.example.com/openfeature/v0/manifest", + "--provider-url", "https://api.example.com/openfeature/v0/manifest", "--manifest", "flags.json", } cmd.SetArgs(args) @@ -232,7 +232,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "https://api.example.com/openfeature/v0/manifest", + "--provider-url", "https://api.example.com/openfeature/v0/manifest", "--auth-token", "secret-token", "--manifest", "flags.json", } @@ -275,7 +275,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "https://api.example.com", + "--provider-url", "https://api.example.com", "--dry-run", "--manifest", "flags.json", } @@ -295,7 +295,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "file:///local/path/flags.json", + "--provider-url", "file:///local/path/flags.json", "--manifest", "flags.json", } cmd.SetArgs(args) @@ -312,7 +312,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "ftp://example.com/flags", + "--provider-url", "ftp://example.com/flags", "--manifest", "flags.json", } cmd.SetArgs(args) @@ -341,7 +341,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "https://api.example.com/openfeature/v0/manifest", + "--provider-url", "https://api.example.com/openfeature/v0/manifest", "--manifest", "flags.json", } cmd.SetArgs(args) @@ -379,7 +379,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "https://api.example.com/openfeature/v0/manifest", + "--provider-url", "https://api.example.com/openfeature/v0/manifest", "--manifest", "flags.json", } cmd.SetArgs(args) @@ -418,7 +418,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "https://api.example.com/openfeature/v0/manifest", + "--provider-url", "https://api.example.com/openfeature/v0/manifest", "--manifest", "flags.json", } cmd.SetArgs(args) @@ -481,7 +481,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "https://api.example.com/openfeature/v0/manifest", + "--provider-url", "https://api.example.com/openfeature/v0/manifest", "--manifest", "flags.json", } cmd.SetArgs(args) @@ -497,7 +497,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "https://api.example.com/flags", + "--provider-url", "https://api.example.com/flags", "--manifest", "nonexistent.json", } cmd.SetArgs(args) @@ -526,7 +526,7 @@ func TestPush(t *testing.T) { cmd := GetPushCmd() args := []string{ - "--flag-source-url", "https://api.example.com/flags", + "--provider-url", "https://api.example.com/flags", "--manifest", "invalid.json", } cmd.SetArgs(args) diff --git a/internal/config/flags.go b/internal/config/flags.go index 35540d3..2449422 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -18,7 +18,8 @@ const ( CSharpNamespaceName = "namespace" OverrideFlagName = "override" JavaPackageFlagName = "package-name" - FlagSourceUrlFlagName = "flag-source-url" + ProviderURLFlagName = "provider-url" + FlagSourceUrlFlagName = "flag-source-url" // Deprecated: use ProviderFlagName instead AuthTokenFlagName = "auth-token" NoPromptFlagName = "no-prompt" DryRunFlagName = "dry-run" @@ -66,20 +67,26 @@ func AddJavaGenerateFlags(cmd *cobra.Command) { // AddInitFlags adds the init command specific flags func AddInitFlags(cmd *cobra.Command) { cmd.Flags().Bool(OverrideFlagName, false, "Override an existing configuration") - cmd.Flags().String(FlagSourceUrlFlagName, "", "The URL of the flag source") + cmd.Flags().String(ProviderURLFlagName, "", "The URL of the flag provider") + cmd.Flags().String(FlagSourceUrlFlagName, "", "The URL of the flag source (deprecated: use --provider-url instead)") + _ = cmd.Flags().MarkDeprecated(FlagSourceUrlFlagName, "use --provider-url instead") } // AddPullFlags adds the pull command specific flags func AddPullFlags(cmd *cobra.Command) { - cmd.Flags().String(FlagSourceUrlFlagName, "", "The URL of the flag source") - cmd.Flags().String(AuthTokenFlagName, "", "The auth token for the flag source") + cmd.Flags().String(ProviderURLFlagName, "", "The URL of the flag provider") + cmd.Flags().String(FlagSourceUrlFlagName, "", "The URL of the flag source (deprecated: use --provider-url instead)") + _ = cmd.Flags().MarkDeprecated(FlagSourceUrlFlagName, "use --provider-url instead") + cmd.Flags().String(AuthTokenFlagName, "", "The auth token for the flag provider") cmd.Flags().Bool(NoPromptFlagName, false, "Disable interactive prompts for missing default values") } // AddPushFlags adds the push command specific flags func AddPushFlags(cmd *cobra.Command) { - cmd.Flags().String(FlagSourceUrlFlagName, "", "The URL of the flag destination") - cmd.Flags().String(AuthTokenFlagName, "", "The auth token for the flag destination") + cmd.Flags().String(ProviderURLFlagName, "", "The URL of the flag provider") + cmd.Flags().String(FlagSourceUrlFlagName, "", "The URL of the flag destination (deprecated: use --provider-url instead)") + _ = cmd.Flags().MarkDeprecated(FlagSourceUrlFlagName, "use --provider-url instead") + cmd.Flags().String(AuthTokenFlagName, "", "The auth token for the flag provider") cmd.Flags().Bool(DryRunFlagName, false, "Preview changes without pushing") } @@ -127,18 +134,34 @@ func GetOverride(cmd *cobra.Command) bool { // getConfigValueWithFallback is a helper function that attempts to get a value from Viper config // if the provided value is empty. This reduces duplication for flag source/destination URLs. -func getConfigValueWithFallback(value string, configKey string) string { +// It checks both the new and legacy config keys, with the new key taking precedence. +func getConfigValueWithFallback(value string, newConfigKey string, legacyConfigKey string) string { if value != "" { return value } - // Viper is already configured in initializeConfig, just retrieve the value - return viper.GetString(configKey) + // Check the new config key first + if configValue := viper.GetString(newConfigKey); configValue != "" { + return configValue + } + // Fall back to legacy config key for backward compatibility + return viper.GetString(legacyConfigKey) } // GetFlagSourceUrl gets the flag source URL from the given command +// It checks the new --provider-url flag first, then falls back to the deprecated --flag-source-url flag +// for backward compatibility. Finally, it checks the config file for both keys. func GetFlagSourceUrl(cmd *cobra.Command) string { + // Check new flag first + provider, _ := cmd.Flags().GetString(ProviderURLFlagName) + if provider != "" { + return provider + } + + // Fall back to deprecated flag for backward compatibility flagSourceUrl, _ := cmd.Flags().GetString(FlagSourceUrlFlagName) - return getConfigValueWithFallback(flagSourceUrl, "flagSourceUrl") + + // Use the fallback helper which checks both config keys + return getConfigValueWithFallback(flagSourceUrl, "provider", "flagSourceUrl") } // GetAuthToken gets the auth token from the given command