- Replace stub module with real code-server module manifest - Export ParseModulesFromFS(fs.FS) for test isolation (no sync.Once coupling) - Use sync.OnceValues for the cached loader - Validate module ID (non-empty, unique), pinned_version, and variable types during parsing; reject unknown types at load time - Normalize nil slices to empty in ToSDK() to prevent null in JSON responses - Distinguish fs.ErrNotExist from other ReadFile errors - Improve error messages to describe what failed, not internal plumbing - Rewrite tests with fstest.MapFS fixtures covering all variable types, default pointer, validation errors, and full SDK field assertions - Trim zero-value godoc comments - Add future tense to README for unimplemented behavior; link RFC
3.6 KiB
templatebuilder
Package templatebuilder implements the bundled module catalog for the guided
template creation workflow
(RFC).
It embeds module metadata (module.json manifests) into the Coder binary via
go:embed and provides functions to load and convert them for the API layer.
Directory layout
coderd/templatebuilder/
catalog.go # embed.FS, manifest types, LoadModules(), ToSDK()
catalog_test.go
modules/
<module-id>/
module.json # module manifest (on-disk schema)
Each subdirectory under modules/ represents a single catalog entry. The
directory must contain a module.json file. Directories without one are
silently skipped.
module.json schema
{
"id": "code-server", // unique module identifier
"display_name": "code-server", // human-readable name
"description": "VS Code in the browser",
"icon": "...", // path or URL to icon asset
"category": "IDE", // grouping in the UI
"tags": ["ide", "web"], // internal tags (not exposed in API)
"compatible_os": ["linux"], // OS filter against base template
"conflicts_with": [], // module IDs that conflict
"pinned_version": "1.2.3", // exact version bundled with this release
"variables": [
{
"name": "agent_id",
"type": "string", // "string" | "number" | "bool"
"description": "The Coder agent ID.",
"required": true,
"sensitive": false,
"builder_managed": true // wired automatically, hidden from users
},
{
"name": "port",
"type": "number",
"description": "Port to run code-server on.",
"default": "13337",
"required": false,
"sensitive": false,
"builder_managed": false
}
]
}
Key fields:
builder_managed: Variables the compose engine will inject automatically (e.g.agent_id). These should not be shown to users.sensitive: Variables containing secrets. The builder will not collect these; they will become bare Terraformvariableblocks so values are supplied at a later stage.pinned_version: The exact module version shipped with this Coder release. The compose endpoint will always emit this version;latestis never used.compatible_os: Will be matched against the base template's OS to filter incompatible modules.conflicts_with: Module IDs that should not be selected together. The UI will surface a warning but not block selection.
Two type layers
| Type | Location | Purpose |
|---|---|---|
ModuleManifest / ModuleVariable |
catalog.go |
On-disk module.json schema. Has pinned_version, tags. |
codersdk.TemplateBuilderModule / codersdk.TemplateBuilderModuleVariable |
codersdk/templatebuilder.go |
API response type. Has version (mapped from pinned_version). No tags. |
ModuleManifest.ToSDK() handles the conversion.
Adding a module
- Create
modules/<module-id>/module.jsonfollowing the schema above. - Run
go build ./coderd/templatebuilder/to verify the embed compiles. - Run
go test ./coderd/templatebuilder/to verify parsing and validation.
The catalog is bundled at build time. New modules or version bumps require a Coder release to appear in the builder.
Testing
ParseModulesFromFS(fs.FS) accepts an arbitrary filesystem, so tests can
supply custom fixtures via fstest.MapFS without modifying the embedded
production catalog.