Skip to content

Commit 982031a

Browse files
committed
feat(cli): add CLI support for creating a workspace with preset
1 parent aae5fc2 commit 982031a

File tree

7 files changed

+1060
-3
lines changed

7 files changed

+1060
-3
lines changed

cli/create.go

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,15 @@ 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"
27+
2428
func (r *RootCmd) create() *serpent.Command {
2529
var (
2630
templateName string
2731
templateVersion string
32+
presetName string
2833
startAt string
2934
stopAfter time.Duration
3035
workspaceName string
@@ -263,11 +268,58 @@ func (r *RootCmd) create() *serpent.Command {
263268
}
264269
}
265270

271+
// If a preset name is provided, resolve the preset to use.
272+
var preset *codersdk.Preset
273+
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+
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)
297+
}
298+
299+
// Convert preset parameters into workspace build parameters.
300+
presetBuildParameters, err := presetParameterAsWorkspaceBuildParameters(preset.Parameters)
301+
if err != nil {
302+
return xerrors.Errorf("failed to parse preset parameters: %w", err)
303+
}
304+
presetParameters = append(presetParameters, presetBuildParameters...)
305+
306+
// Inform the user which preset was applied and its parameters.
307+
presetLabel := fmt.Sprintf("Preset '%s'", preset.Name)
308+
if isDefaultPreset {
309+
presetLabel += " (default)"
310+
}
311+
_, _ = fmt.Fprintf(inv.Stdout, "%s applied:", cliui.Bold(presetLabel))
312+
for _, p := range presetParameters {
313+
_, _ = fmt.Fprintf(inv.Stdout, " %s: '%s'\n", cliui.Bold(p.Name), p.Value)
314+
}
315+
}
316+
266317
richParameters, err := prepWorkspaceBuild(inv, client, prepWorkspaceBuildArgs{
267318
Action: WorkspaceCreate,
268319
TemplateVersionID: templateVersionID,
269320
NewWorkspaceName: workspaceName,
270321

322+
PresetParameters: presetParameters,
271323
RichParameterFile: parameterFlags.richParameterFile,
272324
RichParameters: cliBuildParameters,
273325
RichParameterDefaults: cliBuildParameterDefaults,
@@ -291,14 +343,21 @@ func (r *RootCmd) create() *serpent.Command {
291343
ttlMillis = ptr.Ref(stopAfter.Milliseconds())
292344
}
293345

294-
workspace, err := client.CreateUserWorkspace(inv.Context(), workspaceOwner, codersdk.CreateWorkspaceRequest{
346+
req := codersdk.CreateWorkspaceRequest{
295347
TemplateVersionID: templateVersionID,
296348
Name: workspaceName,
297349
AutostartSchedule: schedSpec,
298350
TTLMillis: ttlMillis,
299351
RichParameterValues: richParameters,
300352
AutomaticUpdates: codersdk.AutomaticUpdates(autoUpdates),
301-
})
353+
}
354+
355+
// If a preset exists, update the create workspace request's preset ID
356+
if preset != nil {
357+
req.TemplateVersionPresetID = preset.ID
358+
}
359+
360+
workspace, err := client.CreateUserWorkspace(inv.Context(), workspaceOwner, req)
302361
if err != nil {
303362
return xerrors.Errorf("create workspace: %w", err)
304363
}
@@ -333,6 +392,12 @@ func (r *RootCmd) create() *serpent.Command {
333392
Description: "Specify a template version name.",
334393
Value: serpent.StringOf(&templateVersion),
335394
},
395+
serpent.Option{
396+
Flag: "preset",
397+
Env: "CODER_PRESET_NAME",
398+
Description: "Specify a template version preset name. Use 'default' to apply the default preset defined in the template version, if available.",
399+
Value: serpent.StringOf(&presetName),
400+
},
336401
serpent.Option{
337402
Flag: "start-at",
338403
Env: "CODER_WORKSPACE_START_AT",
@@ -377,6 +442,7 @@ type prepWorkspaceBuildArgs struct {
377442
PromptEphemeralParameters bool
378443
EphemeralParameters []codersdk.WorkspaceBuildParameter
379444

445+
PresetParameters []codersdk.WorkspaceBuildParameter
380446
PromptRichParameters bool
381447
RichParameters []codersdk.WorkspaceBuildParameter
382448
RichParameterFile string
@@ -411,6 +477,7 @@ func prepWorkspaceBuild(inv *serpent.Invocation, client *codersdk.Client, args p
411477
WithSourceWorkspaceParameters(args.SourceWorkspaceParameters).
412478
WithPromptEphemeralParameters(args.PromptEphemeralParameters).
413479
WithEphemeralParameters(args.EphemeralParameters).
480+
WithPresetParameters(args.PresetParameters).
414481
WithPromptRichParameters(args.PromptRichParameters).
415482
WithRichParameters(args.RichParameters).
416483
WithRichParametersFile(parameterFile).

0 commit comments

Comments
 (0)