Repository: lima-vm/lima
Pull request #3008 (2024-12-11 05:46:54)

Use yq string evaluator instead of stream evaluator
This allows us to pass the YAML content in memory instead of having to write it to a file.

This commit also switches to "eval-all" mode, so multiple documents are all loaded together, allowing merge operations between them.

This is in preparation of multi-file template assembly.


Patch:
diff --git a/pkg/yqutil/yqutil.go b/pkg/yqutil/yqutil.go
index 2bae00b4..cd0f0ddd 100644
--- a/pkg/yqutil/yqutil.go
+++ b/pkg/yqutil/yqutil.go
@@ -6,7 +6,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"os"
 	"strings"
 
 	"github.com/google/yamlfmt"
@@ -37,7 +36,7 @@ func ValidateContent(content []byte) error {
 	return err
 }
 
-// EvaluateExpression evaluates the yq expression, and returns the modified yaml.
+// EvaluateExpression evaluates the yq expression and returns the modified yaml.
 func EvaluateExpression(expression string, content []byte) ([]byte, error) {
 	if expression == "" {
 		return content, nil
@@ -56,21 +55,6 @@ func EvaluateExpression(expression string, content []byte) ([]byte, error) {
 	if err != nil {
 		return nil, err
 	}
-	tmpYAMLFile, err := os.CreateTemp("", "lima-yq-*.yaml")
-	if err != nil {
-		return nil, err
-	}
-	tmpYAMLPath := tmpYAMLFile.Name()
-	defer os.RemoveAll(tmpYAMLPath)
-	_, err = tmpYAMLFile.Write(contentModified)
-	if err != nil {
-		tmpYAMLFile.Close()
-		return nil, err
-	}
-	if err = tmpYAMLFile.Close(); err != nil {
-		return nil, err
-	}
-
 	memory := logging.NewMemoryBackend(0)
 	backend := logging.AddModuleLevel(memory)
 	logging.SetBackend(backend)
@@ -80,13 +64,8 @@ func EvaluateExpression(expression string, content []byte) ([]byte, error) {
 	encoderPrefs.Indent = 2
 	encoderPrefs.ColorsEnabled = false
 	encoder := yqlib.NewYamlEncoder(encoderPrefs)
-	out := new(bytes.Buffer)
-	printer := yqlib.NewPrinter(encoder, yqlib.NewSinglePrinterWriter(out))
 	decoder := yqlib.NewYamlDecoder(yqlib.ConfiguredYamlPreferences)
-
-	streamEvaluator := yqlib.NewStreamEvaluator()
-	files := []string{tmpYAMLPath}
-	err = streamEvaluator.EvaluateFiles(expression, files, printer, decoder)
+	out, err := yqlib.NewStringEvaluator().EvaluateAll(expression, string(contentModified), encoder, decoder)
 	if err != nil {
 		logger := logrus.StandardLogger()
 		for node := memory.Head(); node != nil; node = node.Next() {
@@ -110,8 +89,7 @@ func EvaluateExpression(expression string, content []byte) ([]byte, error) {
 		}
 		return nil, err
 	}
-
-	return formatter.Format(out.Bytes())
+	return formatter.Format([]byte(out))
 }
 
 func Join(yqExprs []string) string {


Test patch:
diff --git a/pkg/yqutil/yqutil_test.go b/pkg/yqutil/yqutil_test.go
index 89df0f24..fb9b17a8 100644
--- a/pkg/yqutil/yqutil_test.go
+++ b/pkg/yqutil/yqutil_test.go
@@ -1,6 +1,7 @@
 package yqutil
 
 import (
+	"strings"
 	"testing"
 
 	"gotest.tools/v3/assert"
@@ -95,3 +96,27 @@ func TestEvaluateExpressionError(t *testing.T) {
 	_, err := EvaluateExpression(expression, []byte(""))
 	assert.ErrorContains(t, err, "invalid input text")
 }
+
+func TestEvaluateMergeExpression(t *testing.T) {
+	expression := `select(di == 0) * select(di == 1)`
+	content := `
+yolo: true
+foo:
+  bar: 1
+  baz: 2
+---
+foo:
+  bar: 3
+  fomo: false
+`
+	expected := `
+yolo: true
+foo:
+  bar: 3
+  baz: 2
+  fomo: false
+`
+	out, err := EvaluateExpression(expression, []byte(strings.TrimSpace(content)))
+	assert.NilError(t, err)
+	assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(out)))
+}