feat: show template prerequisites in builder UI#26523
Conversation
Implement the module settings wizard step: - Add ModuleSettingsStep component that renders a ModuleConfiguration card per selected module with configurable variable fields - Map non-sensitive variables to ConfigurationField definitions (switch for bool, text input for string/number) - Show info notice for sensitive variables indicating they will be collected from developers at workspace creation - Export moduleSettingsComplete helper to validate required fields - Extend canContinue in PageView to gate module-settings step - Wire the step into TemplateBuilderPageView with SET_MODULE_VARIABLES dispatch
…URL default Terraform module registry source addresses must not include a URL scheme. The default value 'https://registry.coder.com' produced invalid source paths like 'https://registry.coder.com/coder/code-server/coder', causing 'terraform init' to fail with 'failed to parse module registry address'. Change the default to 'registry.coder.com'.
… declarations Sensitive variable blocks in module .tf.tmpl files and the modulegen script were missing default = "", causing terraform plan to prompt for values during template import.
The backend now accepts raw string values from callers (e.g. "anthropic") and wraps them in HCL quotes automatically via hclQuote(). Previously callers were required to send pre-quoted HCL literals, which is not a reasonable API contract for frontend consumers. - validateStringValue now validates raw strings (rejects interpolation and overlong values) - toHCLLiteral wraps string values in quotes with proper escaping - hclQuote handles backslash, quote, newline, and carriage return escaping - null is passed through unquoted for all types
…and compose POST Implement the final wizard step and template creation flow: - Add createTemplateFromBuilder API client method and react-query mutation for POST /api/v2/templatebuilder/compose/template - Add TemplateCustomizationsStep with organization picker (OrganizationAutocomplete), name, display name, description, and icon picker (IconField) fields - Extend wizard state with name and icon fields; add toCreateTemplateRequest projection - Wire Create Template button to mutation; on success redirect to the new template's files page with justCreated state - Show mutation error inline on the customizations step - Disable Create Template button while mutation is pending - Require non-empty name to proceed on customizations step
…ct step renderer Move data-fetching concerns out of the PageView into the Page: - Move useQuery (bases) and useMutation (createTemplate) to TemplateBuilderPage via a container component - Pass resolved data and callbacks as props to TemplateBuilderPageView - Extract inline ternary chain into renderStepContent switch function - Extract canContinue logic into computeCanContinue switch function - Export StepId from steps.ts for use in the switch functions
…into Page Remove the unnecessary TemplateBuilderPageViewContainer component. All query/mutation state now lives directly in TemplateBuilderPage.
…tead of tfvars
The kubernetes base template used Terraform variable blocks and var.*
references for use_kubeconfig and namespace, but the composed tar bundle
never included a .tfvars file, so terraform plan failed with 'required
template variables need values'.
Fix: base templates now use Go template variables ({{ .Variables.* }})
like module templates do. renderBase() validates and HCL-quotes base
variable values via mergeBaseVariables(). DefaultBaseRenderContext()
populates default variable values from the manifest.
Also adds explicit 'variable is required' validation to both
mergeBaseVariables and mergeModuleVariables, replacing the previous
reliance on missingkey=error at render time.
…mplates Add README.md files to each base template directory (docker, kubernetes, aws-linux) with HTML comment markers (<!-- prerequisites:start --> and <!-- prerequisites:end -->) to delimit the prerequisites section. At boot time, the base catalog loader reads the README and extracts the prerequisites content between the markers using simple string literal matching. This content is served via a new 'prerequisites' field on the GET /api/v2/templatebuilder/bases response. The full README is also included in the composed template tar bundle (README.md) and set on the template version row, so builder-created templates have proper documentation visible in the Coder UI. Part of DEVEX-446
Docs preview📖 View docs preview for |
This comment has been minimized.
This comment has been minimized.
dcbc7b1 to
8560438
Compare
|
|
||
| Provision AWS EC2 VMs as [Coder workspaces](https://coder.com/docs/user-guides/workspace-management) with this example template. | ||
|
|
||
| <!-- prerequisites:start --> |
There was a problem hiding this comment.
these are the markers that need to be added by hand to each README that has prerequisites
| readmeData, err := fs.ReadFile(baseFS, "README.md") | ||
| if err != nil { | ||
| return nil, xerrors.Errorf("read README.md for base %q: %w", manifest.ID, err) | ||
| } | ||
| readme := string(readmeData) |
There was a problem hiding this comment.
base templates should always have a README file, which may or may not contain prerequisites
There was a problem hiding this comment.
Should we expect an empty prerequisites section if none exist (as opposed to missing prerequisites) to get a confirmed signal of not having any?
There was a problem hiding this comment.
that's a good idea, I'll ask on Tracy the figma
| t.Run("KnownBasesHaveReadme", func(t *testing.T) { | ||
| t.Parallel() | ||
| for _, id := range templatebuilder.BaseTemplateIDs() { | ||
| readme := templatebuilder.BaseReadme(id) | ||
| require.NotEmpty(t, readme, "base %q should have a README", id) | ||
| } | ||
| }) |
There was a problem hiding this comment.
the is the test-time check for READMEs in each base template; the embed time check will also fail if no README is present in the base template directory
| readmeData, err := fs.ReadFile(baseFS, "README.md") | ||
| if err != nil { | ||
| return nil, xerrors.Errorf("read README.md for base %q: %w", manifest.ID, err) | ||
| } | ||
| readme := string(readmeData) |
There was a problem hiding this comment.
Should we expect an empty prerequisites section if none exist (as opposed to missing prerequisites) to get a confirmed signal of not having any?
…tead of tfvars
The kubernetes base template used Terraform variable blocks and var.*
references for use_kubeconfig and namespace, but the composed tar bundle
never included a .tfvars file, so terraform plan failed with 'required
template variables need values'.
Fix: base templates now use Go template variables ({{ .Variables.* }})
like module templates do. renderBase() validates and HCL-quotes base
variable values via mergeBaseVariables(). DefaultBaseRenderContext()
populates default variable values from the manifest.
Also adds explicit 'variable is required' validation to both
mergeBaseVariables and mergeModuleVariables, replacing the previous
reliance on missingkey=error at render time.
e0a04c0 to
342778b
Compare
…y/devex-446-show-template-prerequisites-in-advance-in-ui

Surface base template prerequisites to admins before they create a template in the Template Builder wizard.
Today, template prerequisites (Docker socket setup, Kubernetes auth, AWS IAM policies) are only visible in the registry README after import. Admins hit opaque provisioner errors and have to hunt for docs. This change extracts the prerequisites from the README and serves them via the API so the frontend can display them inline.
How it works
Each base template README uses HTML comment markers (
<!-- prerequisites:start -->/<!-- prerequisites:end -->) to delimit the prerequisites section. At boot time, the base catalog loader reads the README, extracts the content between markers viastrings.Index, and caches both the full README and the prerequisites string.The prerequisites are served via a new
prerequisitesfield onGET /api/v2/templatebuilder/bases. The full README is included in the composed template tar bundle and stored as the template version readme.Changes
README.mdwith prerequisite markers tocoderd/templatebuilder/bases/{docker,kubernetes,aws-linux}/ExtractPrerequisites()inprerequisites.gousing literal string matchingbases.go: load README at boot, fail loudly if missing, extract prerequisitescompose.go: include README inComposeResultand tar bundlecodersdk: addPrerequisitesfield toTemplateBuilderBaseImplementation notes
strings.Indexfor exact literal marker matching; no regex or AST parser needed since we control the markers.TemplateDocsPagealready strips it at render time viafront-matter.RejectsMissingReadmetest enforces that every base template must include a README.## Prerequisitesand## Required permissions / policy), which is why heading-based parsing was rejected in favor of explicit markers.Generated with the assistance of an AI coding agent. Reviewed by @jeremyruppel.
Relates to https://linear.app/codercom/issue/DEVEX-446