feat: add template builder module catalog structure and go:embed wiring#25909
feat: add template builder module catalog structure and go:embed wiring#25909jeremyruppel wants to merge 9 commits into
Conversation
48e91e9 to
bf131f3
Compare
|
/coder-agents-review |
There was a problem hiding this comment.
Clean scaffolding with good structural decisions: the two-type-layer design (internal manifest vs. SDK response) is well-considered, the ToSDK() conversion is explicit, and the README documents the design rationale thoroughly. Tests are parallel and use subtests properly. The go:embed wiring is correct.
4 P2, 7 P3, 3 Nit, 2 Note. The P2s cluster around two themes: the cached slice is mutable (CRF-2), and the parse/validation pipeline trusts its input too much (CRF-3, CRF-4, CRF-5). Most findings resolve with a validation pass in parseModules() and a defensive copy in LoadModules().
Ryosuke put it well: "parseModules() should validate and LoadModules() should return immutable data. If the parse step rejects malformed manifests and the return path clones or pre-converts the slice, the mutable-cache, ghost-entry, duplicate-ID, and invalid-type findings all disappear."
PS. Gon audited all 10 comments in the PR and found 8 of them restate the type name without adding information. The godoc convention for exported types is worth following, but consider trimming the ones that decompose the identifier into words (e.g., "TemplateBuilderVariableType represents the type of a template builder variable").
🤖 This review was automatically generated with Coder Agents.
| these; they become bare Terraform `variable` blocks so values are supplied | ||
| at workspace creation time. | ||
| - **`pinned_version`**: The exact module version shipped with this Coder | ||
| release. The compose endpoint always emits this version; `latest` is never |
There was a problem hiding this comment.
Note [CRF-16] Statements like "The compose endpoint always emits this version", "Matched against the base template's OS to filter incompatible modules", and "The UI surfaces a warning but does not block selection" describe behavior that doesn't exist yet. Reasonable for a scaffolding design doc, but someone reading this README cold will think these features work now. (Leorio)
🤖
There was a problem hiding this comment.
Fixed. README now uses future tense ("will inject", "will be matched", etc.) for behavior that depends on unimplemented endpoints.
There was a problem hiding this comment.
this does not bode well for the README idea
There was a problem hiding this comment.
Round 2 is blocked. No new commits since R1 (head SHA unchanged) and no author responses on any of the 17 open findings.
Unaddressed findings (4 P2, 7 P3, 3 Nit, 2 Note):
- CRF-2 (P2) LoadModules returns mutable cached slice
- CRF-3 (P2) Unvalidated type cast to TemplateBuilderVariableType
- CRF-4 (P2) Stub module ships in production binary
- CRF-5 (P2) ToSDK test assertions cover 4 of 14 fields
- CRF-1 (P3) Nil-to-empty normalization missing
- CRF-6 (P3) "First successful call" comment lie
- CRF-7 (P3) Pre-1.21 sync.Once pattern
- CRF-8 (P3) No ID uniqueness/validation
- CRF-9 (P3) Fixture doesn't exercise all schema fields
- CRF-10 (P3) TestToSDK coupled to LoadModules
- CRF-11 (P3) ReadFile error swallowed
- CRF-18 (P3) RFC schema claim unverifiable
- CRF-12 (Nit) Error message jargon
- CRF-13 (Nit) Vague embed variable name
- CRF-14 (Nit) Zero-value godoc comments
- CRF-16 (Note) README describes unimplemented behavior
- CRF-17 (Note) sync.Once blocks test isolation
Further review is blocked until the author responds to or pushes fixes for these findings.
🤖 This review was automatically generated with Coder Agents.
8ee14bc to
ebd6c99
Compare
|
/coder-agents-review |
There was a problem hiding this comment.
Substantial improvement. 16 of 17 R1 findings verified fixed. The ParseModulesFromFS(fs.FS) refactor, type validation, fstest.MapFS test fixtures, and real code-server module are all well-executed. Test density went from 31% to 50%. Bisky on the new test suite: "That one was costume jewelry. This one is real."
One R1 finding re-opened: CRF-2 (mutable cached slice). Five reviewers independently verified that sync.OnceValues returns the same slice reference on every call. The initialization ergonomics improved (CRF-7 fix), but the mutation safety issue persists. Four new P3s from the panel.
R3 severity count: 1 P3 re-opened (CRF-2), 4 new P3, 2 new Nit.
CRF-24 (P3, not posted inline): The PR description still says "Stub module exercising all schema fields" but the stub was replaced with code-server in this revision. If squash-merged, the permanent commit body describes code that no longer exists. Update the description. (Leorio)
🤖 This review was automatically generated with Coder Agents.
ccbe603 to
cc381e1
Compare
code-asher
left a comment
There was a problem hiding this comment.
I just gave a cursory glance for now but I left some thoughts on the RFC too
| return nil, xerrors.Errorf("list module catalog entries: %w", err) | ||
| } | ||
|
|
||
| seen := make(map[string]bool) |
There was a problem hiding this comment.
| seen := make(map[string]bool) | |
| seen := make(map[string]struct{}) |
I sometimes feel like this is an over optimization, but I have seen other places in the code where when using a map like a set we use struct{} as the value type, the benefit being slightly better memory efficiency because struct{} is 0 bytes but bool is like 4 or 8 or something.
then to add something to the set is seen[manifest.ID] = struct{}{} which is hilariously ugly
There was a problem hiding this comment.
Ouch my eyes! 😅 If we currently do this everywhere else (The Coder Way™️) then I'm down to make this change, but imo this reads pretty poorly, and unless we are really counting our bytes it doesn't feel like this will even move the needle. Strong opinion loosely held, though!
| } | ||
| seen[manifest.ID] = true | ||
|
|
||
| seenVars := make(map[string]bool) |
…ed wiring Scaffolds the coderd/templatebuilder package for the guided template builder (DEVEX-272). Adds: - codersdk.TemplateBuilderModule and related types matching the RFC schema - ModuleManifest internal type with go:embed wiring for bundled catalog - LoadModules() to parse module.json files from embedded FS - ToSDK() conversion from on-disk manifest to API response type - Stub module exercising all schema fields (builder_managed, sensitive, conflicts_with, compatible_os, pinned_version) - Tests for catalog loading, field parsing, and SDK conversion
- 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
it was a bad idea
Co-authored-by: McKayla はな <mckayla@hey.com>
…ternal test - Unexport ParseModulesFromFS to parseModulesFromFS since it is only used internally and in tests. - Move TestParseModulesFromFS into catalog_internal_test.go (package templatebuilder) so it can call the unexported function directly. - Fix go:embed directive: remove invalid trailing slash from pattern.
Co-authored-by: McKayla はな <mckayla@hey.com>
Rename BuilderManaged/builder_managed to Computed/computed across Go structs, JSON tags, module manifests, and tests. Regenerate TypeScript types.
…ctory A directory under modules/ without a module.json now returns an error instead of being silently skipped. The embed directive already excludes empty directories, so a dir without a manifest likely indicates a missing or misnamed file.
2a73c93 to
7bead3b
Compare

Scaffolds the
coderd/templatebuilderpackage for the guided template builder (DEVEX-272, RFC).Adds the module catalog types and
go:embedwiring that the template builder endpoints will use:codersdk.TemplateBuilderModule,TemplateBuilderModuleVariable, and related types matching the RFC schemaModuleManifesttype withgo:embedwiring to bundlemodule.jsonfiles fromcoderd/templatebuilder/modules/LoadModules()with defensive copy, unexportedparseModulesFromFS(fs.FS)for test isolation,ToSDK()conversioncode-servermodule manifest as the first catalog entryDisallowUnknownFields, and requiringmodule.jsonin every module directorycatalog_internal_test.go(forparseModulesFromFSwithfstest.MapFSfixtures) and externalcatalog_test.go(forLoadModulesandToSDK), covering multi-module parsing, all variable types, validation errors, nil-slice normalization, and full SDK field assertionsNote
Generated with Coder Agents by @jeremyruppel