diff --git a/cmd/pro/login.go b/cmd/pro/login.go index c690e5d2c..e81dcd763 100644 --- a/cmd/pro/login.go +++ b/cmd/pro/login.go @@ -247,15 +247,11 @@ func (cmd *LoginCmd) loginAndConfigure( } if cmd.Use { + // Post-login: preserve user values; resolver prunes anything stale. err := providercmd.ConfigureProvider(ctx, providercmd.ProviderOptionsConfig{ - Provider: providerConfig, - Context: devsyConfig.DefaultContext, - UserOptions: cmd.Options, - Reconfigure: false, - SkipRequired: false, - SkipInit: false, - SkipSubOptions: false, - SingleMachine: nil, + Provider: providerConfig, + ContextName: devsyConfig.DefaultContext, + UserOptions: cmd.Options, }) if err != nil { return fmt.Errorf("configure provider: %w", err) diff --git a/cmd/pro/update_provider.go b/cmd/pro/update_provider.go index 0b3ce9170..4f3fc82fe 100644 --- a/cmd/pro/update_provider.go +++ b/cmd/pro/update_provider.go @@ -77,15 +77,14 @@ func (cmd *UpdateProviderCmd) Run(ctx context.Context, args []string) error { return fmt.Errorf("update provider %s: %w", provider.Name, err) } + // Automated version bump: re-resolve from the new schema's defaults + // without prompting and without re-running init. err = providercmd.ConfigureProvider(ctx, providercmd.ProviderOptionsConfig{ - Provider: provider, - Context: devsyConfig.DefaultContext, - UserOptions: []string{}, - Reconfigure: true, - SkipRequired: true, - SkipInit: true, - SkipSubOptions: false, - SingleMachine: nil, + Provider: provider, + ContextName: devsyConfig.DefaultContext, + DiscardPriorValues: true, + SkipRequired: true, + SkipInit: true, }) if err != nil { return fmt.Errorf( diff --git a/cmd/provider/add.go b/cmd/provider/add.go index a74c74748..472ef84ea 100644 --- a/cmd/provider/add.go +++ b/cmd/provider/add.go @@ -119,15 +119,15 @@ func (cmd *AddCmd) Run(ctx context.Context, devsyConfig *config.Config, args []s log.Infof("installed provider: providerName=%s", providerConfig.Name) if cmd.Use { + // First add: there are no prior user values to merge, so + // DiscardPriorValues is moot. Set it explicitly so future readers + // don't wonder whether merging matters here. configureErr := ConfigureProvider(ctx, ProviderOptionsConfig{ - Provider: providerConfig, - Context: devsyConfig.DefaultContext, - UserOptions: options, - Reconfigure: true, - SkipRequired: false, - SkipInit: false, - SkipSubOptions: false, - SingleMachine: &cmd.SingleMachine, + Provider: providerConfig, + ContextName: devsyConfig.DefaultContext, + UserOptions: options, + DiscardPriorValues: true, + SingleMachine: &cmd.SingleMachine, }) if configureErr != nil { devsyConfig, err := config.LoadConfig(cmd.Context, "") diff --git a/cmd/provider/configure_shared.go b/cmd/provider/configure_shared.go index 56217c8b7..c7e4cfae0 100644 --- a/cmd/provider/configure_shared.go +++ b/cmd/provider/configure_shared.go @@ -14,15 +14,26 @@ import ( provider2 "github.com/devsy-org/devsy/pkg/provider" ) +// ProviderOptionsConfig parameterizes ConfigureProvider. +// +// DiscardPriorValues controls whether previously user-provided option +// values are carried forward as defaults for prompts in the new option +// resolution. When false (default), values the user set previously seed +// the new prompts; when true, the user is asked from scratch. +// +// Note: this does NOT control stale-data pruning. The downstream +// resolver (pkg/options/resolver) always prunes keys absent from the +// new schema and re-resolves values that fail validation, regardless +// of this flag. type ProviderOptionsConfig struct { - Provider *provider2.ProviderConfig - Context string - UserOptions []string - Reconfigure bool - SkipRequired bool - SkipInit bool - SkipSubOptions bool - SingleMachine *bool + Provider *provider2.ProviderConfig + ContextName string + UserOptions []string + DiscardPriorValues bool + SkipRequired bool + SkipInit bool + SkipSubOptions bool + SingleMachine *bool } func ConfigureProvider(ctx context.Context, cfg ProviderOptionsConfig) error { @@ -57,7 +68,7 @@ func configureProviderOptions( ctx context.Context, cfg ProviderOptionsConfig, ) (*config.Config, error) { - devsyConfig, err := config.LoadConfig(cfg.Context, "") + devsyConfig, err := config.LoadConfig(cfg.ContextName, "") if err != nil { return nil, err } @@ -74,8 +85,10 @@ func configureProviderOptions( return nil, fmt.Errorf("parse options: %w", err) } - // merge with old values - if !cfg.Reconfigure { + // Seed prompts with the user's previous answers unless the caller + // explicitly wants a fresh slate. Stale keys are pruned downstream + // by the resolver regardless of this branch. + if !cfg.DiscardPriorValues { mergeExistingOptions(options, devsyConfig.ProviderOptions(cfg.Provider.Name)) } diff --git a/cmd/provider/init.go b/cmd/provider/init.go index aa06d339d..aa9913816 100644 --- a/cmd/provider/init.go +++ b/cmd/provider/init.go @@ -11,7 +11,7 @@ import ( // InitCmd holds flags for the `provider init` subcommand. type InitCmd struct { *flags.GlobalFlags - Reconfigure bool + Reset bool SingleMachine bool Options []string SkipInit bool @@ -38,14 +38,12 @@ func NewInitCmd(f *flags.GlobalFlags) *cobra.Command { return err } return ConfigureProvider(cobraCmd.Context(), ProviderOptionsConfig{ - Provider: p.Config, - Context: devsyConfig.DefaultContext, - UserOptions: cmd.Options, - Reconfigure: cmd.Reconfigure, - SkipRequired: false, - SkipInit: cmd.SkipInit, - SkipSubOptions: false, - SingleMachine: &cmd.SingleMachine, + Provider: p.Config, + ContextName: devsyConfig.DefaultContext, + UserOptions: cmd.Options, + DiscardPriorValues: cmd.Reset, + SkipInit: cmd.SkipInit, + SingleMachine: &cmd.SingleMachine, }) }, ValidArgsFunction: func( @@ -64,7 +62,7 @@ func NewInitCmd(f *flags.GlobalFlags) *cobra.Command { }, } initCmd.Flags(). - BoolVar(&cmd.Reconfigure, "reconfigure", false, "Force re-resolution of all options") + BoolVar(&cmd.Reset, "reset", false, "Discard previously stored option answers and re-prompt from scratch") initCmd.Flags(). BoolVar(&cmd.SingleMachine, "single-machine", false, "Use a single machine for all workspaces") initCmd.Flags(). diff --git a/cmd/provider/init_test.go b/cmd/provider/init_test.go index 3e6a35ac3..6123a420a 100644 --- a/cmd/provider/init_test.go +++ b/cmd/provider/init_test.go @@ -15,7 +15,7 @@ func TestNewInitCmd(t *testing.T) { t.Error("Short must be set") } // Verify flags exist - for _, flag := range []string{"reconfigure", "single-machine", "option", "skip-init"} { + for _, flag := range []string{"reset", "single-machine", "option", "skip-init"} { if cmd.Flag(flag) == nil { t.Errorf("missing flag %q", flag) } diff --git a/cmd/provider/set.go b/cmd/provider/set.go index 9f109862e..7ed0aac43 100644 --- a/cmd/provider/set.go +++ b/cmd/provider/set.go @@ -20,7 +20,6 @@ type SetCmd struct { Dry bool SkipInit bool - Reconfigure bool SingleMachine bool Options []string } @@ -50,8 +49,6 @@ func NewSetCmd(f *flags.GlobalFlags) *cobra.Command { setCmd.Flags(). BoolVar(&cmd.SingleMachine, "single-machine", false, "If enabled will use a single machine for all workspaces") - setCmd.Flags(). - BoolVar(&cmd.Reconfigure, "reconfigure", false, "If enabled will not merge existing provider config") setCmd.Flags(). StringArrayVarP(&cmd.Options, "option", "o", []string{}, "Provider option in the form KEY=VALUE") setCmd.Flags(). @@ -68,14 +65,12 @@ func (cmd *SetCmd) Run(ctx context.Context, args []string) error { } devsyConfig, err = configureProviderOptions(ctx, ProviderOptionsConfig{ - Provider: providerWithOptions.Config, - Context: devsyConfig.DefaultContext, - UserOptions: cmd.Options, - Reconfigure: cmd.Reconfigure, - SkipRequired: cmd.Dry, - SkipInit: cmd.Dry || cmd.SkipInit, - SkipSubOptions: false, - SingleMachine: &cmd.SingleMachine, + Provider: providerWithOptions.Config, + ContextName: devsyConfig.DefaultContext, + UserOptions: cmd.Options, + SkipRequired: cmd.Dry, + SkipInit: cmd.Dry || cmd.SkipInit, + SingleMachine: &cmd.SingleMachine, }) if err != nil { return err diff --git a/cmd/provider/set_source.go b/cmd/provider/set_source.go index fc9cfd9fb..13255d835 100644 --- a/cmd/provider/set_source.go +++ b/cmd/provider/set_source.go @@ -74,15 +74,14 @@ func (cmd *SetSourceCmd) Run(ctx context.Context, devsyConfig *config.Config, ar return nil } + // Preserve previously user-provided values (default DiscardPriorValues=false). + // The resolver prunes keys absent from the new schema and re-resolves values + // that fail validation, so stale data cannot leak through this path. if err := ConfigureProvider(ctx, ProviderOptionsConfig{ Provider: providerConfig, - Context: devsyConfig.DefaultContext, + ContextName: devsyConfig.DefaultContext, UserOptions: cmd.Options, }); err != nil { - log.Errorf( - "Error initializing provider, retry with 'devsy provider init %s --reconfigure'", - providerConfig.Name, - ) return fmt.Errorf("configure provider: %w", err) }