Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(cli): prompt for missing required template variables on template creation#19973

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
rafrdz merged 2 commits intomainfromrafrdz/missing-template-variable
Oct 3, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 158 additions & 3 deletionscli/templatepush.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"time"

Expand DownExpand Up@@ -461,10 +462,14 @@ func createValidTemplateVersion(inv *serpent.Invocation, args createValidTemplat
})
if err != nil {
var jobErr *cliui.ProvisionerJobError
if errors.As(err, &jobErr) && !codersdk.JobIsMissingParameterErrorCode(jobErr.Code) {
return nil, err
if errors.As(err, &jobErr) {
if codersdk.JobIsMissingRequiredTemplateVariableErrorCode(jobErr.Code) {
return handleMissingTemplateVariables(inv, args, version.ID)
}
if !codersdk.JobIsMissingParameterErrorCode(jobErr.Code) {
return nil, err
}
}

return nil, err
}
version, err = client.TemplateVersion(inv.Context(), version.ID)
Expand DownExpand Up@@ -528,3 +533,153 @@ func prettyDirectoryPath(dir string) string {
}
return prettyDir
}

func handleMissingTemplateVariables(inv *serpent.Invocation, args createValidTemplateVersionArgs, failedVersionID uuid.UUID) (*codersdk.TemplateVersion, error) {
client := args.Client

templateVariables, err := client.TemplateVersionVariables(inv.Context(), failedVersionID)
if err != nil {
return nil, xerrors.Errorf("fetch template variables: %w", err)
}

existingValues := make(map[string]string)
for _, v := range args.UserVariableValues {
existingValues[v.Name] = v.Value
}

var missingVariables []codersdk.TemplateVersionVariable
for _, variable := range templateVariables {
if !variable.Required {
continue
}

if existingValue, exists := existingValues[variable.Name]; exists && existingValue != "" {
continue
}

// Only prompt for variables that don't have a default value or have a redacted default
// Sensitive variables have a default value of "*redacted*"
// See: https://github.com/coder/coder/blob/a78790c632974e04babfef6de0e2ddf044787a7a/coderd/provisionerdserver/provisionerdserver.go#L3206
if variable.DefaultValue == "" || (variable.Sensitive && variable.DefaultValue == "*redacted*") {
missingVariables = append(missingVariables, variable)
}
}

if len(missingVariables) == 0 {
return nil, xerrors.New("no missing required variables found")
}

_, _ = fmt.Fprintf(inv.Stderr, "Found %d missing required variables:\n", len(missingVariables))
for _, v := range missingVariables {
_, _ = fmt.Fprintf(inv.Stderr, " - %s (%s): %s\n", v.Name, v.Type, v.Description)
}

_, _ = fmt.Fprintln(inv.Stderr, "\nThe template requires values for the following variables:")

var promptedValues []codersdk.VariableValue
for _, variable := range missingVariables {
value, err := promptForTemplateVariable(inv, variable)
if err != nil {
return nil, xerrors.Errorf("prompt for variable %q: %w", variable.Name, err)
}
promptedValues = append(promptedValues, codersdk.VariableValue{
Name: variable.Name,
Value: value,
})
}

combinedValues := codersdk.CombineVariableValues(args.UserVariableValues, promptedValues)

_, _ = fmt.Fprintln(inv.Stderr, "\nRetrying template build with provided variables...")

retryArgs := args
retryArgs.UserVariableValues = combinedValues

return createValidTemplateVersion(inv, retryArgs)
}

func promptForTemplateVariable(inv *serpent.Invocation, variable codersdk.TemplateVersionVariable) (string, error) {
displayVariableInfo(inv, variable)

switch variable.Type {
case "bool":
return promptForBoolVariable(inv, variable)
case "number":
return promptForNumberVariable(inv, variable)
default:
return promptForStringVariable(inv, variable)
}
}

func displayVariableInfo(inv *serpent.Invocation, variable codersdk.TemplateVersionVariable) {
_, _ = fmt.Fprintf(inv.Stderr, "var.%s", cliui.Bold(variable.Name))
if variable.Required {
_, _ = fmt.Fprint(inv.Stderr, pretty.Sprint(cliui.DefaultStyles.Error, " (required)"))
}
if variable.Sensitive {
_, _ = fmt.Fprint(inv.Stderr, pretty.Sprint(cliui.DefaultStyles.Warn, ", sensitive"))
}
_, _ = fmt.Fprintln(inv.Stderr, "")

if variable.Description != "" {
_, _ = fmt.Fprintf(inv.Stderr, " Description: %s\n", variable.Description)
}
_, _ = fmt.Fprintf(inv.Stderr, " Type: %s\n", variable.Type)
_, _ = fmt.Fprintf(inv.Stderr, " Current value: %s\n", pretty.Sprint(cliui.DefaultStyles.Placeholder, "<empty>"))
}

func promptForBoolVariable(inv *serpent.Invocation, variable codersdk.TemplateVersionVariable) (string, error) {
defaultValue := variable.DefaultValue
if defaultValue == "" {
defaultValue = "false"
}

return cliui.Select(inv, cliui.SelectOptions{
Options: []string{"true", "false"},
Default: defaultValue,
Message: "Select value:",
})
}

func promptForNumberVariable(inv *serpent.Invocation, variable codersdk.TemplateVersionVariable) (string, error) {
prompt := "Enter value:"
if !variable.Required && variable.DefaultValue != "" {
prompt = fmt.Sprintf("Enter value (default: %q):", variable.DefaultValue)
}

return cliui.Prompt(inv, cliui.PromptOptions{
Text: prompt,
Default: variable.DefaultValue,
Validate: createVariableValidator(variable),
})
}

func promptForStringVariable(inv *serpent.Invocation, variable codersdk.TemplateVersionVariable) (string, error) {
prompt := "Enter value:"
if !variable.Sensitive {
if !variable.Required && variable.DefaultValue != "" {
prompt = fmt.Sprintf("Enter value (default: %q):", variable.DefaultValue)
}
}

return cliui.Prompt(inv, cliui.PromptOptions{
Text: prompt,
Default: variable.DefaultValue,
Secret: variable.Sensitive,
Validate: createVariableValidator(variable),
})
}

func createVariableValidator(variable codersdk.TemplateVersionVariable) func(string) error {
return func(s string) error {
if variable.Required && s == "" && variable.DefaultValue == "" {
return xerrors.New("value is required")
}
if variable.Type == "number" && s != "" {
if _, err := strconv.ParseFloat(s, 64); err != nil {
return xerrors.Errorf("must be a valid number, got: %q", s)
}
}
return nil
}
}
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp