From 18aac6508b00db9041a3128ac1b511803b36eb8e Mon Sep 17 00:00:00 2001 From: GLEF1X Date: Mon, 29 Jul 2024 22:38:10 -0400 Subject: [PATCH] fix(cli): make yaml exporting reliable and standardized --- cli/go.mod | 1 + cli/go.sum | 1 + cli/packages/cmd/export.go | 18 +++++--- cli/packages/cmd/export_test.go | 79 +++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 cli/packages/cmd/export_test.go diff --git a/cli/go.mod b/cli/go.mod index bcde660ab..3c0a240bb 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -77,6 +77,7 @@ require ( github.com/muesli/termenv v0.15.2 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/pelletier/go-toml v1.9.3 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/afero v1.6.0 // indirect diff --git a/cli/go.sum b/cli/go.sum index d6f04aed0..7932bed99 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -341,6 +341,7 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9 h1:lL+y4Xv20pVlCGyLzNHRC0I0rIHhIL1lTvHizoS/dU8= github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9/go.mod h1:EHPiTAKtiFmrMldLUNswFwfZ2eJIYBHktdaUTZxYWRw= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go index c0fea738e..5ae273fdc 100644 --- a/cli/packages/cmd/export.go +++ b/cli/packages/cmd/export.go @@ -12,8 +12,10 @@ import ( "github.com/Infisical/infisical-merge/packages/models" "github.com/Infisical/infisical-merge/packages/util" + "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/spf13/cobra" + "gopkg.in/yaml.v2" ) const ( @@ -188,7 +190,7 @@ func formatEnvs(envs []models.SingleEnvironmentVariable, format string) (string, case FormatCSV: return formatAsCSV(envs), nil case FormatYaml: - return formatAsYaml(envs), nil + return formatAsYaml(envs) default: return "", fmt.Errorf("invalid format type: %s. Available format types are [%s]", format, []string{FormatDotenv, FormatJson, FormatCSV, FormatYaml, FormatDotEnvExport}) } @@ -224,12 +226,18 @@ func formatAsDotEnvExport(envs []models.SingleEnvironmentVariable) string { return dotenv } -func formatAsYaml(envs []models.SingleEnvironmentVariable) string { - var dotenv string +func formatAsYaml(envs []models.SingleEnvironmentVariable) (string, error) { + m := make(map[string]string) for _, env := range envs { - dotenv += fmt.Sprintf("%s: %s\n", env.Key, env.Value) + m[env.Key] = env.Value } - return dotenv + + yamlBytes, err := yaml.Marshal(m) + if err != nil { + return "", errors.Wrap(err, "unable to format env variables as yaml") + } + + return string(yamlBytes), nil } // Format environment variables as a JSON file diff --git a/cli/packages/cmd/export_test.go b/cli/packages/cmd/export_test.go new file mode 100644 index 000000000..d26741165 --- /dev/null +++ b/cli/packages/cmd/export_test.go @@ -0,0 +1,79 @@ +package cmd // Replace with your actual package name + +import ( + "testing" + + "github.com/Infisical/infisical-merge/packages/models" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v2" +) + +func TestFormatAsYaml(t *testing.T) { + tests := []struct { + name string + input []models.SingleEnvironmentVariable + expected string + }{ + { + name: "Empty input", + input: []models.SingleEnvironmentVariable{}, + expected: "{}\n", + }, + { + name: "Single environment variable", + input: []models.SingleEnvironmentVariable{ + {Key: "KEY1", Value: "VALUE1"}, + }, + expected: "KEY1: VALUE1\n", + }, + { + name: "Multiple environment variables", + input: []models.SingleEnvironmentVariable{ + {Key: "KEY1", Value: "VALUE1"}, + {Key: "KEY2", Value: "VALUE2"}, + {Key: "KEY3", Value: "VALUE3"}, + }, + expected: "KEY1: VALUE1\nKEY2: VALUE2\nKEY3: VALUE3\n", + }, + { + name: "Overwriting duplicate keys", + input: []models.SingleEnvironmentVariable{ + {Key: "KEY1", Value: "VALUE1"}, + {Key: "KEY1", Value: "VALUE2"}, + }, + expected: "KEY1: VALUE2\n", + }, + { + name: "Special characters in values", + input: []models.SingleEnvironmentVariable{ + {Key: "KEY1", Value: "Value with spaces"}, + {Key: "KEY2", Value: "Value:with:colons"}, + {Key: "KEY3", Value: "Value\nwith\nnewlines"}, + }, + expected: "KEY1: Value with spaces\nKEY2: Value:with:colons\nKEY3: |-\n Value\n with\n newlines\n", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := formatAsYaml(tt.input) + assert.NoError(t, err) + + // Compare the result with the expected output + assert.Equal(t, tt.expected, result) + + // Additionally, parse the result back into a map to ensure it's valid YAML + var resultMap map[string]string + err = yaml.Unmarshal([]byte(result), &resultMap) + assert.NoError(t, err) + + // Create an expected map from the input + expectedMap := make(map[string]string) + for _, env := range tt.input { + expectedMap[env.Key] = env.Value + } + + assert.Equal(t, expectedMap, resultMap) + }) + } +}