From 88329f8103fbab49fe033a1398d8467274791e18 Mon Sep 17 00:00:00 2001 From: Aditya Thebe Date: Wed, 24 Jun 2026 09:32:48 +0545 Subject: [PATCH] perf(context): build template funcs by mode RunTemplate built both CEL env functions and Go-template functions before evaluating any template, even when only one template mode was in use. That work also made otherwise cacheable gomplate evaluations look like they had custom functions. Only attach CEL env options for CEL expressions and only attach Go-template functions for Go templates. Add regression coverage for the mode-specific construction behavior. --- context/template.go | 18 +++++++++++------- context/template_test.go | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/context/template.go b/context/template.go index 313ef73dc..9c6f74f03 100644 --- a/context/template.go +++ b/context/template.go @@ -23,14 +23,18 @@ func (k Context) RunTemplate(t gomplate.Template, env map[string]any) (string, e } else { l.V(1).Infof("Running template: %s", t.String()) } - for _, f := range CelEnvFuncs { - t.CelEnvs = append(t.CelEnvs, f(k)) - } - if t.Functions == nil { - t.Functions = make(map[string]any) + if t.Expression != "" { + for _, f := range CelEnvFuncs { + t.CelEnvs = append(t.CelEnvs, f(k)) + } } - for name, v := range TemplateFuncs { - t.Functions[name] = v(k) + if t.Template != "" { + if t.Functions == nil { + t.Functions = make(map[string]any) + } + for name, v := range TemplateFuncs { + t.Functions[name] = v(k) + } } if t.Template != "" { diff --git a/context/template_test.go b/context/template_test.go index 3b55949f9..5d56df51f 100644 --- a/context/template_test.go +++ b/context/template_test.go @@ -4,9 +4,50 @@ import ( "testing" "github.com/flanksource/gomplate/v3" + "github.com/google/cel-go/cel" . "github.com/onsi/gomega" ) +func TestRunTemplateOnlyBuildsMatchingFunctionContexts(t *testing.T) { + g := NewWithT(t) + oldCelEnvFuncs := CelEnvFuncs + oldTemplateFuncs := TemplateFuncs + defer func() { + CelEnvFuncs = oldCelEnvFuncs + TemplateFuncs = oldTemplateFuncs + }() + + var celBuilds int + var templateBuilds int + CelEnvFuncs = map[string]func(Context) cel.EnvOption{ + "unused_cel_context": func(ctx Context) cel.EnvOption { + celBuilds++ + return cel.Variable("unused_cel_context", cel.AnyType) + }, + } + TemplateFuncs = map[string]func(Context) any{ + "unused_template_context": func(ctx Context) any { + templateBuilds++ + return func() string { return "unused" } + }, + } + + ctx := New() + env := map[string]any{"name": "World"} + + got, err := ctx.RunTemplate(gomplate.Template{Template: "Hello {{.name}}!"}, env) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(got).To(Equal("Hello World!")) + g.Expect(celBuilds).To(Equal(0)) + g.Expect(templateBuilds).To(Equal(1)) + + ok, err := ctx.RunTemplateBool(gomplate.Template{Expression: `name == "World"`}, env) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ok).To(BeTrue()) + g.Expect(celBuilds).To(Equal(1)) + g.Expect(templateBuilds).To(Equal(1)) +} + func TestRunTemplate(t *testing.T) { t.Parallel()