refactor(yaml): upgrade from gopkg.in/yaml.v2 to v3 (#2039)
* refactor(yaml): upgrade from gopkg.in/yaml.v2 to v3 Signed-off-by: yxxhero <aiopsclub@163.com> * refactor(yaml): enhance yaml encoding with consistent formatting and quotes Signed-off-by: yxxhero <aiopsclub@163.com> * optimize code Signed-off-by: yxxhero <aiopsclub@163.com> * fix tests Signed-off-by: yxxhero <aiopsclub@163.com> * fix more issues Signed-off-by: yxxhero <aiopsclub@163.com> * fix tests Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
		
							parent
							
								
									867bef0f03
								
							
						
					
					
						commit
						b52ca9ae04
					
				|  | @ -570,18 +570,17 @@ Helmfile uses some OS environment variables to override default behaviour: | ||||||
| * `HELMFILE_ENVIRONMENT` - specify [Helmfile environment](https://helmfile.readthedocs.io/en/latest/#environment), it has lower priority than CLI argument `--environment` | * `HELMFILE_ENVIRONMENT` - specify [Helmfile environment](https://helmfile.readthedocs.io/en/latest/#environment), it has lower priority than CLI argument `--environment` | ||||||
| * `HELMFILE_TEMPDIR` - specify directory to store temporary files | * `HELMFILE_TEMPDIR` - specify directory to store temporary files | ||||||
| * `HELMFILE_UPGRADE_NOTICE_DISABLED` - expecting any non-empty value to skip the check for the latest version of Helmfile in [helmfile version](https://helmfile.readthedocs.io/en/latest/#version) | * `HELMFILE_UPGRADE_NOTICE_DISABLED` - expecting any non-empty value to skip the check for the latest version of Helmfile in [helmfile version](https://helmfile.readthedocs.io/en/latest/#version) | ||||||
| * `HELMFILE_GOCCY_GOYAML` - use *goccy/go-yaml* instead of *gopkg.in/yaml.v2*.  It's `false` by default in Helmfile v0.x and `true` by default for Helmfile v1.x. | * `HELMFILE_GO_YAML_V3` - use *gopkg.in/yaml.v3* instead of *gopkg.in/yaml.v2*.  It's `false` by default in Helmfile v0.x, and `true` in Helmfile v1.x. | ||||||
| * `HELMFILE_CACHE_HOME` - specify directory to store cached files for remote operations | * `HELMFILE_CACHE_HOME` - specify directory to store cached files for remote operations | ||||||
| * `HELMFILE_FILE_PATH` - specify the path to the helmfile.yaml file | * `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_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 | ## CLI Reference | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| Declaratively deploy your Kubernetes manifests, Kustomize configs, and Charts as Helm releases in one shot | Declaratively deploy your Kubernetes manifests, Kustomize configs, and Charts as Helm releases in one shot | ||||||
| V1 mode = false | V1 mode = false | ||||||
| YAML library = gopkg.in/yaml.v2 | YAML library = gopkg.in/yaml.v3 | ||||||
| 
 | 
 | ||||||
| Usage: | Usage: | ||||||
|   helmfile [command] |   helmfile [command] | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										4
									
								
								go.mod
								
								
								
								
							|  | @ -8,7 +8,6 @@ require ( | ||||||
| 	github.com/Masterminds/sprig/v3 v3.3.0 | 	github.com/Masterminds/sprig/v3 v3.3.0 | ||||||
| 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc | 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc | ||||||
| 	github.com/go-test/deep v1.1.1 | 	github.com/go-test/deep v1.1.1 | ||||||
| 	github.com/goccy/go-yaml v1.17.1 |  | ||||||
| 	github.com/golang/mock v1.6.0 | 	github.com/golang/mock v1.6.0 | ||||||
| 	github.com/google/go-cmp v0.7.0 | 	github.com/google/go-cmp v0.7.0 | ||||||
| 	github.com/gosuri/uitable v0.0.4 | 	github.com/gosuri/uitable v0.0.4 | ||||||
|  | @ -29,6 +28,7 @@ require ( | ||||||
| 	golang.org/x/sync v0.14.0 | 	golang.org/x/sync v0.14.0 | ||||||
| 	golang.org/x/term v0.32.0 | 	golang.org/x/term v0.32.0 | ||||||
| 	gopkg.in/yaml.v2 v2.4.0 | 	gopkg.in/yaml.v2 v2.4.0 | ||||||
|  | 	gopkg.in/yaml.v3 v3.0.1 | ||||||
| 	helm.sh/helm/v3 v3.17.3 | 	helm.sh/helm/v3 v3.17.3 | ||||||
| 	k8s.io/apimachinery v0.33.0 | 	k8s.io/apimachinery v0.33.0 | ||||||
| ) | ) | ||||||
|  | @ -217,6 +217,7 @@ require ( | ||||||
| 	github.com/go-openapi/swag v0.23.1 // indirect | 	github.com/go-openapi/swag v0.23.1 // indirect | ||||||
| 	github.com/go-openapi/validate v0.24.0 // indirect | 	github.com/go-openapi/validate v0.24.0 // indirect | ||||||
| 	github.com/gobwas/glob v0.2.3 // indirect | 	github.com/gobwas/glob v0.2.3 // indirect | ||||||
|  | 	github.com/goccy/go-yaml v1.17.1 // indirect | ||||||
| 	github.com/godbus/dbus/v5 v5.1.0 // indirect | 	github.com/godbus/dbus/v5 v5.1.0 // indirect | ||||||
| 	github.com/gogo/protobuf v1.3.2 // indirect | 	github.com/gogo/protobuf v1.3.2 // indirect | ||||||
| 	github.com/golang-jwt/jwt/v5 v5.2.2 // indirect | 	github.com/golang-jwt/jwt/v5 v5.2.2 // indirect | ||||||
|  | @ -307,7 +308,6 @@ require ( | ||||||
| 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect | 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect | ||||||
| 	gopkg.in/gookit/color.v1 v1.1.6 // indirect | 	gopkg.in/gookit/color.v1 v1.1.6 // indirect | ||||||
| 	gopkg.in/inf.v0 v0.9.1 // indirect | 	gopkg.in/inf.v0 v0.9.1 // indirect | ||||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect |  | ||||||
| 	k8s.io/api v0.33.0 // indirect | 	k8s.io/api v0.33.0 // indirect | ||||||
| 	k8s.io/apiextensions-apiserver v0.32.2 // indirect | 	k8s.io/apiextensions-apiserver v0.32.2 // indirect | ||||||
| 	k8s.io/cli-runtime v0.32.2 // indirect | 	k8s.io/cli-runtime v0.32.2 // indirect | ||||||
|  |  | ||||||
|  | @ -338,18 +338,18 @@ releases: | ||||||
| 
 | 
 | ||||||
| func TestTemplate_StrictParsing(t *testing.T) { | func TestTemplate_StrictParsing(t *testing.T) { | ||||||
| 	type testcase struct { | 	type testcase struct { | ||||||
| 		goccyGoYaml bool | 		GoYamlV3 bool | ||||||
| 		ns          string | 		ns       string | ||||||
| 		error       string | 		error    string | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	check := func(t *testing.T, tc testcase) { | 	check := func(t *testing.T, tc testcase) { | ||||||
| 		t.Helper() | 		t.Helper() | ||||||
| 
 | 
 | ||||||
| 		v := runtime.GoccyGoYaml | 		v := runtime.GoYamlV3 | ||||||
| 		runtime.GoccyGoYaml = tc.goccyGoYaml | 		runtime.GoYamlV3 = tc.GoYamlV3 | ||||||
| 		t.Cleanup(func() { | 		t.Cleanup(func() { | ||||||
| 			runtime.GoccyGoYaml = v | 			runtime.GoYamlV3 = v | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		var helm = &exectest.Helm{ | 		var helm = &exectest.Helm{ | ||||||
|  | @ -411,21 +411,17 @@ releases: | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	t.Run("fail due to unknown field with goccy/go-yaml", func(t *testing.T) { | 	t.Run("fail due to unknown field with gopkg.in/yaml.v3", func(t *testing.T) { | ||||||
| 		check(t, testcase{ | 		check(t, testcase{ | ||||||
| 			goccyGoYaml: true, | 			GoYamlV3: true, | ||||||
| 			error: `in ./helmfile.yaml: failed to read helmfile.yaml: reading document at index 1. Started seeing this since Helmfile v1? Add the .gotmpl file extension: [4:3] unknown field "foobar" | 			error: `in ./helmfile.yaml: failed to read helmfile.yaml: reading document at index 1. Started seeing this since Helmfile v1? Add the .gotmpl file extension: yaml: unmarshal errors: | ||||||
|    2 | releases: |   line 4: field foobar not found in type state.ReleaseSpec`, | ||||||
|    3 | - name: app1 |  | ||||||
| >  4 |   foobar: FOOBAR |  | ||||||
|          ^ |  | ||||||
|    5 |   chart: incubator/raw`, |  | ||||||
| 		}) | 		}) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("fail due to unknown field with gopkg.in/yaml.v2", func(t *testing.T) { | 	t.Run("fail due to unknown field with gopkg.in/yaml.v2", func(t *testing.T) { | ||||||
| 		check(t, testcase{ | 		check(t, testcase{ | ||||||
| 			goccyGoYaml: false, | 			GoYamlV3: false, | ||||||
| 			error: `in ./helmfile.yaml: failed to read helmfile.yaml: reading document at index 1. Started seeing this since Helmfile v1? Add the .gotmpl file extension: yaml: unmarshal errors: | 			error: `in ./helmfile.yaml: failed to read helmfile.yaml: reading document at index 1. Started seeing this since Helmfile v1? Add the .gotmpl file extension: yaml: unmarshal errors: | ||||||
|   line 4: field foobar not found in type state.ReleaseSpec`, |   line 4: field foobar not found in type state.ReleaseSpec`, | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
|  | @ -4020,13 +4020,13 @@ myrelease4	testNamespace	true   	true     	chart:mychart1,id:myrelease1,name:myr | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(t, expected, out) | 	assert.Equal(t, expected, out) | ||||||
| } | } | ||||||
| func testSetStringValuesTemplate(t *testing.T, goccyGoYaml bool) { | func testSetStringValuesTemplate(t *testing.T, GoYamlV3 bool) { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 
 | 
 | ||||||
| 	v := runtime.GoccyGoYaml | 	v := runtime.GoYamlV3 | ||||||
| 	runtime.GoccyGoYaml = goccyGoYaml | 	runtime.GoYamlV3 = GoYamlV3 | ||||||
| 	t.Cleanup(func() { | 	t.Cleanup(func() { | ||||||
| 		runtime.GoccyGoYaml = v | 		runtime.GoYamlV3 = v | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	files := map[string]string{ | 	files := map[string]string{ | ||||||
|  | @ -4088,13 +4088,13 @@ releases: | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func testSetValuesTemplate(t *testing.T, goccyGoYaml bool) { | func testSetValuesTemplate(t *testing.T, GoYamlV3 bool) { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 
 | 
 | ||||||
| 	v := runtime.GoccyGoYaml | 	v := runtime.GoYamlV3 | ||||||
| 	runtime.GoccyGoYaml = goccyGoYaml | 	runtime.GoYamlV3 = GoYamlV3 | ||||||
| 	t.Cleanup(func() { | 	t.Cleanup(func() { | ||||||
| 		runtime.GoccyGoYaml = v | 		runtime.GoYamlV3 = v | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	files := map[string]string{ | 	files := map[string]string{ | ||||||
|  | @ -4161,7 +4161,7 @@ releases: | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestSetValuesTemplate(t *testing.T) { | func TestSetValuesTemplate(t *testing.T) { | ||||||
| 	t.Run("with goccy/go-yaml", func(t *testing.T) { | 	t.Run("with gopkg.in/yaml.v3", func(t *testing.T) { | ||||||
| 		testSetValuesTemplate(t, true) | 		testSetValuesTemplate(t, true) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | @ -4171,7 +4171,7 @@ func TestSetValuesTemplate(t *testing.T) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestSetStringValuesTemplate(t *testing.T) { | func TestSetStringValuesTemplate(t *testing.T) { | ||||||
| 	t.Run("with goccy/go-yaml", func(t *testing.T) { | 	t.Run("with gopkg.in/yaml.v3", func(t *testing.T) { | ||||||
| 		testSetStringValuesTemplate(t, true) | 		testSetStringValuesTemplate(t, true) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,14 +6,13 @@ const ( | ||||||
| 	// use helm status to check if a release exists before installing it
 | 	// use helm status to check if a release exists before installing it
 | ||||||
| 	UseHelmStatusToCheckReleaseExistence = "HELMFILE_USE_HELM_STATUS_TO_CHECK_RELEASE_EXISTENCE" | 	UseHelmStatusToCheckReleaseExistence = "HELMFILE_USE_HELM_STATUS_TO_CHECK_RELEASE_EXISTENCE" | ||||||
| 
 | 
 | ||||||
| 	DisableRunnerUniqueID      = "HELMFILE_DISABLE_RUNNER_UNIQUE_ID" | 	DisableRunnerUniqueID = "HELMFILE_DISABLE_RUNNER_UNIQUE_ID" | ||||||
| 	Experimental               = "HELMFILE_EXPERIMENTAL" // environment variable for experimental features, expecting "true" lower case
 | 	Experimental          = "HELMFILE_EXPERIMENTAL" // environment variable for experimental features, expecting "true" lower case
 | ||||||
| 	Environment                = "HELMFILE_ENVIRONMENT" | 	Environment           = "HELMFILE_ENVIRONMENT" | ||||||
| 	FilePath                   = "HELMFILE_FILE_PATH" | 	FilePath              = "HELMFILE_FILE_PATH" | ||||||
| 	TempDir                    = "HELMFILE_TEMPDIR" | 	TempDir               = "HELMFILE_TEMPDIR" | ||||||
| 	UpgradeNoticeDisabled      = "HELMFILE_UPGRADE_NOTICE_DISABLED" | 	UpgradeNoticeDisabled = "HELMFILE_UPGRADE_NOTICE_DISABLED" | ||||||
| 	GoccyGoYaml                = "HELMFILE_GOCCY_GOYAML" | 	GoYamlV3              = "HELMFILE_GO_YAML_V3" | ||||||
| 	CacheHome                  = "HELMFILE_CACHE_HOME" | 	CacheHome             = "HELMFILE_CACHE_HOME" | ||||||
| 	Interactive                = "HELMFILE_INTERACTIVE" | 	Interactive           = "HELMFILE_INTERACTIVE" | ||||||
| 	EnableGoccyGoYamlJSONStyle = "HELMFILE_ENABLE_GOCCY_GOYAML_JSON_STYLE" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | @ -8,16 +8,16 @@ import ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	// GoccyGoYaml is set to true in order to let Helmfile use
 | 	// GoYamlV3 is set to true in order to let Helmfile use
 | ||||||
| 	// goccy/go-yaml instead of gopkg.in/yaml.v2.
 | 	// gopkg.in/yaml.v3 instead of gopkg.in/yaml.v2.
 | ||||||
| 	// It's false by default in Helmfile v0.x and true by default for Helmfile v1.x.
 | 	// It's false by default in Helmfile v0.x and true in Helmfile v1.x.
 | ||||||
| 	GoccyGoYaml bool | 	GoYamlV3 bool | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func Info() string { | func Info() string { | ||||||
| 	yamlLib := "gopkg.in/yaml.v2" | 	yamlLib := "gopkg.in/yaml.v2" | ||||||
| 	if GoccyGoYaml { | 	if GoYamlV3 { | ||||||
| 		yamlLib = "goccy/go-yaml" | 		yamlLib = "gopkg.in/yaml.v3" | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return fmt.Sprintf("YAML library = %v", yamlLib) | 	return fmt.Sprintf("YAML library = %v", yamlLib) | ||||||
|  | @ -25,12 +25,12 @@ func Info() string { | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	// You can switch the YAML library at runtime via an envvar:
 | 	// You can switch the YAML library at runtime via an envvar:
 | ||||||
| 	switch os.Getenv(envvar.GoccyGoYaml) { | 	switch os.Getenv(envvar.GoYamlV3) { | ||||||
| 	case "true": | 	case "true": | ||||||
| 		GoccyGoYaml = true | 		GoYamlV3 = true | ||||||
| 	case "false": | 	case "false": | ||||||
| 		GoccyGoYaml = false | 		GoYamlV3 = false | ||||||
| 	default: | 	default: | ||||||
| 		GoccyGoYaml = true | 		GoYamlV3 = true | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -22,10 +22,10 @@ func TestExecuteTemplateExpressions(t *testing.T) { | ||||||
| 		}, | 		}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	v := runtime.GoccyGoYaml | 	v := runtime.GoYamlV3 | ||||||
| 	runtime.GoccyGoYaml = true | 	runtime.GoYamlV3 = true | ||||||
| 	t.Cleanup(func() { | 	t.Cleanup(func() { | ||||||
| 		runtime.GoccyGoYaml = v | 		runtime.GoYamlV3 = v | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	rs := ReleaseSpec{ | 	rs := ReleaseSpec{ | ||||||
|  |  | ||||||
|  | @ -4,14 +4,12 @@ import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/fs" | 	"io/fs" | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	goruntime "runtime" | 	goruntime "runtime" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/stretchr/testify/require" | 	"github.com/stretchr/testify/require" | ||||||
| 
 | 
 | ||||||
| 	"github.com/helmfile/helmfile/pkg/envvar" |  | ||||||
| 	"github.com/helmfile/helmfile/pkg/filesystem" | 	"github.com/helmfile/helmfile/pkg/filesystem" | ||||||
| 	"github.com/helmfile/helmfile/pkg/runtime" | 	"github.com/helmfile/helmfile/pkg/runtime" | ||||||
| ) | ) | ||||||
|  | @ -162,9 +160,9 @@ func TestReadFile_PassAbsPath(t *testing.T) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestToYaml_NestedMapInterfaceKey(t *testing.T) { | func TestToYaml_NestedMapInterfaceKey(t *testing.T) { | ||||||
| 	v := runtime.GoccyGoYaml | 	v := runtime.GoYamlV3 | ||||||
| 	t.Cleanup(func() { | 	t.Cleanup(func() { | ||||||
| 		runtime.GoccyGoYaml = v | 		runtime.GoYamlV3 = v | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	// nolint: unconvert
 | 	// nolint: unconvert
 | ||||||
|  | @ -174,13 +172,13 @@ func TestToYaml_NestedMapInterfaceKey(t *testing.T) { | ||||||
| 		}, | 		}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	runtime.GoccyGoYaml = true | 	runtime.GoYamlV3 = true | ||||||
| 
 | 
 | ||||||
| 	actual, err := ToYaml(vals) | 	actual, err := ToYaml(vals) | ||||||
| 	require.Equal(t, "foo:\n  bar: BAR\n", actual) | 	require.Equal(t, "foo:\n  bar: BAR\n", actual) | ||||||
| 	require.NoError(t, err, "expected nil, but got: %v, when type: map[interface {}]interface {}", err) | 	require.NoError(t, err, "expected nil, but got: %v, when type: map[interface {}]interface {}", err) | ||||||
| 
 | 
 | ||||||
| 	runtime.GoccyGoYaml = false | 	runtime.GoYamlV3 = false | ||||||
| 
 | 
 | ||||||
| 	actual, err = ToYaml(vals) | 	actual, err = ToYaml(vals) | ||||||
| 	require.Equal(t, "foo:\n  bar: BAR\n", actual) | 	require.Equal(t, "foo:\n  bar: BAR\n", actual) | ||||||
|  | @ -189,18 +187,16 @@ func TestToYaml_NestedMapInterfaceKey(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| func TestToYaml(t *testing.T) { | func TestToYaml(t *testing.T) { | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
| 		name            string | 		name     string | ||||||
| 		input           any | 		input    any | ||||||
| 		expected        string | 		expected string | ||||||
| 		wantErr         bool | 		wantErr  bool | ||||||
| 		enableJsonStyle bool |  | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			// https://github.com/helmfile/helmfile/issues/2024
 | 			// https://github.com/helmfile/helmfile/issues/2024
 | ||||||
| 			name:            "test unmarshalling issue 2024", | 			name:  "test unmarshalling issue 2024", | ||||||
| 			enableJsonStyle: true, | 			input: map[string]any{"thisShouldBeString": "01234567890123456789"}, | ||||||
| 			input:           map[string]any{"thisShouldBeString": "01234567890123456789"}, | 			expected: `thisShouldBeString: "01234567890123456789" | ||||||
| 			expected: `'thisShouldBeString': '01234567890123456789' |  | ||||||
| `, | `, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|  | @ -247,12 +243,6 @@ func TestToYaml(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| 	for _, tt := range tests { | 	for _, tt := range tests { | ||||||
| 		t.Run(tt.name, func(t *testing.T) { | 		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) | 			actual, err := ToYaml(tt.input) | ||||||
| 			if tt.wantErr { | 			if tt.wantErr { | ||||||
| 				require.Error(t, err) | 				require.Error(t, err) | ||||||
|  | @ -345,13 +335,13 @@ func testFromYamlNull(t *testing.T) { | ||||||
| 	require.Equal(t, nil, actual) | 	require.Equal(t, nil, actual) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func testFromYaml(t *testing.T, goccyGoYaml bool) { | func testFromYaml(t *testing.T, GoYamlV3 bool) { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 
 | 
 | ||||||
| 	v := runtime.GoccyGoYaml | 	v := runtime.GoYamlV3 | ||||||
| 	runtime.GoccyGoYaml = goccyGoYaml | 	runtime.GoYamlV3 = GoYamlV3 | ||||||
| 	t.Cleanup(func() { | 	t.Cleanup(func() { | ||||||
| 		runtime.GoccyGoYaml = v | 		runtime.GoYamlV3 = v | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("test unmarshalling object", testFromYamlObject) | 	t.Run("test unmarshalling object", testFromYamlObject) | ||||||
|  | @ -368,11 +358,11 @@ func testFromYaml(t *testing.T, goccyGoYaml bool) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestFromYaml(t *testing.T) { | func TestFromYaml(t *testing.T) { | ||||||
| 	t.Run("with goccy/go-yaml", func(t *testing.T) { | 	t.Run("with gopkg.in/yaml.v2", func(t *testing.T) { | ||||||
| 		testFromYaml(t, true) | 		testFromYaml(t, true) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("with gopkg.in/yaml.v2", func(t *testing.T) { | 	t.Run("with gopkg.in/yaml.v3", func(t *testing.T) { | ||||||
| 		testFromYaml(t, false) | 		testFromYaml(t, false) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,12 +3,10 @@ package yaml | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"io" | 	"io" | ||||||
| 	"os" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/goccy/go-yaml" |  | ||||||
| 	v2 "gopkg.in/yaml.v2" | 	v2 "gopkg.in/yaml.v2" | ||||||
|  | 	v3 "gopkg.in/yaml.v3" | ||||||
| 
 | 
 | ||||||
| 	"github.com/helmfile/helmfile/pkg/envvar" |  | ||||||
| 	"github.com/helmfile/helmfile/pkg/runtime" | 	"github.com/helmfile/helmfile/pkg/runtime" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -19,24 +17,22 @@ type Encoder interface { | ||||||
| 
 | 
 | ||||||
| // NewEncoder creates and returns a function that is used to encode a Go object to a YAML document
 | // NewEncoder creates and returns a function that is used to encode a Go object to a YAML document
 | ||||||
| func NewEncoder(w io.Writer) Encoder { | func NewEncoder(w io.Writer) Encoder { | ||||||
| 	if runtime.GoccyGoYaml { | 	if runtime.GoYamlV3 { | ||||||
| 		yamlEncoderOpts := []yaml.EncodeOption{} | 		v3Encoder := v3.NewEncoder(w) | ||||||
| 		// enable JSON style if the envvar is set
 | 		v3Encoder.SetIndent(2) | ||||||
| 		if os.Getenv(envvar.EnableGoccyGoYamlJSONStyle) == "true" { | 		return v3Encoder | ||||||
| 			yamlEncoderOpts = append(yamlEncoderOpts, yaml.JSON(), yaml.Flow(false)) |  | ||||||
| 		} |  | ||||||
| 		return yaml.NewEncoder(w, yamlEncoderOpts...) |  | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	return v2.NewEncoder(w) | 	return v2.NewEncoder(w) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Unmarshal(data []byte, v any) error { | func Marshal(v any) ([]byte, error) { | ||||||
| 	if runtime.GoccyGoYaml { | 	var b bytes.Buffer | ||||||
| 		return yaml.Unmarshal(data, v) | 	yamlEncoder := NewEncoder(&b) | ||||||
| 	} | 	err := yamlEncoder.Encode(v) | ||||||
| 
 | 	defer func() { | ||||||
| 	return v2.Unmarshal(data, v) | 		_ = yamlEncoder.Close() | ||||||
|  | 	}() | ||||||
|  | 	return b.Bytes(), err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewDecoder creates and returns a function that is used to decode a YAML document
 | // NewDecoder creates and returns a function that is used to decode a YAML document
 | ||||||
|  | @ -44,19 +40,9 @@ func Unmarshal(data []byte, v any) error { | ||||||
| // When strict is true, this function ensures that every field found in the YAML document
 | // When strict is true, this function ensures that every field found in the YAML document
 | ||||||
| // to have the corresponding field in the decoded Go struct.
 | // to have the corresponding field in the decoded Go struct.
 | ||||||
| func NewDecoder(data []byte, strict bool) func(any) error { | func NewDecoder(data []byte, strict bool) func(any) error { | ||||||
| 	if runtime.GoccyGoYaml { | 	if runtime.GoYamlV3 { | ||||||
| 		var opts []yaml.DecodeOption | 		decoder := v3.NewDecoder(bytes.NewReader(data)) | ||||||
| 		if strict { | 		decoder.KnownFields(strict) | ||||||
| 			opts = append(opts, yaml.DisallowUnknownField()) |  | ||||||
| 		} |  | ||||||
| 		// allow duplicate keys
 |  | ||||||
| 		opts = append(opts, yaml.AllowDuplicateMapKey()) |  | ||||||
| 
 |  | ||||||
| 		decoder := yaml.NewDecoder( |  | ||||||
| 			bytes.NewReader(data), |  | ||||||
| 			opts..., |  | ||||||
| 		) |  | ||||||
| 
 |  | ||||||
| 		return func(v any) error { | 		return func(v any) error { | ||||||
| 			return decoder.Decode(v) | 			return decoder.Decode(v) | ||||||
| 		} | 		} | ||||||
|  | @ -70,29 +56,10 @@ func NewDecoder(data []byte, strict bool) func(any) error { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Marshal(v any) ([]byte, error) { | func Unmarshal(data []byte, v any) error { | ||||||
| 	if runtime.GoccyGoYaml { | 	if runtime.GoYamlV3 { | ||||||
| 		var b bytes.Buffer | 		return v3.Unmarshal(data, v) | ||||||
| 		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() { |  | ||||||
| 			_ = yamlEncoder.Close() |  | ||||||
| 		}() |  | ||||||
| 		return b.Bytes(), err |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return v2.Marshal(v) | 	return v2.Unmarshal(data, v) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,20 +8,20 @@ import ( | ||||||
| 	"github.com/helmfile/helmfile/pkg/runtime" | 	"github.com/helmfile/helmfile/pkg/runtime" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func testYamlMarshal(t *testing.T, goccyGoYaml bool) { | func testYamlMarshal(t *testing.T, GoYamlV3 bool) { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 
 | 
 | ||||||
| 	var yamlLibraryName string | 	var yamlLibraryName string | ||||||
| 	if goccyGoYaml { | 	if GoYamlV3 { | ||||||
| 		yamlLibraryName = "goccy/go-yaml" | 		yamlLibraryName = "gopkg.in/yaml.v3" | ||||||
| 	} else { | 	} else { | ||||||
| 		yamlLibraryName = "gopkg.in/yaml.v2" | 		yamlLibraryName = "gopkg.in/yaml.v2" | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	v := runtime.GoccyGoYaml | 	v := runtime.GoYamlV3 | ||||||
| 	runtime.GoccyGoYaml = goccyGoYaml | 	runtime.GoYamlV3 = GoYamlV3 | ||||||
| 	t.Cleanup(func() { | 	t.Cleanup(func() { | ||||||
| 		runtime.GoccyGoYaml = v | 		runtime.GoYamlV3 = v | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
|  | @ -49,8 +49,8 @@ func testYamlMarshal(t *testing.T, goccyGoYaml bool) { | ||||||
| 				Annotation: "on", | 				Annotation: "on", | ||||||
| 			}}, | 			}}, | ||||||
| 			expected: map[string]string{ | 			expected: map[string]string{ | ||||||
| 				"goccy/go-yaml":    "name: John\ninfo:\n- age: 20\n  address: New York\n  annotation: 'on'\n", |  | ||||||
| 				"gopkg.in/yaml.v2": "name: John\ninfo:\n- age: 20\n  address: New York\n  annotation: \"on\"\n", | 				"gopkg.in/yaml.v2": "name: John\ninfo:\n- age: 20\n  address: New York\n  annotation: \"on\"\n", | ||||||
|  | 				"gopkg.in/yaml.v3": "name: John\ninfo:\n  - age: 20\n    address: New York\n    annotation: \"on\"\n", | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  | @ -63,11 +63,11 @@ func testYamlMarshal(t *testing.T, goccyGoYaml bool) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestYamlMarshal(t *testing.T) { | func TestYamlMarshal(t *testing.T) { | ||||||
| 	t.Run("with goccy/go-yaml", func(t *testing.T) { | 	t.Run("with gopkg.in/yaml.v2", func(t *testing.T) { | ||||||
| 		testYamlMarshal(t, true) | 		testYamlMarshal(t, true) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("with gopkg.in/yaml.v2", func(t *testing.T) { | 	t.Run("with gopkg.in/yaml.v3", func(t *testing.T) { | ||||||
| 		testYamlMarshal(t, false) | 		testYamlMarshal(t, false) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -58,17 +58,17 @@ func (f fakeInit) Force() bool { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestHelmfileTemplateWithBuildCommand(t *testing.T) { | func TestHelmfileTemplateWithBuildCommand(t *testing.T) { | ||||||
| 	t.Run("with goccy/go-yaml", func(t *testing.T) { | 	t.Run("with gopkg.in/yaml.v2", func(t *testing.T) { | ||||||
| 		testHelmfileTemplateWithBuildCommand(t, true) | 		testHelmfileTemplateWithBuildCommand(t, true) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("with gopkg.in/yaml.v2", func(t *testing.T) { | 	t.Run("with gopkg.in/yaml.v3", func(t *testing.T) { | ||||||
| 		testHelmfileTemplateWithBuildCommand(t, false) | 		testHelmfileTemplateWithBuildCommand(t, false) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func testHelmfileTemplateWithBuildCommand(t *testing.T, goccyGoYaml bool) { | func testHelmfileTemplateWithBuildCommand(t *testing.T, GoYamlV3 bool) { | ||||||
| 	t.Setenv(envvar.GoccyGoYaml, strconv.FormatBool(goccyGoYaml)) | 	t.Setenv(envvar.GoYamlV3, strconv.FormatBool(GoYamlV3)) | ||||||
| 
 | 
 | ||||||
| 	localChartPortSets := make(map[int]struct{}) | 	localChartPortSets := make(map[int]struct{}) | ||||||
| 
 | 
 | ||||||
|  | @ -224,7 +224,12 @@ func testHelmfileTemplateWithBuildCommand(t *testing.T, goccyGoYaml bool) { | ||||||
| 			t.Logf("Using HELM_CACHE_HOME=%s, HELMFILE_CACHE_HOME=%s, HELM_CONFIG_HOME=%s", helmCacheHome, helmfileCacheHome, helmConfigHome) | 			t.Logf("Using HELM_CACHE_HOME=%s, HELMFILE_CACHE_HOME=%s, HELM_CONFIG_HOME=%s", helmCacheHome, helmfileCacheHome, helmConfigHome) | ||||||
| 
 | 
 | ||||||
| 			inputFile := filepath.Join(testdataDir, name, "input.yaml.gotmpl") | 			inputFile := filepath.Join(testdataDir, name, "input.yaml.gotmpl") | ||||||
| 			outputFile := filepath.Join(testdataDir, name, "output.yaml") | 			outputFile := "" | ||||||
|  | 			if GoYamlV3 { | ||||||
|  | 				outputFile = filepath.Join(testdataDir, name, "gopkg.in-yaml.v3-output.yaml") | ||||||
|  | 			} else { | ||||||
|  | 				outputFile = filepath.Join(testdataDir, name, "gopkg.in-yaml.v2-output.yaml") | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) | 			ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) | ||||||
| 			defer cancel() | 			defer cancel() | ||||||
|  |  | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | --- | ||||||
|  | #  Source: __workingdir__/testdata/snapshot/issue_2098_release_template_needs/input.yaml.gotmpl | ||||||
|  | 
 | ||||||
|  | filepath: input.yaml.gotmpl | ||||||
|  | helmBinary: helm | ||||||
|  | kustomizeBinary: kustomize | ||||||
|  | environments: | ||||||
|  |   default: {} | ||||||
|  | repositories: | ||||||
|  |   - name: aservo | ||||||
|  |     url: https://aservo.github.io/charts | ||||||
|  | releases: | ||||||
|  |   - chart: aservo/util | ||||||
|  |     version: 0.0.1 | ||||||
|  |     name: default-shared-resources | ||||||
|  |     namespace: default | ||||||
|  |     labels: | ||||||
|  |       chart: util | ||||||
|  |       name: default-shared-resources | ||||||
|  |       namespace: default | ||||||
|  |       service: shared-resources | ||||||
|  |   - chart: aservo/util | ||||||
|  |     version: 0.0.1 | ||||||
|  |     needs: | ||||||
|  |       - default/default-shared-resources | ||||||
|  |     name: default-release-resources | ||||||
|  |     namespace: default | ||||||
|  |     labels: | ||||||
|  |       chart: util | ||||||
|  |       name: default-release-resources | ||||||
|  |       namespace: default | ||||||
|  |       service: release-resources | ||||||
|  | templates: | ||||||
|  |   defaults: | ||||||
|  |     name: default-{{ .Release.Labels.service }} | ||||||
|  |     namespace: default | ||||||
|  | renderedvalues: {} | ||||||
		Loading…
	
		Reference in New Issue