diff --git a/.gitignore b/.gitignore index 66fd13c..8f8e7ff 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +go-yaml-merger \ No newline at end of file diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 86b5f6d..6870025 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -14,7 +14,8 @@ builds: goarch: - amd64 - arm - - arm64 + - arm64 + binary: ym archives: - replacements: darwin: Darwin diff --git a/go.mod b/go.mod index 567efc8..c5a98cc 100644 --- a/go.mod +++ b/go.mod @@ -6,17 +6,23 @@ replace github.com/RaftechNL/go-yaml-merger => /Users/rafpe/raftech/github/rafte require ( github.com/labstack/gommon v0.4.0 - github.com/urfave/cli/v2 v2.24.3 - gopkg.in/yaml.v2 v2.4.0 + github.com/stretchr/testify v1.8.1 ) require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect +) + +require ( github.com/mattn/go-colorable v0.1.11 // indirect github.com/mattn/go-isatty v0.0.14 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/urfave/cli/v2 v2.24.3 github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.1 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect + gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 084e3e8..db4cb77 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,13 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/urfave/cli/v2 v2.24.3 h1:7Q1w8VN8yE0MJEHP06bv89PjYsN4IHWED2s1v/Zlfm0= github.com/urfave/cli/v2 v2.24.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -30,8 +35,7 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1U golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index e9c73ac..3627241 100644 --- a/main.go +++ b/main.go @@ -1,17 +1,15 @@ package main import ( - "bytes" - "context" "errors" "fmt" "io/ioutil" "os" - "sync" + "strings" "github.com/labstack/gommon/log" "github.com/urfave/cli/v2" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) var version = "v0.0.1" @@ -40,14 +38,13 @@ func main() { }, }, Action: func(c *cli.Context) error { - ctx := context.Background() // Read input files inputFiles := c.StringSlice("input") if len(inputFiles) <= 0 { return cli.ShowAppHelp(c) } - mergedData, err := MergeYAML(ctx, inputFiles...) + mergedData, err := MergeYAML(inputFiles...) if err != nil { return err } @@ -73,91 +70,57 @@ func main() { } } -func MergeYAML(ctx context.Context, filenames ...string) ([]byte, error) { - if len(filenames) <= 0 { - return nil, errors.New("You must provide at least one filename for merging YAML files") +func removeLastLineIfEmpty(s string) string { + lines := strings.Split(s, "\n") + lastLine := strings.TrimSpace(lines[len(lines)-1]) + if lastLine == "" { + lines = lines[:len(lines)-1] } + return strings.Join(lines, "\n") +} - var buf bytes.Buffer - encoder := yaml.NewEncoder(&buf) - - var resultValues sync.Map - +func MergeYAML(filenames ...string) ([]byte, error) { + if len(filenames) <= 0 { + return nil, errors.New("You must provide at least one filename for reading Values") + } + var resultValues map[string]interface{} for _, filename := range filenames { - // Check if the context has been cancelled - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } var override map[string]interface{} bs, err := ioutil.ReadFile(filename) if err != nil { log.Info(err) - continue + return nil, fmt.Errorf("failed to read file %q: %w", filename, err) } if err := yaml.Unmarshal(bs, &override); err != nil { log.Info(err) - continue + return nil, fmt.Errorf("failed to unmarshal data from file %q: %w", filename, err) } - // Merge override map with resultValues map - resultValues.Range(func(key, value interface{}) bool { - overrideKey := key.(string) - overrideValue := value - if _, ok := override[overrideKey]; !ok { - override[overrideKey] = overrideValue + //check if is nil. This will only happen for the first filename + if resultValues == nil { + resultValues = override + } else { + for k, v := range override { + // Check if value is nil before adding to resultValues + if v != nil { + resultValues[k] = v + } } - return true - }) - - // Store merged map in resultValues - resultValues.Store(filename, override) - - // Encode override map to buffer - if err := encoder.Encode(override); err != nil { - log.Info(err) - continue } - } - // Check if the context has been cancelled - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: } - if err := encoder.Close(); err != nil { + bs, err := yaml.Marshal(resultValues) + if err != nil { log.Info(err) return nil, err } - var mergedMap map[string]interface{} - resultValues.Range(func(key, value interface{}) bool { - override := value.(map[string]interface{}) - if mergedMap == nil { - mergedMap = override - } else { - for k, v := range override { - mergedMap[k] = v - } - } - return true - }) - - bs, err := yaml.Marshal(mergedMap) if err != nil { log.Info(err) return nil, err } - // Save result to file - if err := ioutil.WriteFile("result.yaml", bs, 0644); err != nil { - fmt.Println(err) - return nil, err - } - return bs, nil } diff --git a/merge_test.go b/merge_test.go new file mode 100644 index 0000000..5b5a139 --- /dev/null +++ b/merge_test.go @@ -0,0 +1,48 @@ +// file: main_test.go +package main + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMergeYAML_TwoFiles(t *testing.T) { + result, err := MergeYAML("tests/test1.yaml", "tests/test2.yaml") + assert.NoError(t, err) + assert.Equal(t, strings.TrimSpace(` +modules: + my_module: + providerAliasRef: my_provider2 + source: github.com/example/module + version: v1.6.0 + my_module2: + providerAliasRef: my_provider2 + source: github.com/example/module + version: v1.6.0 + my_module3: + providerAliasRef: my_provider2 + source: github.com/example/module + version: v1.6.0 +providers: + my_provider: + auth: + ssh_key: ssh:key:2312312 + providerType: github +`), strings.TrimSpace(string(result))) +} + +func TestMergeYAML_MissingFile(t *testing.T) { + result, err := MergeYAML("tests/test1.yaml", "tests/test3.yaml") + assert.Error(t, err) + assert.Contains(t, err.Error(), "no such file or directory") + assert.Equal(t, "", string(result)) +} + +func TestMergeYAML_InvalidYAML(t *testing.T) { + result, err := MergeYAML("tests/test1.yaml", "tests/invalid.yaml") + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to unmarshal data from file") + assert.Equal(t, "", string(result)) +} diff --git a/tests/invalid.yaml b/tests/invalid.yaml new file mode 100644 index 0000000..f813d74 --- /dev/null +++ b/tests/invalid.yaml @@ -0,0 +1,3 @@ +--- +foo: bar +{{ invalid yaml }} diff --git a/tests/test1.yaml b/tests/test1.yaml new file mode 100644 index 0000000..f8c742d --- /dev/null +++ b/tests/test1.yaml @@ -0,0 +1,10 @@ +providers: + my_provider: + providerType: github + auth: + ssh_key: "ssh:key:2312312" +modules: + my_module: + source: "github.com/example/module" + version: "v1.0.0" + providerAliasRef: "my_provider2" \ No newline at end of file diff --git a/tests/test2.yaml b/tests/test2.yaml new file mode 100644 index 0000000..34c03ee --- /dev/null +++ b/tests/test2.yaml @@ -0,0 +1,13 @@ +modules: + my_module: + source: "github.com/example/module" + version: "v1.6.0" + providerAliasRef: "my_provider2" + my_module2: + source: "github.com/example/module" + version: "v1.6.0" + providerAliasRef: "my_provider2" + my_module3: + source: "github.com/example/module" + version: "v1.6.0" + providerAliasRef: "my_provider2" \ No newline at end of file