appmanager: make app responsible for rendering its own resources
diff --git a/core/installer/app.go b/core/installer/app.go
index 384e54f..76acf74 100644
--- a/core/installer/app.go
+++ b/core/installer/app.go
@@ -2,6 +2,7 @@
 
 import (
 	"archive/tar"
+	"bytes"
 	"compress/gzip"
 	"embed"
 	"fmt"
@@ -35,7 +36,7 @@
 type App struct {
 	Name       string
 	Namespaces []string
-	Templates  []*template.Template
+	templates  []*template.Template
 	schema     Schema
 	Readme     *template.Template
 }
@@ -44,6 +45,18 @@
 	return a.schema
 }
 
+func (a App) Render(derived Derived) (map[string][]byte, error) {
+	ret := make(map[string][]byte)
+	for _, t := range a.templates {
+		var buf bytes.Buffer
+		if err := t.Execute(&buf, derived); err != nil {
+			return nil, err
+		}
+		ret[t.Name()] = buf.Bytes()
+	}
+	return ret, nil
+}
+
 type StoreApp struct {
 	App
 	Icon             htemplate.HTML
@@ -808,7 +821,7 @@
 			Readme:     readmeTmpl,
 			schema:     schema,
 			Namespaces: appCfg.Namespaces,
-			Templates:  tmpls,
+			templates:  tmpls,
 		},
 		ShortDescription: appCfg.Description,
 		Icon:             appCfg.Icon,
diff --git a/core/installer/repoio.go b/core/installer/repoio.go
index 45a2a53..31f4e28 100644
--- a/core/installer/repoio.go
+++ b/core/installer/repoio.go
@@ -278,14 +278,18 @@
 	}
 	{
 		appKust := NewKustomization()
-		for _, t := range app.Templates {
-			appKust.AddResources(t.Name())
-			out, err := r.Writer(path.Join(appRootDir, t.Name()))
+		resources, err := app.Render(derived)
+		if err != nil {
+			return err
+		}
+		for name, contents := range resources {
+			appKust.AddResources(name)
+			out, err := r.Writer(path.Join(appRootDir, name))
 			if err != nil {
 				return err
 			}
 			defer out.Close()
-			if err := t.Execute(out, derived); err != nil {
+			if _, err := out.Write(contents); err != nil {
 				return err
 			}
 		}
diff --git a/core/installer/welcome/appmanager.go b/core/installer/welcome/appmanager.go
index 5d7e537..8622fdc 100644
--- a/core/installer/welcome/appmanager.go
+++ b/core/installer/welcome/appmanager.go
@@ -155,7 +155,6 @@
 
 type rendered struct {
 	Readme string `json:"readme"`
-	Files  []file `json:"files"`
 }
 
 func (s *AppManagerServer) handleAppRender(c echo.Context) error {
@@ -179,9 +178,9 @@
 			}
 		}
 	}
-	all := map[string]any{
-		"Global": global.Values,
-		"Values": values,
+	all := installer.Derived{
+		Global: global.Values,
+		Values: values,
 	}
 	a, err := s.r.Find(slug)
 	if err != nil {
@@ -193,16 +192,6 @@
 	}
 	var resp rendered
 	resp.Readme = readme.String()
-	for _, tmpl := range a.Templates { // TODO(giolekva): deduplicate with Install
-		var f bytes.Buffer
-		if err := tmpl.Execute(&f, all); err != nil {
-			fmt.Printf("%+v\n", all)
-			fmt.Println(err.Error())
-			return err
-		} else {
-			resp.Files = append(resp.Files, file{tmpl.Name(), f.String()})
-		}
-	}
 	out, err := json.Marshal(resp)
 	if err != nil {
 		return err