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

fix(cli): make -y flag properly support non-interactive mode in create#21208

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

Open
ThomasK33 wants to merge1 commit intomain
base:main
Choose a base branch
Loading
fromfix-create-non-interactive-y-flag

Conversation

@ThomasK33
Copy link
Member

@ThomasK33ThomasK33 commentedDec 10, 2025
edited
Loading

Previously, the-y flag only bypassed the final "Confirm create?" prompt while still showing interactive prompts for preset selection, parameter input, and template selection. This broke CI/CD and automation workflows that expected-y to enable true non-interactive mode.

Now when-y is passed: workspace name must be an argument, templates auto-select if only one exists (error if multiple), presets default to "none", parameters use their defaults (error if required without default), and external auth must be pre-authenticated.

ScenarioBehavior with-y
Workspace name missingError: provide as argument
Single template availableAuto-select
Multiple templatesError: use--template
Preset selectionSkip (default to none)
Parameters with defaultsUse default
Required param, no defaultError: use--parameter
External auth requiredError: pre-authenticate first
Implementation Plan

Plan: Fix-y Flag to Properly Support Non-Interactive Mode incoder create

Problem

The-y flag claims to "Bypass prompts" but only bypasses the final "Confirm create?" prompt. All other interactive prompts (preset selection, parameter input, template selection, workspace name) still appear, breaking non-interactive/CI environments.

Requirements

  1. If-y is passed,never prompt the user
  2. If no preset explicitly passed with-y,default to "none" (skip presets)
  3. If a parameter has a default value,use it automatically
  4. If a required parameter has no default value,fail with explicit error

Files to Modify

  1. cli/create.go - Main create command logic
  2. cli/parameterresolver.go - Parameter resolution with input prompts
  3. cli/cliui/externalauth.go - External auth handling
  4. cli/cliui/prompt.go - Update flag description (minor)

Implementation Plan

1. Add Helper to Check Non-Interactive Mode

Incli/create.go, add a helper function to check if we're in non-interactive mode:

funcisNonInteractive(inv*serpent.Invocation)bool {ifinv.ParsedFlags().Lookup("yes")!=nil {ifskip,_:=inv.ParsedFlags().GetBool("yes");skip {returntrue        }    }returnfalse}

2. Handle Workspace Name (cli/create.go:77-95)

Current: Prompts if workspace name not provided as argument.

Change: If-y and no workspace name, return an error:

ifworkspaceName=="" {ifisNonInteractive(inv) {returnxerrors.New("workspace name is required in non-interactive mode; provide it as an argument")    }// existing prompt code...}

3. Handle Template Selection (cli/create.go:124-178)

Current: Prompts with template selection if--template not provided.

Change: If-y and no template specified, return an error:

casetemplateName=="":ifisNonInteractive(inv) {returnxerrors.New("template is required in non-interactive mode; use --template flag")    }// existing prompt code...

4. Handle Preset Selection (cli/create.go:293-304)

Current: If presets exist and no--preset flag, prompts for selection.

Change: If-y and no preset specified, default to "none" (skip presets):

iflen(tvPresets)>0&&strings.ToLower(presetName)!=PresetNone {preset,err=resolvePreset(tvPresets,presetName)iferr!=nil {if!errors.Is(err,ErrNoPresetFound) {returnxerrors.Errorf("unable to resolve preset: %w",err)        }// NEW: In non-interactive mode, skip presets instead of promptingifisNonInteractive(inv) {// No preset found and non-interactive - skip presetspreset=nil        }else {// Interactive mode - prompt userifpreset,err=promptPresetSelection(inv,tvPresets);err!=nil {returnxerrors.Errorf("unable to prompt user for preset: %w",err)            }        }    }// ... rest of preset handling}

5. Handle Parameter Input (cli/parameterresolver.go)

Current:resolveWithInput prompts for any unresolved parameters.

Change: Pass non-interactive flag to resolver and handle appropriately.

5a. Add field to ParameterResolver struct:

typeParameterResolverstruct {// ... existing fieldsnonInteractivebool// NEW}func (pr*ParameterResolver)WithNonInteractive(nonInteractivebool)*ParameterResolver {pr.nonInteractive=nonInteractivereturnpr}

5b. ModifyresolveWithInput to respect non-interactive mode:

func (pr*ParameterResolver)resolveWithInput(...) ([]codersdk.WorkspaceBuildParameter,error) {for_,tvp:=rangetemplateVersionParameters {p:=findWorkspaceBuildParameter(tvp.Name,resolved)ifp!=nil {continue        }// Parameter needs resolutionfirstTimeUse:=pr.isFirstTimeUse(tvp.Name)promptParameterOption:=pr.isLastBuildParameterInvalidOption(tvp)needsInput:= (tvp.Ephemeral&&pr.promptEphemeralParameters)||            (action==WorkspaceCreate&&tvp.Required)||            (action==WorkspaceCreate&&!tvp.Ephemeral)||            (action==WorkspaceUpdate&&promptParameterOption)||            (action==WorkspaceUpdate&&tvp.Mutable&&tvp.Required)||            (action==WorkspaceUpdate&&!tvp.Mutable&&firstTimeUse)||            (tvp.Mutable&&!tvp.Ephemeral&&pr.promptRichParameters)ifneedsInput {// NEW: In non-interactive mode, use default or failifpr.nonInteractive {iftvp.DefaultValue!="" {// Use default valueresolved=append(resolved, codersdk.WorkspaceBuildParameter{Name:tvp.Name,Value:tvp.DefaultValue,                    })                }elseiftvp.Required {// Required parameter with no default - failreturnnil,xerrors.Errorf("parameter %q is required but has no default value; "+"provide it with --parameter %s=<value>",tvp.Name,tvp.Name)                }// Optional parameter with no default - skip (will use empty/server default)continue            }// Interactive mode - prompt as beforeparameterValue,err:=cliui.RichParameter(inv,tvp,pr.richParametersDefaults)// ... existing code        }// ... rest of existing logic    }returnresolved,nil}

5c. Update caller incli/create.go to pass non-interactive flag:

resolver:=new(ParameterResolver).// ... existing chainWithNonInteractive(isNonInteractive(inv))

6. Update Flag Description

Incli/cliui/prompt.go, update the description to be accurate:

funcSkipPromptOption() serpent.Option {return serpent.Option{Flag:skipPromptFlag,FlagShorthand:"y",Description:"Run in non-interactive mode. Use default values and fail if required inputs are missing.",Value:serpent.BoolOf(new(bool)),    }}

7. Handle External Auth (cli/create.go:571)

Current: Waits/polls for browser-based authentication if external auth is required.

Change: In non-interactive mode, fail immediately if any required external auth is not already authenticated:

// In prepWorkspaceBuild, modify the ExternalAuth callerr=cliui.ExternalAuth(ctx,inv.Stdout, cliui.ExternalAuthOptions{Fetch:func(ctx context.Context) ([]codersdk.TemplateVersionExternalAuth,error) {returnclient.TemplateVersionExternalAuth(ctx,templateVersion.ID)    },NonInteractive:isNonInteractive(inv),// NEW: pass flag})

Modifycliui.ExternalAuth incli/cliui/externalauth.go to accept a non-interactive flag and fail immediately if required auth is missing:

typeExternalAuthOptionsstruct {Fetchfunc(ctx context.Context) ([]codersdk.TemplateVersionExternalAuth,error)NonInteractivebool// NEW}// In the function body, after fetching auth status:for_,auth:=rangeauths {if!auth.Authenticated&&!auth.Optional {ifo.NonInteractive {returnxerrors.Errorf("external authentication %q is required but not authenticated; "+"authenticate via browser first or ensure the template marks it as optional",auth.DisplayName)        }// ... existing polling/waiting logic    }}

Edge Cases

  1. Ephemeral Parameters: These are build-time options. In non-interactive mode with--prompt-ephemeral-parameters, we should probably fail if ephemeral parameters exist but weren't provided via--ephemeral-parameter flags.

  2. Multi-select Parameters (list(string)): These have defaults that are JSON arrays. The default handling should work, but verify the default value is properly applied.

Testing

  1. Testcoder create workspace -t template -y with a template that has:

    • Presets → should skip presets (default to none)
    • Required parameters with defaults → should use defaults
    • Required parameters without defaults → should fail with clear error
    • Optional parameters → should use defaults or skip
  2. Test error messages are clear and actionable

  3. Test that interactive mode (without-y) still works as before

Summary of Changes

LocationCurrent BehaviorNew Behavior with-y
Workspace namePromptsError: provide as argument
Template selectionPromptsError: use--template
Preset selectionPromptsSkip presets (default to none)
Parameters with defaultsPromptsUse default value
Required params no defaultPromptsError: use--parameter
External auth (required)Waits/pollsError: must pre-authenticate
Confirm createSkipped ✓Skipped ✓ (already works)

🤖 Generated withClaude Code

Co-Authored-By: Claudenoreply@anthropic.com

@bpmct
Copy link
Member

@ThomasK33 I wrote a guide for PRs which should create less AI-like descriptions. However, it seems like claude code doesn't always listen. Any ideas why?https://github.com/coder/coder/blob/main/.claude/docs/PR_STYLE_GUIDE.md

Perhaps these should be skills

@ThomasK33ThomasK33force-pushed thefix-create-non-interactive-y-flag branch from9d5bb50 to34b73d4CompareDecember 10, 2025 15:36
@ThomasK33Graphite App
Copy link
MemberAuthor

I instructed CC to create the PR near the compaction window limit, around 150k tokens. As a result, its recall may no longer be optimal, since all documents have been placed at the front, and tool calls occurred in between.

If we want to optimize for Claude Code, we might consider introducing a sub-agent dedicated to strictly following rules within a much cleaner and more streamlined context window, instead of letting the main session handle the PR description.

The -y flag was documented as 'Bypass prompts' but only bypassed the final'Confirm create?' prompt. All other interactive prompts (preset selection,parameter input, template selection, workspace name) would still appear,breaking CI/CD and non-interactive usage.Changes:- Add isNonInteractive(inv) helper in create.go- Workspace name: error if not provided with -y- Template selection: auto-select if exactly 1 template exists, error if multiple- Preset selection: skip presets entirely (default to none) with -y- Parameters: use default values automatically, error if required param has no default- External auth: error immediately if required auth not authenticated- Update SkipPromptOption() description to accurately describe behavior- Regenerate golden files for updated flag descriptionThe -y flag now enables true non-interactive mode for `coder create`.
@ThomasK33ThomasK33force-pushed thefix-create-non-interactive-y-flag branch from34b73d4 to7e2b111CompareDecember 10, 2025 16:00
@ThomasK33
Copy link
MemberAuthor

@ThomasK33 I wrote a guide for PRs which should create less AI-like descriptions. However, it seems like claude code doesn't always listen. Any ideas why?main/.claude/docs/PR_STYLE_GUIDE.md

Perhaps these should be skills

@bpmct, rerean it on a fresh context, and the PR description is now more aligned with what's in that doc.

bpmct reacted with thumbs up emoji

@ThomasK33ThomasK33 marked this pull request as ready for reviewDecember 10, 2025 16:04
// isNonInteractive checks if the command is running in non-interactive mode
// (i.e., the --yes/-y flag was provided).
funcisNonInteractive(inv*serpent.Invocation)bool {
ifinv.ParsedFlags().Lookup("yes")!=nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

not sure if that’s a pattern or not, but can we not do a double look up of string “yes”, there’s gotta be a better way to handle this

deansheather reacted with thumbs up emoji
Copy link
Member

@deansheatherdeansheather left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Originally I was going to suggest leaving the erroring up tocliui.Select and co., but it's probably better to deal with it in the command handler like you've done so we get better error messages.

Looks good.


// isNonInteractive checks if the command is running in non-interactive mode
// (i.e., the --yes/-y flag was provided).
funcisNonInteractive(inv*serpent.Invocation)bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This should probably go into the cliui package beside the SkipPromptOption function

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@ibetitsmikeibetitsmikeibetitsmike left review comments

@deansheatherdeansheatherdeansheather approved these changes

Assignees

@ThomasK33ThomasK33

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

5 participants

@ThomasK33@bpmct@deansheather@ibetitsmike

[8]ページ先頭

©2009-2025 Movatter.jp