Skip to content

Commit 77a72b1

Browse files
committed
chore: prompt user to select a preset when no --preset flag is provided
1 parent b8a1c14 commit 77a72b1

File tree

3 files changed

+203
-170
lines changed

3 files changed

+203
-170
lines changed

cli/create.go

Lines changed: 90 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ import (
2121
"github.com/coder/serpent"
2222
)
2323

24-
// DefaultPresetName is used when a user runs `create --preset default`.
25-
// It instructs the CLI to use the default preset defined for the template version, if one exists.
26-
const DefaultPresetName = "default"
24+
// PresetNone represents the special preset value "none".
25+
// It is used when a user runs `create --preset none`,
26+
// indicating that the CLI should not apply any preset.
27+
const PresetNone = "none"
2728

2829
func (r *RootCmd) create() *serpent.Command {
2930
var (
@@ -268,47 +269,31 @@ func (r *RootCmd) create() *serpent.Command {
268269
}
269270
}
270271

271-
// If a preset name is provided, resolve the preset to use.
272+
// Get presets for the template version
273+
tvPresets, err := client.TemplateVersionPresets(inv.Context(), templateVersionID)
274+
if err != nil {
275+
return xerrors.Errorf("failed to get presets: %w", err)
276+
}
277+
272278
var preset *codersdk.Preset
273279
var presetParameters []codersdk.WorkspaceBuildParameter
274-
isDefaultPreset := false
275-
if len(presetName) > 0 {
276-
tvPresets, err := client.TemplateVersionPresets(inv.Context(), templateVersionID)
277-
if err != nil {
278-
return xerrors.Errorf("failed to get presets: %w", err)
279-
}
280280

281-
for _, tvPreset := range tvPresets {
282-
// If the preset name is the special "default" keyword,
283-
// fetch the template version's default preset (if any).
284-
if presetName == DefaultPresetName && tvPreset.Default {
285-
preset = &tvPreset
286-
isDefaultPreset = true
287-
break
288-
}
289-
if tvPreset.Name == presetName {
290-
preset = &tvPreset
291-
break
292-
}
293-
}
294-
295-
if preset == nil {
296-
return xerrors.Errorf("preset %q not found", presetName)
281+
// If the template has no presets, or the user explicitly used --preset none,
282+
// skip applying a preset.
283+
if len(tvPresets) > 0 && presetName != PresetNone {
284+
// Resolve which preset to use
285+
preset, err = resolvePreset(inv, tvPresets, presetName)
286+
if err != nil {
287+
return xerrors.Errorf("unable to resolve preset: %w", err)
297288
}
298289

299290
// Convert preset parameters into workspace build parameters.
300-
presetBuildParameters := presetParameterAsWorkspaceBuildParameters(preset.Parameters)
301-
presetParameters = append(presetParameters, presetBuildParameters...)
302-
291+
presetParameters = presetParameterAsWorkspaceBuildParameters(preset.Parameters)
303292
// Inform the user which preset was applied and its parameters.
304-
presetLabel := fmt.Sprintf("Preset '%s'", preset.Name)
305-
if isDefaultPreset {
306-
presetLabel += " (default)"
307-
}
308-
_, _ = fmt.Fprintf(inv.Stdout, "%s applied:", cliui.Bold(presetLabel))
309-
for _, p := range presetParameters {
310-
_, _ = fmt.Fprintf(inv.Stdout, " %s: '%s'\n", cliui.Bold(p.Name), p.Value)
311-
}
293+
displayAppliedPreset(inv, preset, presetParameters)
294+
} else {
295+
// Inform the user that no preset was applied
296+
_, _ = fmt.Fprintf(inv.Stdout, "%s", cliui.Bold("No preset applied."))
312297
}
313298

314299
richParameters, err := prepWorkspaceBuild(inv, client, prepWorkspaceBuildArgs{
@@ -446,6 +431,74 @@ type prepWorkspaceBuildArgs struct {
446431
RichParameterDefaults []codersdk.WorkspaceBuildParameter
447432
}
448433

434+
// resolvePreset determines which preset to use based on the --preset flag,
435+
// or prompts the user to select one if the flag is not provided.
436+
func resolvePreset(inv *serpent.Invocation, presets []codersdk.Preset, presetName string) (*codersdk.Preset, error) {
437+
// If preset name is specified, find it
438+
if presetName != "" {
439+
for _, preset := range presets {
440+
if preset.Name == presetName {
441+
return &preset, nil
442+
}
443+
}
444+
return nil, xerrors.Errorf("preset %q not found", presetName)
445+
}
446+
447+
// No preset specified, prompt user to select one
448+
return promptPresetSelection(inv, presets)
449+
}
450+
451+
// promptPresetSelection shows a CLI selection menu of the presets defined in the template version.
452+
func promptPresetSelection(inv *serpent.Invocation, presets []codersdk.Preset) (*codersdk.Preset, error) {
453+
presetMap := make(map[string]*codersdk.Preset)
454+
var defaultOption string
455+
var options []string
456+
457+
// Process presets, with the default option (if any) listed first.
458+
for _, preset := range presets {
459+
option := preset.Name
460+
if preset.Default {
461+
option = "(default) " + preset.Name
462+
defaultOption = option
463+
}
464+
presetMap[option] = &preset
465+
}
466+
467+
if defaultOption != "" {
468+
options = append(options, defaultOption)
469+
}
470+
for option := range presetMap {
471+
if option != defaultOption {
472+
options = append(options, option)
473+
}
474+
}
475+
476+
// Show selection UI
477+
_, _ = fmt.Fprintln(inv.Stdout, pretty.Sprint(cliui.DefaultStyles.Wrap, "Select a preset below:"))
478+
selected, err := cliui.Select(inv, cliui.SelectOptions{
479+
Options: options,
480+
HideSearch: true,
481+
})
482+
if err != nil {
483+
return nil, xerrors.Errorf("failed to select preset: %w", err)
484+
}
485+
486+
return presetMap[selected], nil
487+
}
488+
489+
// displayAppliedPreset shows the user which preset was applied and its parameters
490+
func displayAppliedPreset(inv *serpent.Invocation, preset *codersdk.Preset, parameters []codersdk.WorkspaceBuildParameter) {
491+
label := fmt.Sprintf("Preset '%s'", preset.Name)
492+
if preset.Default {
493+
label += " (default)"
494+
}
495+
496+
_, _ = fmt.Fprintf(inv.Stdout, "%s applied:\n", cliui.Bold(label))
497+
for _, param := range parameters {
498+
_, _ = fmt.Fprintf(inv.Stdout, " %s: '%s'\n", cliui.Bold(param.Name), param.Value)
499+
}
500+
}
501+
449502
// prepWorkspaceBuild will ensure a workspace build will succeed on the latest template version.
450503
// Any missing params will be prompted to the user. It supports rich parameters.
451504
func prepWorkspaceBuild(inv *serpent.Invocation, client *codersdk.Client, args prepWorkspaceBuildArgs) ([]codersdk.WorkspaceBuildParameter, error) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy