Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: variables in template file names #105

Merged
merged 2 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/variable-file-names/recipe.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
name: variable-filename
version: v0.0.0
description: Minimal recipe which just creates a file named from a variable
vars:
- name: README_FILE_NAME
description: |
The base name for the rendered file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The name of this file is {{ .Variables.README_FILE_NAME }}.md
20 changes: 19 additions & 1 deletion pkg/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,25 @@ func (e Engine) Render(templates map[string][]byte, values map[string]interface{

// TODO: Could we detect unused variables, and give warning about those?

rendered[name] = []byte(output)
// File names can be templates to, render them here as well

buf.Reset()

template, err := t.New("__template_file_filename").Parse(name)
if err != nil {
return nil, fmt.Errorf("failed to parse file name template: %w", err)
}

if err := template.Execute(&buf, values); err != nil {
return nil, fmt.Errorf("failed to execute file name template: %w", err)
}

filename := buf.String()
if strings.Contains(filename, "<no value>") {
return nil, fmt.Errorf("variable for file name %q was undefined", name)
}

rendered[filename] = []byte(output)
}

return rendered, nil
Expand Down
4 changes: 3 additions & 1 deletion pkg/engine/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ func TestRender(t *testing.T) {
{
"values_and_functions",
map[string][]byte{
"templates/test1": []byte("{{.var1 | title }} {{.var2 | title}}"),
"templates/test1": []byte("{{.var1 | title }} {{.var2 | title}}"),
"templates/{{.var1}}": []byte("{{.var1}}"),
},
map[string]interface{}{
"var1": "first",
"var2": "second",
},
map[string][]byte{
"templates/test1": []byte("First Second"),
"templates/first": []byte("first"),
},
},
{
Expand Down
26 changes: 25 additions & 1 deletion pkg/recipe/saver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"gopkg.in/yaml.v3"
)
Expand All @@ -13,6 +14,24 @@ const (
yamlIndent int = 2
)

func ValidatePath(basePath, filePath string) error {
absBasePath, err := filepath.Abs(basePath)
if err != nil {
return fmt.Errorf("invalid base path %q: %v", basePath, err)
}

absFilePath, err := filepath.Abs(filePath)
if err != nil {
return fmt.Errorf("invalid file path %q: %v", basePath, err)
}

if !strings.HasPrefix(absFilePath, absBasePath) {
return fmt.Errorf("file path escapes destination: %q outside %q", absFilePath, absBasePath)
}

return nil
}

// Save saves recipe to given destination
func (re *Recipe) Save(dest string) error {
err := os.MkdirAll(dest, defaultFileMode)
Expand Down Expand Up @@ -197,8 +216,13 @@ func saveFileMap(files map[string]File, dest string) error {
for path, file := range files {
destPath := filepath.Join(dest, path)

err := ValidatePath(dest, destPath)
if err != nil {
return err
}

// Create file's parent directories (if not already exist)
err := os.MkdirAll(filepath.Dir(destPath), 0700)
err = os.MkdirAll(filepath.Dir(destPath), 0700)
if err != nil {
return err
}
Expand Down
35 changes: 35 additions & 0 deletions pkg/recipe/saver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package recipe
import (
"os"
"path/filepath"
"strings"
"testing"

"github.com/futurice/jalapeno/pkg/engine"
Expand Down Expand Up @@ -117,3 +118,37 @@ func TestSaveSauce(t *testing.T) {
}
}
}

func TestSaveSauceDoesNotWriteOutsideDest(t *testing.T) {
dir, err := os.MkdirTemp("", "jalapeno-test-saver")
if err != nil {
t.Fatalf("cannot create temp dir: %s", err)
}
defer os.RemoveAll(dir)

re := NewRecipe()
re.Name = "Test"
re.Version = "v0.0.1"
re.Templates = map[string]File{
"../foo.md": NewFile([]byte("foo")),
}

err = re.Validate()
if err != nil {
t.Fatalf("test recipe was not valid: %s", err)
}

sauce, err := re.Execute(engine.New(), nil, uuid.Must(uuid.NewV4()))
if err != nil {
t.Fatalf("recipe execution failed: %s", err)
}

err = sauce.Save(dir)
if err == nil {
t.Fatalf("should not have saved sauce")
}

if !strings.Contains(err.Error(), "file path escapes destination") {
t.Fatalf("error received was not expected: %s", err)
}
}
Loading