@@ -6,11 +6,14 @@ package limatmpl
6
6
import (
7
7
"bytes"
8
8
"context"
9
+ "encoding/base64"
9
10
"fmt"
10
11
"os"
11
12
"path/filepath"
12
13
"slices"
14
+ "strings"
13
15
"sync"
16
+ "unicode"
14
17
15
18
"github.com/coreos/go-semver/semver"
16
19
"github.com/lima-vm/lima/pkg/limayaml"
@@ -254,7 +257,7 @@ const mergeDocuments = `
254
257
| $a | (select(.mountTypesUnsupported) | .mountTypesUnsupported) |= unique
255
258
256
259
# Remove the custom tags again so they do not clutter up the YAML output.
257
- | $a | .. tag = ""
260
+ | $a | .. | select(tag == "!!tag") tag = ""
258
261
`
259
262
260
263
// listFields returns dst and src fields like "list[idx].field".
@@ -552,11 +555,72 @@ func (tmpl *Template) combineNetworks() {
552
555
}
553
556
}
554
557
558
+ // yamlfmt will fail with a buffer overflow while trying to retain line breaks if the line
559
+ // is longer than 64K. We will encode all text files that have a line that comes close.
560
+ // maxLineLength is a constant; it is only a variable for the benefit of the unit tests.
561
+ var maxLineLength = 65000
562
+
563
+ // encodeScriptReason returns the reason why a script needs to be base64 encoded or the empty string if it doesn't.
564
+ func encodeScriptReason (script string ) string {
565
+ start := 0
566
+ line := 1
567
+ for i , r := range script {
568
+ if ! (unicode .IsPrint (r ) || r == '\n' || r == '\r' || r == '\t' ) {
569
+ return fmt .Sprintf ("unprintable character %q at offset %d" , r , i )
570
+ }
571
+ // maxLineLength includes final newline
572
+ if i - start >= maxLineLength {
573
+ return fmt .Sprintf ("line %d (offset %d) is longer than %d characters" , line , start , maxLineLength )
574
+ }
575
+ if r == '\n' {
576
+ line ++
577
+ start = i + 1
578
+ }
579
+ }
580
+ return ""
581
+ }
582
+
583
+ // Break base64 strings into shorter chunks. Technically we could use maxLineLength here,
584
+ // but shorter lines look better.
585
+ const base64ChunkLength = 76
586
+
587
+ // binaryString returns a base64 encoded version of the binary string, broken into chunks
588
+ // of at most base64ChunkLength characters per line.
589
+ func binaryString (s string ) string {
590
+ encoded := base64 .StdEncoding .EncodeToString ([]byte (s ))
591
+ if len (encoded ) <= base64ChunkLength {
592
+ return encoded
593
+ }
594
+
595
+ // Estimate capacity: encoded length + number of newlines
596
+ lineCount := (len (encoded ) + base64ChunkLength - 1 ) / base64ChunkLength
597
+ builder := strings.Builder {}
598
+ builder .Grow (len (encoded ) + lineCount )
599
+
600
+ for i := 0 ; i < len (encoded ); i += base64ChunkLength {
601
+ end := i + base64ChunkLength
602
+ if end > len (encoded ) {
603
+ end = len (encoded )
604
+ }
605
+ builder .WriteString (encoded [i :end ])
606
+ builder .WriteByte ('\n' )
607
+ }
608
+
609
+ return builder .String ()
610
+ }
611
+
555
612
// updateScript replaces a "file" property with the actual script and then renames the field to newName ("script" or "content").
556
- func (tmpl * Template ) updateScript (field string , idx int , newName , script string ) {
613
+ func (tmpl * Template ) updateScript (field string , idx int , newName , script , file string ) {
614
+ tag := ""
615
+ if reason := encodeScriptReason (script ); reason != "" {
616
+ logrus .Infof ("File %q is being base64 encoded: %s" , file , reason )
617
+ script = binaryString (script )
618
+ tag = "!!binary"
619
+ }
557
620
entry := fmt .Sprintf ("$a.%s[%d].file" , field , idx )
558
- // Assign script to the "file" field and then rename it to "script".
559
- tmpl .expr .WriteString (fmt .Sprintf ("| (%s) = %q | (%s | key) = %q\n " , entry , script , entry , newName ))
621
+ // Assign script to the "file" field and then rename it to "script" or "content".
622
+ tmpl .expr .WriteString (fmt .Sprintf ("| (%s) = %q | (%s) tag = %q | (%s | key) = %q\n " ,
623
+ entry , script , entry , tag , entry , newName ))
560
624
}
561
625
562
626
// embedAllScripts replaces all "provision" and "probes" file references with the actual script.
@@ -579,7 +643,7 @@ func (tmpl *Template) embedAllScripts(ctx context.Context, embedAll bool) error
579
643
if err != nil {
580
644
return err
581
645
}
582
- tmpl .updateScript ("probes" , i , "script" , string (scriptTmpl .Bytes ))
646
+ tmpl .updateScript ("probes" , i , "script" , string (scriptTmpl .Bytes ), p . File . URL )
583
647
}
584
648
}
585
649
for i , p := range tmpl .Config .Provision {
@@ -605,7 +669,7 @@ func (tmpl *Template) embedAllScripts(ctx context.Context, embedAll bool) error
605
669
if err != nil {
606
670
return err
607
671
}
608
- tmpl .updateScript ("provision" , i , newName , string (scriptTmpl .Bytes ))
672
+ tmpl .updateScript ("provision" , i , newName , string (scriptTmpl .Bytes ), p . File . URL )
609
673
}
610
674
}
611
675
return tmpl .evalExpr ()
0 commit comments