diff --git a/cmd/root.go b/cmd/root.go index 099b8ad946d2e55f5e754b714dc3ca27135b87d5..3f3e82d95d663dbf595edc93afe431c7b5aad6cf 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -23,7 +23,6 @@ var ( flagPreset string flagShowCmd bool flagConfigFile string - presetResolved bool ) const overrideArgumentsKey = "_arguments" @@ -54,14 +53,7 @@ var rootCmd = &cobra.Command{ return nil } - // Preset was resolved by the session; pass it directly - // so runCommand skips its own preset prompt. - flagPreset = preset - // Mark that the session already handled preset selection - // so runCommand does not re-prompt even when preset is "". - presetResolved = true - - return runCommand(commandName, cmd, nil, overrides) + return runCommand(commandName, cmd, nil, overrides, &preset) }, } @@ -72,7 +64,14 @@ func registerRootFlags() { flags.StringVarP(&flagConfigFile, "config", "C", "", "path to keld config file") } -func runCommand(commandName string, cmd *cobra.Command, rawArgs []string, sessionOverrides map[string][]string) error { +// runCommand resolves config and executes restic. +// +// sessionPreset signals that a TUI session already handled preset +// selection. When non-nil, its value is used as the preset and the +// standalone preset prompt is skipped. When nil, the subcommand was +// invoked non-interactively (with arguments) and no prompting +// occurs. +func runCommand(commandName string, cmd *cobra.Command, rawArgs []string, sessionOverrides map[string][]string, sessionPreset *string) error { if flagConfigFile != "" { if err := os.Setenv("KELD_CONFIG_FILE", flagConfigFile); err != nil { return fmt.Errorf("setting KELD_CONFIG_FILE: %w", err) @@ -86,10 +85,15 @@ func runCommand(commandName string, cmd *cobra.Command, rawArgs []string, sessio preset := flagPreset // Interactive mode when launched from root command or when a - // TUI session ran (presetResolved). The session handles preset - // selection, but runCommand may still need to collect values - // for commands without dedicated TUI screens (e.g. backup paths). - interactive := cmd == cmd.Root() || presetResolved + // TUI session ran (sessionPreset != nil). The session handles + // preset selection, but runCommand may still need to collect + // values for commands without dedicated TUI screens (e.g. + // backup paths). + interactive := cmd == cmd.Root() || sessionPreset != nil + + if sessionPreset != nil { + preset = *sessionPreset + } if preset != "" { if err := validatePreset(preset); err != nil { @@ -99,18 +103,10 @@ func runCommand(commandName string, cmd *cobra.Command, rawArgs []string, sessio overrides := mergeOverrides(parsePassthrough(rawArgs), sessionOverrides) - // In interactive mode, fill missing preset/command inputs via prompts. - // When the unified session has already resolved the preset (presetResolved), - // skip the standalone preset prompt. + // In interactive mode, fill missing command-specific inputs + // via prompts. The session already handles preset selection, + // so only command-level prompts are needed here. if interactive { - if !presetResolved && preset == "" { - p, err := promptPreset() - if err != nil { - return err - } - preset = p - } - // Resolve config before prompting so we can skip questions for values // already provided by presets. peek, err := config.Resolve(preset, commandName, overrides) @@ -473,20 +469,6 @@ func validatePreset(preset string) error { return fmt.Errorf("unknown preset %q; available presets: %s", preset, strings.Join(known, ", ")) } -// promptPreset shows an interactive preset selector when presets are defined -// in the config. Returns "" (global-only) if no presets exist. -func promptPreset() (string, error) { - presets := config.Presets() - if len(presets) == 0 { - return "", nil - } - selected, err := form.SelectPreset(presets) - if err != nil { - return "", fmt.Errorf("preset selection: %w", err) - } - return selected, nil -} - // promptForCommand collects required inputs for commands that need them. // The resolved config is checked first so prompts are skipped for values // the preset already provides. diff --git a/cmd/root_test.go b/cmd/root_test.go index 8b46fb0ed1c175a6d03a0e9cf8b6c72f39cf307f..a25c1e89f5cdc2d4964e0894f527b371cc0e80ee 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -108,7 +108,7 @@ func TestRunCommandAppliesRootFlagsAndPassthrough(t *testing.T) { backup := lookupSubcommand(t, "backup") out, err := captureStdout(t, func() error { - return runCommand("backup", backup, []string{"--tag", "daily", "/src"}, nil) + return runCommand("backup", backup, []string{"--tag", "daily", "/src"}, nil, nil) }) if err != nil { t.Fatalf("runCommand returned error: %v", err) @@ -133,7 +133,7 @@ func TestRunCommandRejectsUnknownPreset(t *testing.T) { setRootFlagValuesForTest(t, "missing", true, configFile) backup := lookupSubcommand(t, "backup") - err := runCommand("backup", backup, nil, nil) + err := runCommand("backup", backup, nil, nil, nil) if err == nil { t.Fatal("expected unknown preset error") } @@ -199,16 +199,13 @@ func setRootFlagValuesForTest(t *testing.T, preset string, showCommand bool, con t.Helper() prevPreset, prevShowCommand, prevConfigFile := flagPreset, flagShowCmd, flagConfigFile - prevPresetResolved := presetResolved flagPreset = preset flagShowCmd = showCommand flagConfigFile = configFile - presetResolved = false t.Cleanup(func() { flagPreset = prevPreset flagShowCmd = prevShowCommand flagConfigFile = prevConfigFile - presetResolved = prevPresetResolved }) } diff --git a/cmd/subcommands.go b/cmd/subcommands.go index af33d688d52932af2f2921106d130ad504ba822c..caa6bdf6b64150778191be5a8ed2ff2f7d2d0a87 100644 --- a/cmd/subcommands.go +++ b/cmd/subcommands.go @@ -58,11 +58,10 @@ func registerSubcommands() { return nil } flagPreset = preset - presetResolved = true - return runCommand(cmdName, cmd, nil, overrides) + return runCommand(cmdName, cmd, nil, overrides, &preset) } - return runCommand(commandName, cmd, args, nil) + return runCommand(commandName, cmd, args, nil, nil) }, }