Skip to content

feat: show template prerequisites in builder UI#26523

Merged
jeremyruppel merged 11 commits into
mainfrom
jeremy/devex-446-show-template-prerequisites-in-advance-in-ui
Jun 23, 2026
Merged

feat: show template prerequisites in builder UI#26523
jeremyruppel merged 11 commits into
mainfrom
jeremy/devex-446-show-template-prerequisites-in-advance-in-ui

Conversation

@jeremyruppel

@jeremyruppel jeremyruppel commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

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 via strings.Index, and caches both the full README and the prerequisites string.

The prerequisites are served via a new prerequisites field on GET /api/v2/templatebuilder/bases. The full README is included in the composed template tar bundle and stored as the template version readme.

Changes

  • Add README.md with prerequisite markers to coderd/templatebuilder/bases/{docker,kubernetes,aws-linux}/
  • New ExtractPrerequisites() in prerequisites.go using literal string matching
  • bases.go: load README at boot, fail loudly if missing, extract prerequisites
  • compose.go: include README in ComposeResult and tar bundle
  • codersdk: add Prerequisites field to TemplateBuilderBase
  • Handler: populate prerequisites in bases response, set readme on template version
Implementation notes
  • Prerequisites extraction uses strings.Index for exact literal marker matching; no regex or AST parser needed since we control the markers.
  • YAML frontmatter is deliberately retained in the stored README. The frontend TemplateDocsPage already strips it at render time via front-matter.
  • The prerequisite markers are HTML comments, invisible in rendered markdown.
  • The RejectsMissingReadme test enforces that every base template must include a README.
  • AWS Linux prerequisites span two H2 sections (## Prerequisites and ## 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

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
@linear-code

linear-code Bot commented Jun 18, 2026

Copy link
Copy Markdown

DEVEX-446

@github-actions

Copy link
Copy Markdown

Docs preview

📖 View docs preview for docs/reference/api/schemas.md

@datadog-coder

This comment has been minimized.

@jeremyruppel jeremyruppel force-pushed the jeremy/devex-446-show-template-prerequisites-in-advance-in-ui branch from dcbc7b1 to 8560438 Compare June 18, 2026 15:24

Provision AWS EC2 VMs as [Coder workspaces](https://coder.com/docs/user-guides/workspace-management) with this example template.

<!-- prerequisites:start -->

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

these are the markers that need to be added by hand to each README that has prerequisites

Comment on lines +119 to +123
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)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

base templates should always have a README file, which may or may not contain prerequisites

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we expect an empty prerequisites section if none exist (as opposed to missing prerequisites) to get a confirmed signal of not having any?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

that's a good idea, I'll ask on Tracy the figma

@jeremyruppel jeremyruppel changed the title feat(coderd/templatebuilder): show template prerequisites in builder UI feat: show template prerequisites in builder UI Jun 18, 2026
Comment on lines +108 to +114
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)
}
})

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

@jeremyruppel jeremyruppel requested a review from aslilac June 18, 2026 15:52
Comment on lines +119 to +123
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.
@jeremyruppel jeremyruppel force-pushed the jeremy/devex-287-be-base-variable-rendering branch from e0a04c0 to 342778b Compare June 23, 2026 14:36
…y/devex-446-show-template-prerequisites-in-advance-in-ui
Base automatically changed from jeremy/devex-287-be-base-variable-rendering to main June 23, 2026 15:26
@jeremyruppel jeremyruppel merged commit 1071757 into main Jun 23, 2026
37 of 40 checks passed
@jeremyruppel jeremyruppel deleted the jeremy/devex-446-show-template-prerequisites-in-advance-in-ui branch June 23, 2026 15:36
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants