Files
coder/coderd/templatebuilder/prerequisites.go
T
Jeremy RuppelandAndrew Aquino 52142e6e94 fix(coderd/templatebuilder): strip prerequisite markers from delivered README (#28953)
## Problem

Template builder base READMEs delimit their prerequisites section with
HTML comment markers (`<!-- prerequisites:start -->` / `<!--
prerequisites:end -->`). `ExtractPrerequisites` uses these to populate
the builder's Prerequisites step, which works correctly.

However, `Compose` embeds the **full** base README verbatim into the
generated template's tarball. Because `react-markdown` renders raw HTML
comments as literal text, the markers were visible when the delivered
README is viewed via `MemoizedMarkdown`.

<img width="876" height="586" alt="Screenshot 2026-09-03 at 3 28 08 PM"
src="https://github.com/user-attachments/assets/70353ee2-1ff6-4e70-9b5d-7ac0521b945f"
/>

## Fix

Strip the prerequisite markers from the README before writing it into
the tarball (Option 2), keeping the change contained to the template
builder and leaving global markdown rendering untouched.

- `prerequisites.go`: new `StripPrerequisiteMarkers` removes each marker
line (plus an optional trailing blank line so no gap is left) while
preserving the content between them.
- `compose.go`: new `composedReadme` helper applies the strip in both
`Compose` return paths; returns `nil` for an empty README, preserving
the existing "omit README.md when empty" behavior.
- `ExtractPrerequisites` still reads the markers from the source
`BaseReadme`, so the builder's Prerequisites step is unaffected.

## Testing

- `TestStripPrerequisiteMarkers` covers both markers, no markers, single
markers, missing trailing blank line, and end-of-file cases.
- Extended the compose test to assert the delivered README contains no
marker text.
- `go test ./coderd/templatebuilder/`, `go vet`, and `golangci-lint run
./coderd/templatebuilder/...` all pass.

<details>
<summary>Approach considered</summary>

Two options were on the table:

1. Skip HTML comments in the shared `MemoizedMarkdown` component (fixes
any stray comment globally, but changes shared rendering behavior
app-wide).
2. Strip the markers from the README before inserting it into the
template zipball (targeted; markers have no purpose in the delivered
README since prerequisites are already extracted separately).

Option 2 was chosen for being contained and easily unit-tested.
</details>

---
_This PR was created by Coder Agents on behalf of @jeremyruppel._

---------

Co-authored-by: Andrew Aquino <dawneraq@gmail.com>
2026-09-04 19:14:10 +00:00

46 lines
1.5 KiB
Go

package templatebuilder
import (
"regexp"
"strings"
)
const (
// prerequisitesStartMarker delimits the beginning of the prerequisites
// section inside a base template README.md.
prerequisitesStartMarker = "<!-- prerequisites:start -->"
// prerequisitesEndMarker delimits the end of the prerequisites section.
prerequisitesEndMarker = "<!-- prerequisites:end -->"
)
// ExtractPrerequisites returns the content between the prerequisites
// comment markers in a README body. Returns an empty string when either
// marker is absent.
func ExtractPrerequisites(readme string) string {
startIdx := strings.Index(readme, prerequisitesStartMarker)
if startIdx < 0 {
return ""
}
after := readme[startIdx+len(prerequisitesStartMarker):]
endIdx := strings.Index(after, prerequisitesEndMarker)
if endIdx < 0 {
return ""
}
return strings.TrimSpace(after[:endIdx])
}
// prerequisitesMarkerLine matches either prerequisites marker on its own
// line, including the trailing newline and an optional following blank
// line, so removing it does not leave an extra gap in the rendered README.
var prerequisitesMarkerLine = regexp.MustCompile(
`(?m)^[ \t]*(?:` +
regexp.QuoteMeta(prerequisitesStartMarker) + `|` +
regexp.QuoteMeta(prerequisitesEndMarker) +
`)[ \t]*\n?\n?`)
// Remove the prerequisites comment markers from a README body,
// preserving the content between the markers.
func StripPrerequisitesMarkers(readme string) string {
return prerequisitesMarkerLine.ReplaceAllString(readme, "")
}