From 844726b09be92d186b0662dc19dfdc4dc2abbdde Mon Sep 17 00:00:00 2001 From: yxxhero <11087727+yxxhero@users.noreply.github.com> Date: Fri, 9 May 2025 11:56:54 +0800 Subject: [PATCH] feat(tmpl): enhance ToYaml test with multiple scenarios (#2031) * feat(tmpl): enhance ToYaml test with multiple scenarios Signed-off-by: yxxhero --- docs/index.md | 1 + pkg/envvar/const.go | 19 ++++---- pkg/tmpl/context_funcs_test.go | 86 +++++++++++++++++++++++++++++----- pkg/yaml/yaml.go | 14 +++++- 4 files changed, 98 insertions(+), 22 deletions(-) diff --git a/docs/index.md b/docs/index.md index 211dbf8e..930e6d26 100644 --- a/docs/index.md +++ b/docs/index.md @@ -574,6 +574,7 @@ Helmfile uses some OS environment variables to override default behaviour: * `HELMFILE_CACHE_HOME` - specify directory to store cached files for remote operations * `HELMFILE_FILE_PATH` - specify the path to the helmfile.yaml file * `HELMFILE_INTERACTIVE` - enable interactive mode, expecting `true` lower case. The same as `--interactive` CLI flag +* `HELMFILE_ENABLE_GOCCY_GOYAML_JSON_STYLE`: - enable JSON style for *goccy/go-yaml* instead of *gopkg.in/yaml.v2*. It's `false` by default in Helmfile. it will add quotes to string values. ## CLI Reference diff --git a/pkg/envvar/const.go b/pkg/envvar/const.go index 0235af17..40de9ae9 100644 --- a/pkg/envvar/const.go +++ b/pkg/envvar/const.go @@ -6,13 +6,14 @@ const ( // use helm status to check if a release exists before installing it UseHelmStatusToCheckReleaseExistence = "HELMFILE_USE_HELM_STATUS_TO_CHECK_RELEASE_EXISTENCE" - DisableRunnerUniqueID = "HELMFILE_DISABLE_RUNNER_UNIQUE_ID" - Experimental = "HELMFILE_EXPERIMENTAL" // environment variable for experimental features, expecting "true" lower case - Environment = "HELMFILE_ENVIRONMENT" - FilePath = "HELMFILE_FILE_PATH" - TempDir = "HELMFILE_TEMPDIR" - UpgradeNoticeDisabled = "HELMFILE_UPGRADE_NOTICE_DISABLED" - GoccyGoYaml = "HELMFILE_GOCCY_GOYAML" - CacheHome = "HELMFILE_CACHE_HOME" - Interactive = "HELMFILE_INTERACTIVE" + DisableRunnerUniqueID = "HELMFILE_DISABLE_RUNNER_UNIQUE_ID" + Experimental = "HELMFILE_EXPERIMENTAL" // environment variable for experimental features, expecting "true" lower case + Environment = "HELMFILE_ENVIRONMENT" + FilePath = "HELMFILE_FILE_PATH" + TempDir = "HELMFILE_TEMPDIR" + UpgradeNoticeDisabled = "HELMFILE_UPGRADE_NOTICE_DISABLED" + GoccyGoYaml = "HELMFILE_GOCCY_GOYAML" + CacheHome = "HELMFILE_CACHE_HOME" + Interactive = "HELMFILE_INTERACTIVE" + EnableGoccyGoYamlJSONStyle = "HELMFILE_ENABLE_GOCCY_GOYAML_JSON_STYLE" ) diff --git a/pkg/tmpl/context_funcs_test.go b/pkg/tmpl/context_funcs_test.go index 58ac5bd0..4a75a9c2 100644 --- a/pkg/tmpl/context_funcs_test.go +++ b/pkg/tmpl/context_funcs_test.go @@ -4,12 +4,14 @@ import ( "encoding/json" "fmt" "io/fs" + "os" "path/filepath" goruntime "runtime" "testing" "github.com/stretchr/testify/require" + "github.com/helmfile/helmfile/pkg/envvar" "github.com/helmfile/helmfile/pkg/filesystem" "github.com/helmfile/helmfile/pkg/runtime" ) @@ -186,18 +188,80 @@ func TestToYaml_NestedMapInterfaceKey(t *testing.T) { } func TestToYaml(t *testing.T) { - expected := `foo: - bar: BAR -` - // nolint: unconvert - vals := Values(map[string]any{ - "foo": map[string]any{ - "bar": "BAR", + tests := []struct { + name string + input any + expected string + wantErr bool + enableJsonStyle bool + }{ + { + // https://github.com/helmfile/helmfile/issues/2024 + name: "test unmarshalling issue 2024", + enableJsonStyle: true, + input: map[string]any{"thisShouldBeString": "01234567890123456789"}, + expected: `'thisShouldBeString': '01234567890123456789' +`, }, - }) - actual, err := ToYaml(vals) - require.NoError(t, err) - require.Equal(t, expected, actual) + { + name: "test unmarshalling issue 2024 with int64", + input: map[string]any{"thisShouldBeString": 1234567890123456789}, + expected: `thisShouldBeString: 1234567890123456789 +`, + }, + { + name: "test unmarshalling object", + input: map[string]any{"foo": map[string]any{"bar": "BAR"}}, + expected: `foo: + bar: BAR +`, + }, + { + name: "test unmarshalling array", + input: []any{"foo", map[string]any{"bar": "BAR"}}, + expected: `- foo +- bar: BAR +`, + }, + { + name: "test unmarshalling string", + input: "foo", + expected: "foo\n", + }, + { + name: "test unmarshalling number", + input: 1234, + expected: "1234\n", + }, + { + name: "test unmarshalling boolean", + input: true, + expected: "true\n", + }, + { + name: "test unmarshalling null", + input: nil, + expected: "null\n", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.enableJsonStyle { + _ = os.Setenv(envvar.EnableGoccyGoYamlJSONStyle, "true") + } + defer func() { + _ = os.Unsetenv(envvar.EnableGoccyGoYamlJSONStyle) + }() + actual, err := ToYaml(tt.input) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.expected, actual) + }) + } } func testFromYamlObject(t *testing.T) { diff --git a/pkg/yaml/yaml.go b/pkg/yaml/yaml.go index 06054261..1b30da25 100644 --- a/pkg/yaml/yaml.go +++ b/pkg/yaml/yaml.go @@ -3,10 +3,12 @@ package yaml import ( "bytes" "io" + "os" "github.com/goccy/go-yaml" v2 "gopkg.in/yaml.v2" + "github.com/helmfile/helmfile/pkg/envvar" "github.com/helmfile/helmfile/pkg/runtime" ) @@ -66,11 +68,19 @@ func NewDecoder(data []byte, strict bool) func(any) error { func Marshal(v any) ([]byte, error) { if runtime.GoccyGoYaml { var b bytes.Buffer - yamlEncoder := yaml.NewEncoder( - &b, + yamlEncoderOpts := []yaml.EncodeOption{ yaml.Indent(2), yaml.UseSingleQuote(true), yaml.UseLiteralStyleIfMultiline(true), + } + // enable JSON style if the envvar is set + if os.Getenv(envvar.EnableGoccyGoYamlJSONStyle) == "true" { + yamlEncoderOpts = append(yamlEncoderOpts, yaml.JSON(), yaml.Flow(false)) + } + + yamlEncoder := yaml.NewEncoder( + &b, + yamlEncoderOpts..., ) err := yamlEncoder.Encode(v) defer func() {