Skip to content

Commit

Permalink
feat: support template headers (#72)
Browse files Browse the repository at this point in the history
* feat: support go template headers

* feat: use comments instead of frontmatter
  • Loading branch information
adityathebe authored Jul 26, 2024
1 parent a4d7faf commit 54c6641
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 1 deletion.
64 changes: 63 additions & 1 deletion template.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gomplate

import (
"bufio"
"bytes"
"context"
"fmt"
Expand Down Expand Up @@ -203,6 +204,11 @@ func goTemplate(template Template, environment map[string]any) (string, error) {
}

if tpl == nil {
template, err := parseAndStripTemplateHeader(template)
if err != nil {
return "", err
}

tpl = gotemplate.New("")
if template.LeftDelim != "" {
tpl = tpl.Delims(template.LeftDelim, template.RightDelim)
Expand All @@ -215,7 +221,7 @@ func goTemplate(template Template, environment map[string]any) (string, error) {
for k, v := range template.Functions {
funcs[k] = v
}
var err error

tpl, err = tpl.Funcs(funcs).Parse(template.Template)
if err != nil {
return "", err
Expand Down Expand Up @@ -249,3 +255,59 @@ func LoadSharedLibrary(source string) error {
registry.Register(func() string { return string(data) })
return nil
}

func parseAndStripTemplateHeader(template Template) (Template, error) {
header, content := extractHeaderAndContent(template.Template)
if header == "" {
return template, nil
}

template.Template = content

fields := strings.Fields(header)
for _, field := range fields {
split := strings.SplitN(field, "=", 2)
if len(split) != 2 {
return template, fmt.Errorf("invalid header: %s", field)
}

switch split[0] {
case "right-delim":
template.RightDelim = split[1]
case "left-delim":
template.LeftDelim = split[1]
}
}

return template, nil
}

const templateHeaderPrefix = "# gotemplate: "

func extractHeaderAndContent(template string) (string, string) {
scanner := bufio.NewScanner(strings.NewReader(template))

// Loop through headers.
// There could be multiple, we look for the gotemplate header.
for scanner.Scan() {
line := scanner.Text()

if line == "---" {
// Special case for yaml where the header might not start from the first line.
continue
}

// end of headers.
isHeader := strings.HasPrefix(line, "#")
if !isHeader {
break
}

if strings.HasPrefix(line, templateHeaderPrefix) {
header := strings.TrimPrefix(line, templateHeaderPrefix)
return header, strings.Replace(template, fmt.Sprintf("%s\n", line), "", 1)
}
}

return "", template
}
37 changes: 37 additions & 0 deletions tests/gomplate_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package tests

import (
"fmt"
"os"
"testing"
"time"

Expand Down Expand Up @@ -80,3 +82,38 @@ func TestGomplate(t *testing.T) {
})
}
}

func TestGomplateHeaders(t *testing.T) {
tests := []struct {
env map[string]interface{}
template string
out string
expectErr bool
}{
{map[string]interface{}{"name": "world"}, readFile(t, "testdata/gotemplate/template-multiple-headers.yaml"), readFile(t, "testdata/gotemplate/expected/template-multiple-headers.yaml"), false},
{map[string]interface{}{"name": "world"}, readFile(t, "testdata/gotemplate/template-header.txt"), "Hello, world", false},
{map[string]interface{}{"name": "world"}, readFile(t, "testdata/gotemplate/bad-template-header.txt"), "", true},
{map[string]interface{}{"name": "world"}, readFile(t, "testdata/gotemplate/template-header-override.txt"), "Hello, world. This ${should} not be {{touched}}", false},
}

for i, tc := range tests {
t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
out, err := gomplate.RunTemplate(tc.env, gomplate.Template{
Template: tc.template,
})
if tc.expectErr {
assert.Error(t, err)
} else {
assert.ErrorIs(t, err, nil)
assert.Equal(t, tc.out, out)
}
})
}
}

func readFile(t *testing.T, filename string) string {
t.Helper()
data, err := os.ReadFile(filename)
assert.ErrorIs(t, err, nil)
return string(data)
}
2 changes: 2 additions & 0 deletions tests/testdata/gotemplate/bad-template-header.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# gotemplate: left-delim=$[ [ right-delim=]]
Hello, $[[.name]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrepository-source-v1beta2.json
name: Hello world
2 changes: 2 additions & 0 deletions tests/testdata/gotemplate/template-header-override.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# gotemplate: left-delim=$[[ right-delim=]]
Hello, $[[.name]]. This ${should} not be {{touched}}
2 changes: 2 additions & 0 deletions tests/testdata/gotemplate/template-header.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# gotemplate: left-delim=$[[ right-delim=]]
Hello, $[[.name]]
4 changes: 4 additions & 0 deletions tests/testdata/gotemplate/template-multiple-headers.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrepository-source-v1beta2.json
# gotemplate: left-delim=<<< right-delim=>>>
name: Hello <<<.name>>>

0 comments on commit 54c6641

Please sign in to comment.