@@ -21,9 +21,10 @@ import (
21
21
"github.com/coder/serpent"
22
22
)
23
23
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"
27
28
28
29
func (r * RootCmd ) create () * serpent.Command {
29
30
var (
@@ -268,47 +269,31 @@ func (r *RootCmd) create() *serpent.Command {
268
269
}
269
270
}
270
271
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
+
272
278
var preset * codersdk.Preset
273
279
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
- }
280
280
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 )
297
288
}
298
289
299
290
// Convert preset parameters into workspace build parameters.
300
- presetBuildParameters := presetParameterAsWorkspaceBuildParameters (preset .Parameters )
301
- presetParameters = append (presetParameters , presetBuildParameters ... )
302
-
291
+ presetParameters = presetParameterAsWorkspaceBuildParameters (preset .Parameters )
303
292
// 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." ))
312
297
}
313
298
314
299
richParameters , err := prepWorkspaceBuild (inv , client , prepWorkspaceBuildArgs {
@@ -446,6 +431,74 @@ type prepWorkspaceBuildArgs struct {
446
431
RichParameterDefaults []codersdk.WorkspaceBuildParameter
447
432
}
448
433
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
+
449
502
// prepWorkspaceBuild will ensure a workspace build will succeed on the latest template version.
450
503
// Any missing params will be prompted to the user. It supports rich parameters.
451
504
func prepWorkspaceBuild (inv * serpent.Invocation , client * codersdk.Client , args prepWorkspaceBuildArgs ) ([]codersdk.WorkspaceBuildParameter , error ) {
0 commit comments