Skip to content

Commit 8f3c2d5

Browse files
authored
Merge pull request helm#2511 from technosophos/fix/2452-predictable-template-order
fix(2452): sort templates before parse
2 parents b031a41 + 8937c77 commit 8f3c2d5

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

pkg/engine/engine.go

+32-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"bytes"
2121
"fmt"
2222
"path"
23+
"sort"
2324
"strings"
2425
"text/template"
2526

@@ -181,8 +182,14 @@ func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) {
181182

182183
funcMap := e.alterFuncMap(t)
183184

185+
// We want to parse the templates in a predictable order. The order favors
186+
// higher-level (in file system) templates over deeply nested templates.
187+
keys := sortTemplates(tpls)
188+
184189
files := []string{}
185-
for fname, r := range tpls {
190+
//for fname, r := range tpls {
191+
for _, fname := range keys {
192+
r := tpls[fname]
186193
t = t.New(fname).Funcs(funcMap)
187194
if _, err := t.Parse(r.tpl); err != nil {
188195
return map[string]string{}, fmt.Errorf("parse error in %q: %s", fname, err)
@@ -215,6 +222,30 @@ func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) {
215222
return rendered, nil
216223
}
217224

225+
func sortTemplates(tpls map[string]renderable) []string {
226+
keys := make([]string, len(tpls))
227+
i := 0
228+
for key := range tpls {
229+
keys[i] = key
230+
i++
231+
}
232+
sort.Sort(sort.Reverse(byPathLen(keys)))
233+
return keys
234+
}
235+
236+
type byPathLen []string
237+
238+
func (p byPathLen) Len() int { return len(p) }
239+
func (p byPathLen) Swap(i, j int) { p[j], p[i] = p[i], p[j] }
240+
func (p byPathLen) Less(i, j int) bool {
241+
a, b := p[i], p[j]
242+
ca, cb := strings.Count(a, "/"), strings.Count(b, "/")
243+
if ca == cb {
244+
return strings.Compare(a, b) == -1
245+
}
246+
return ca < cb
247+
}
248+
218249
// allTemplates returns all templates for a chart and its dependencies.
219250
//
220251
// As it goes, it also prepares the values in a scope-sensitive manner.

pkg/engine/engine_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,37 @@ import (
2727
"github.com/golang/protobuf/ptypes/any"
2828
)
2929

30+
func TestSortTemplates(t *testing.T) {
31+
tpls := map[string]renderable{
32+
"/mychart/templates/foo.tpl": {},
33+
"/mychart/templates/charts/foo/charts/bar/templates/foo.tpl": {},
34+
"/mychart/templates/bar.tpl": {},
35+
"/mychart/templates/charts/foo/templates/bar.tpl": {},
36+
"/mychart/templates/_foo.tpl": {},
37+
"/mychart/templates/charts/foo/templates/foo.tpl": {},
38+
"/mychart/templates/charts/bar/templates/foo.tpl": {},
39+
}
40+
got := sortTemplates(tpls)
41+
if len(got) != len(tpls) {
42+
t.Fatal("Sorted results are missing templates")
43+
}
44+
45+
expect := []string{
46+
"/mychart/templates/charts/foo/charts/bar/templates/foo.tpl",
47+
"/mychart/templates/charts/foo/templates/foo.tpl",
48+
"/mychart/templates/charts/foo/templates/bar.tpl",
49+
"/mychart/templates/charts/bar/templates/foo.tpl",
50+
"/mychart/templates/foo.tpl",
51+
"/mychart/templates/bar.tpl",
52+
"/mychart/templates/_foo.tpl",
53+
}
54+
for i, e := range expect {
55+
if got[i] != e {
56+
t.Errorf("expected %q, got %q at index %d\n\tExp: %#v\n\tGot: %#v", e, got[i], i, expect, got)
57+
}
58+
}
59+
}
60+
3061
func TestEngine(t *testing.T) {
3162
e := New()
3263

0 commit comments

Comments
 (0)