Skip to content

Commit e63b201

Browse files
committed
Add --use-parameter-defaults flag
This will accept parameter defaults without prompting (whether those defaults come from the template or --parameter-default). Similar to --yes, but for parameter prompts.
1 parent dce1ff3 commit e63b201

File tree

8 files changed

+180
-21
lines changed

8 files changed

+180
-21
lines changed

cli/cliui/parameter.go

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,8 @@ import (
1010
"github.com/coder/serpent"
1111
)
1212

13-
func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.TemplateVersionParameter, defaultOverrides map[string]string) (string, error) {
14-
label := templateVersionParameter.Name
15-
if templateVersionParameter.DisplayName != "" {
16-
label = templateVersionParameter.DisplayName
17-
}
18-
13+
func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.TemplateVersionParameter, name, defaultValue string) (string, error) {
14+
label := name
1915
if templateVersionParameter.Ephemeral {
2016
label += pretty.Sprint(DefaultStyles.Warn, " (build option)")
2117
}
@@ -26,11 +22,6 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te
2622
_, _ = fmt.Fprintln(inv.Stdout, " "+strings.TrimSpace(strings.Join(strings.Split(templateVersionParameter.DescriptionPlaintext, "\n"), "\n "))+"\n")
2723
}
2824

29-
defaultValue := templateVersionParameter.DefaultValue
30-
if v, ok := defaultOverrides[templateVersionParameter.Name]; ok {
31-
defaultValue = v
32-
}
33-
3425
var err error
3526
var value string
3627
switch {

cli/create.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command {
4343
stopAfter time.Duration
4444
workspaceName string
4545

46-
parameterFlags workspaceParameterFlags
47-
autoUpdates string
48-
copyParametersFrom string
46+
parameterFlags workspaceParameterFlags
47+
autoUpdates string
48+
copyParametersFrom string
49+
useParameterDefaults bool
4950
// Organization context is only required if more than 1 template
5051
// shares the same name across multiple organizations.
5152
orgContext = NewOrganizationContext()
@@ -309,7 +310,7 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command {
309310
displayAppliedPreset(inv, preset, presetParameters)
310311
} else {
311312
// Inform the user that no preset was applied
312-
_, _ = fmt.Fprintf(inv.Stdout, "%s", cliui.Bold("No preset applied."))
313+
_, _ = fmt.Fprintf(inv.Stdout, "%s\n", cliui.Bold("No preset applied."))
313314
}
314315

315316
if opts.BeforeCreate != nil {
@@ -330,6 +331,8 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command {
330331
RichParameterDefaults: cliBuildParameterDefaults,
331332

332333
SourceWorkspaceParameters: sourceWorkspaceParameters,
334+
335+
UseParameterDefaults: useParameterDefaults,
333336
})
334337
if err != nil {
335338
return xerrors.Errorf("prepare build: %w", err)
@@ -436,6 +439,12 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command {
436439
Description: "Specify the source workspace name to copy parameters from.",
437440
Value: serpent.StringOf(&copyParametersFrom),
438441
},
442+
serpent.Option{
443+
Flag: "use-parameter-defaults",
444+
Env: "CODER_WORKSPACE_USE_PARAMETER_DEFAULTS",
445+
Description: "Automatically accept parameter defaults when no value is provided.",
446+
Value: serpent.BoolOf(&useParameterDefaults),
447+
},
439448
cliui.SkipPromptOption(),
440449
)
441450
cmd.Options = append(cmd.Options, parameterFlags.cliParameters()...)
@@ -460,6 +469,8 @@ type prepWorkspaceBuildArgs struct {
460469
RichParameters []codersdk.WorkspaceBuildParameter
461470
RichParameterFile string
462471
RichParameterDefaults []codersdk.WorkspaceBuildParameter
472+
473+
UseParameterDefaults bool
463474
}
464475

465476
// resolvePreset returns the preset matching the given presetName (if specified),
@@ -562,7 +573,8 @@ func prepWorkspaceBuild(inv *serpent.Invocation, client *codersdk.Client, args p
562573
WithPromptRichParameters(args.PromptRichParameters).
563574
WithRichParameters(args.RichParameters).
564575
WithRichParametersFile(parameterFile).
565-
WithRichParametersDefaults(args.RichParameterDefaults)
576+
WithRichParametersDefaults(args.RichParameterDefaults).
577+
WithUseParameterDefaults(args.UseParameterDefaults)
566578
buildParameters, err := resolver.Resolve(inv, args.Action, templateVersionParameters)
567579
if err != nil {
568580
return nil, err

cli/create_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,108 @@ func TestCreateWithRichParameters(t *testing.T) {
545545
},
546546
withDefaults: true,
547547
},
548+
{
549+
name: "ValuesFromTemplateDefaultsNoPrompt",
550+
setup: func() []string {
551+
return []string{"--use-parameter-defaults"}
552+
},
553+
handlePty: func(pty *ptytest.PTY) {
554+
// Default values should get printed.
555+
for _, param := range params {
556+
pty.ExpectMatch(fmt.Sprintf("%s: '%s'", param.name, param.value))
557+
}
558+
// No prompts, we only need to confirm.
559+
pty.ExpectMatch("Confirm create?")
560+
pty.WriteLine("yes")
561+
},
562+
withDefaults: true,
563+
},
564+
{
565+
name: "ValuesFromDefaultFlagsNoPrompt",
566+
setup: func() []string {
567+
// Provide the defaults on the command line.
568+
args := []string{"--use-parameter-defaults"}
569+
for _, param := range params {
570+
args = append(args, "--parameter-default", fmt.Sprintf("%s=%s", param.name, param.value))
571+
}
572+
return args
573+
},
574+
handlePty: func(pty *ptytest.PTY) {
575+
// Default values should get printed.
576+
for _, param := range params {
577+
pty.ExpectMatch(fmt.Sprintf("%s: '%s'", param.name, param.value))
578+
}
579+
// No prompts, we only need to confirm.
580+
pty.ExpectMatch("Confirm create?")
581+
pty.WriteLine("yes")
582+
},
583+
},
584+
{
585+
// File and flags should override template defaults. Additionally, if a
586+
// value has no default value we should still get a prompt for it.
587+
name: "ValuesFromMultipleSources",
588+
setup: func() []string {
589+
tempDir := t.TempDir()
590+
removeTmpDirUntilSuccessAfterTest(t, tempDir)
591+
parameterFile, _ := os.CreateTemp(tempDir, "testParameterFile*.yaml")
592+
_, err := parameterFile.WriteString(`
593+
file_param: from file
594+
cli_param: from file`)
595+
require.NoError(t, err)
596+
return []string{
597+
"--use-parameter-defaults",
598+
"--rich-parameter-file", parameterFile.Name(),
599+
"--parameter-default", "file_param=from cli default",
600+
"--parameter-default", "cli_param=from cli default",
601+
"--parameter", "cli_param=from cli",
602+
}
603+
},
604+
handlePty: func(pty *ptytest.PTY) {
605+
// Should get prompted for the input param since it has no default.
606+
pty.ExpectMatch("input_param")
607+
pty.WriteLine("from input")
608+
609+
// Confirm the creation.
610+
pty.ExpectMatch("Confirm create?")
611+
pty.WriteLine("yes")
612+
},
613+
withDefaults: true,
614+
inputParameters: []param{
615+
{
616+
name: "template_param",
617+
value: "from template default",
618+
},
619+
{
620+
name: "file_param",
621+
value: "from template default",
622+
},
623+
{
624+
name: "cli_param",
625+
value: "from template default",
626+
},
627+
{
628+
name: "input_param",
629+
},
630+
},
631+
expectedParameters: []param{
632+
{
633+
name: "template_param",
634+
value: "from template default",
635+
},
636+
{
637+
name: "file_param",
638+
value: "from file",
639+
},
640+
{
641+
name: "cli_param",
642+
value: "from cli",
643+
},
644+
{
645+
name: "input_param",
646+
value: "from input",
647+
},
648+
},
649+
},
548650
}
549651

550652
for _, tt := range tests {

cli/parameterresolver.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type ParameterResolver struct {
3434

3535
promptRichParameters bool
3636
promptEphemeralParameters bool
37+
useParameterDefaults bool
3738
}
3839

3940
func (pr *ParameterResolver) WithLastBuildParameters(params []codersdk.WorkspaceBuildParameter) *ParameterResolver {
@@ -86,8 +87,21 @@ func (pr *ParameterResolver) WithPromptEphemeralParameters(promptEphemeralParame
8687
return pr
8788
}
8889

89-
// Resolve gathers workspace build parameters in a layered fashion, applying values from various sources
90-
// in order of precedence: parameter file < CLI/ENV < source build < last build < preset < user input.
90+
func (pr *ParameterResolver) WithUseParameterDefaults(useParameterDefaults bool) *ParameterResolver {
91+
pr.useParameterDefaults = useParameterDefaults
92+
return pr
93+
}
94+
95+
// Resolve gathers workspace build parameters in a layered fashion, applying
96+
// values from various sources in order of precedence:
97+
// 1. template defaults (if auto-accepting defaults)
98+
// 2. cli parameter defaults (if auto-accepting defaults)
99+
// 3. parameter file
100+
// 4. CLI/ENV
101+
// 5. source build
102+
// 6. last build
103+
// 7. preset
104+
// 8. user input (unless auto-accepting defaults)
91105
func (pr *ParameterResolver) Resolve(inv *serpent.Invocation, action WorkspaceCLIAction, templateVersionParameters []codersdk.TemplateVersionParameter) ([]codersdk.WorkspaceBuildParameter, error) {
92106
var staged []codersdk.WorkspaceBuildParameter
93107
var err error
@@ -262,9 +276,25 @@ func (pr *ParameterResolver) resolveWithInput(resolved []codersdk.WorkspaceBuild
262276
(action == WorkspaceUpdate && tvp.Mutable && tvp.Required) ||
263277
(action == WorkspaceUpdate && !tvp.Mutable && firstTimeUse) ||
264278
(tvp.Mutable && !tvp.Ephemeral && pr.promptRichParameters) {
265-
parameterValue, err := cliui.RichParameter(inv, tvp, pr.richParametersDefaults)
266-
if err != nil {
267-
return nil, err
279+
name := tvp.Name
280+
if tvp.DisplayName != "" {
281+
name = tvp.DisplayName
282+
}
283+
284+
parameterValue := tvp.DefaultValue
285+
if v, ok := pr.richParametersDefaults[tvp.Name]; ok {
286+
parameterValue = v
287+
}
288+
289+
// Auto-accept the default if there is one.
290+
if pr.useParameterDefaults && parameterValue != "" {
291+
_, _ = fmt.Fprintf(inv.Stdout, "Using default value for %s: '%s'\n", name, parameterValue)
292+
} else {
293+
var err error
294+
parameterValue, err = cliui.RichParameter(inv, tvp, name, parameterValue)
295+
if err != nil {
296+
return nil, err
297+
}
268298
}
269299

270300
resolved = append(resolved, codersdk.WorkspaceBuildParameter{

cli/testdata/coder_create_--help.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ OPTIONS:
4949
--template-version string, $CODER_TEMPLATE_VERSION
5050
Specify a template version name.
5151

52+
--use-parameter-defaults bool, $CODER_WORKSPACE_USE_PARAMETER_DEFAULTS
53+
Automatically accept parameter defaults when no value is provided.
54+
5255
-y, --yes bool
5356
Bypass confirmation prompts.
5457

docs/reference/cli/create.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/cli/external-workspaces_create.md

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enterprise/cli/testdata/coder_external-workspaces_create_--help.golden

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ OPTIONS:
4949
--template-version string, $CODER_TEMPLATE_VERSION
5050
Specify a template version name.
5151

52+
--use-parameter-defaults bool, $CODER_WORKSPACE_USE_PARAMETER_DEFAULTS
53+
Automatically accept parameter defaults when no value is provided.
54+
5255
-y, --yes bool
5356
Bypass confirmation prompts.
5457

0 commit comments

Comments
 (0)