pull/32040/merge
Barry 12 hours ago committed by GitHub
commit 857266535f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -20,6 +20,7 @@ import (
"bytes"
"encoding/json"
"maps"
"math"
"strings"
"text/template"
@ -157,7 +158,7 @@ func fromYAMLArray(str string) []any {
func toTOML(v any) string {
b := bytes.NewBuffer(nil)
e := toml.NewEncoder(b)
err := e.Encode(v)
err := e.Encode(normalizeForTOML(v))
if err != nil {
return err.Error()
}
@ -172,13 +173,56 @@ func toTOML(v any) string {
func mustToTOML(v any) string {
b := bytes.NewBuffer(nil)
e := toml.NewEncoder(b)
err := e.Encode(v)
err := e.Encode(normalizeForTOML(v))
if err != nil {
panic(err)
}
return b.String()
}
// normalizeForTOML walks v and rewrites any float64 that is a whole number
// (within the int64 range) as an int64. Helm values round-trip through JSON,
// so every YAML number arrives in the template engine as a float64 regardless
// of whether the source was written as "9" or "9.0"; the BurntSushi TOML
// encoder then writes float64(9) as "9.0", which surprises users. This brings
// toToml in line with encoding/json (which already drops trailing ".0" for
// whole-number float64). Non-whole floats and values outside the int64 range
// are left untouched so that true floats still round-trip as floats.
//
// This fix is intentionally scoped to the TOML encoding path. #13533 solved
// the same issue by switching the values loader to json.Decoder.UseNumber,
// which changed every numeric value to json.Number globally and broke charts
// relying on typeOf/typeIs returning "float64" (#30880). Normalizing only
// inside toTOML preserves the in-template type of values so that regression
// cannot recur.
func normalizeForTOML(v any) any {
switch x := v.(type) {
case map[string]any:
out := make(map[string]any, len(x))
for k, val := range x {
out[k] = normalizeForTOML(val)
}
return out
case []any:
out := make([]any, len(x))
for i, val := range x {
out[i] = normalizeForTOML(val)
}
return out
case float64:
// Guard against NaN/Inf and against Go's implementation-defined
// behavior for out-of-range float-to-int conversions. 1<<63 is the
// first positive float64 strictly greater than math.MaxInt64.
if math.IsNaN(x) || math.IsInf(x, 0) || x != math.Trunc(x) ||
x >= 1<<63 || x < -(1<<63) {
return x
}
return int64(x)
default:
return v
}
}
// fromTOML converts a TOML document into a map[string]interface{}.
//
// This is not a general-purpose TOML parser, and will not parse all valid

@ -86,6 +86,32 @@ keyInElement1 = "valueInElement1"`,
tpl: `{{ toToml . }}`,
expect: "[mast]\n sail = \"white\"\n",
vars: map[string]map[string]string{"mast": {"sail": "white"}},
}, {
// Regression for https://github.com/helm/helm/issues/32035
// Helm values round-trip through JSON so YAML integers arrive as
// float64. toToml must still emit them as TOML integers, matching
// what toJson already does. Fractional floats must be preserved.
tpl: `{{ toToml . }}`,
expect: "bar = 9\nbaz = 9.5\n\n[[items]]\n id = 1\n\n" +
"[[items]]\n id = 2\n\n[nested]\n deep = 42\n",
vars: map[string]any{
"bar": float64(9),
"baz": float64(9.5),
"nested": map[string]any{
"deep": float64(42),
},
"items": []any{
map[string]any{"id": float64(1)},
map[string]any{"id": float64(2)},
},
},
}, {
// Regression for https://github.com/helm/helm/issues/32035
// Non-finite floats and floats outside the int64 range must stay
// floats so that true floats still round-trip as floats.
tpl: `{{ toToml . }}`,
expect: "big = 1e+20\n",
vars: map[string]any{"big": float64(1e20)},
}, {
tpl: `{{ fromYaml . }}`,
expect: "map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]",

Loading…
Cancel
Save