mirror of
https://github.com/mikefarah/yq.git
synced 2025-01-23 22:25:42 +00:00
Simplified merge command
This commit is contained in:
parent
08870f8ec9
commit
8ca85b1c64
@ -601,7 +601,7 @@ func TestMergeCmd_ErrorUnreadableFile(t *testing.T) {
|
|||||||
if result.Error == nil {
|
if result.Error == nil {
|
||||||
t.Error("Expected command to fail due to unknown file")
|
t.Error("Expected command to fail due to unknown file")
|
||||||
}
|
}
|
||||||
expectedOutput := `open fake-unknown: no such file or directory`
|
expectedOutput := `Error updating document at index 0: open fake-unknown: no such file or directory`
|
||||||
assertResult(t, expectedOutput, result.Error.Error())
|
assertResult(t, expectedOutput, result.Error.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,5 +637,5 @@ b:
|
|||||||
- 2
|
- 2
|
||||||
c:
|
c:
|
||||||
test: 1`
|
test: 1`
|
||||||
assertResult(t, expectedOutput, gotOutput)
|
assertResult(t, expectedOutput, strings.Trim(gotOutput, "\n "))
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
@ -194,23 +193,6 @@ func calculateValue(value interface{}, tail []string) (interface{}, error) {
|
|||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapToMapSlice(data map[interface{}]interface{}) yaml.MapSlice {
|
|
||||||
var mapSlice yaml.MapSlice
|
|
||||||
|
|
||||||
for k, v := range data {
|
|
||||||
if mv, ok := v.(map[interface{}]interface{}); ok {
|
|
||||||
v = mapToMapSlice(mv)
|
|
||||||
}
|
|
||||||
item := yaml.MapItem{Key: k, Value: v}
|
|
||||||
mapSlice = append(mapSlice, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// because the parsing of the yaml was done via a map the order will be inconsistent
|
|
||||||
// apply order to allow a consistent output
|
|
||||||
sort.SliceStable(mapSlice, func(i, j int) bool { return mapSlice[i].Key.(string) < mapSlice[j].Key.(string) })
|
|
||||||
return mapSlice
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteMap(context interface{}, paths []string) yaml.MapSlice {
|
func deleteMap(context interface{}, paths []string) yaml.MapSlice {
|
||||||
log.Debugf("deleteMap for %v for %v\n", paths, context)
|
log.Debugf("deleteMap for %v for %v\n", paths, context)
|
||||||
|
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMerge(t *testing.T) {
|
|
||||||
result, _ := mergeYaml([]string{"examples/data1.yaml", "examples/data2.yaml", "examples/data3.yaml"})
|
|
||||||
expected := yaml.MapSlice{
|
|
||||||
yaml.MapItem{Key: "a", Value: "simple"},
|
|
||||||
yaml.MapItem{Key: "b", Value: []interface{}{1, 2}},
|
|
||||||
yaml.MapItem{Key: "c", Value: yaml.MapSlice{yaml.MapItem{Key: "other", Value: true}, yaml.MapItem{Key: "test", Value: 1}}},
|
|
||||||
yaml.MapItem{Key: "d", Value: false},
|
|
||||||
}
|
|
||||||
assertResultComplex(t, expected, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMergeWithOverwrite(t *testing.T) {
|
|
||||||
overwriteFlag = true
|
|
||||||
result, _ := mergeYaml([]string{"examples/data1.yaml", "examples/data2.yaml", "examples/data3.yaml"})
|
|
||||||
expected := yaml.MapSlice{
|
|
||||||
yaml.MapItem{Key: "a", Value: "other"},
|
|
||||||
yaml.MapItem{Key: "b", Value: []interface{}{2, 3, 4}},
|
|
||||||
yaml.MapItem{Key: "c", Value: yaml.MapSlice{yaml.MapItem{Key: "other", Value: true}, yaml.MapItem{Key: "test", Value: 2}}},
|
|
||||||
yaml.MapItem{Key: "d", Value: false},
|
|
||||||
}
|
|
||||||
assertResultComplex(t, expected, result)
|
|
||||||
}
|
|
78
yq.go
78
yq.go
@ -275,13 +275,14 @@ func newYaml(args []string) (interface{}, error) {
|
|||||||
return dataBucket, nil
|
return dataBucket, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type updateDataFn func(dataBucket interface{}, currentIndex int) interface{}
|
type updateDataFn func(dataBucket interface{}, currentIndex int) (interface{}, error)
|
||||||
|
|
||||||
func mapYamlDecoder(updateData updateDataFn, encoder *yaml.Encoder) yamlDecoderFn {
|
func mapYamlDecoder(updateData updateDataFn, encoder *yaml.Encoder) yamlDecoderFn {
|
||||||
return func(decoder *yaml.Decoder) error {
|
return func(decoder *yaml.Decoder) error {
|
||||||
var dataBucket interface{}
|
var dataBucket interface{}
|
||||||
var errorReading error
|
var errorReading error
|
||||||
var errorWriting error
|
var errorWriting error
|
||||||
|
var errorUpdating error
|
||||||
var currentIndex = 0
|
var currentIndex = 0
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -296,7 +297,10 @@ func mapYamlDecoder(updateData updateDataFn, encoder *yaml.Encoder) yamlDecoderF
|
|||||||
} else if errorReading != nil {
|
} else if errorReading != nil {
|
||||||
return errors.Wrapf(errorReading, "Error reading document at index %v, %v", currentIndex, errorReading)
|
return errors.Wrapf(errorReading, "Error reading document at index %v, %v", currentIndex, errorReading)
|
||||||
}
|
}
|
||||||
dataBucket = updateData(dataBucket, currentIndex)
|
dataBucket, errorUpdating = updateData(dataBucket, currentIndex)
|
||||||
|
if errorUpdating != nil {
|
||||||
|
return errors.Wrapf(errorUpdating, "Error updating document at index %v", currentIndex)
|
||||||
|
}
|
||||||
|
|
||||||
errorWriting = encoder.Encode(dataBucket)
|
errorWriting = encoder.Encode(dataBucket)
|
||||||
|
|
||||||
@ -313,7 +317,7 @@ func writeProperty(cmd *cobra.Command, args []string) error {
|
|||||||
if writeCommandsError != nil {
|
if writeCommandsError != nil {
|
||||||
return writeCommandsError
|
return writeCommandsError
|
||||||
}
|
}
|
||||||
var updateData = func(dataBucket interface{}, currentIndex int) interface{} {
|
var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
|
||||||
if currentIndex == docIndex {
|
if currentIndex == docIndex {
|
||||||
log.Debugf("Updating doc %v", currentIndex)
|
log.Debugf("Updating doc %v", currentIndex)
|
||||||
for _, entry := range writeCommands {
|
for _, entry := range writeCommands {
|
||||||
@ -324,7 +328,7 @@ func writeProperty(cmd *cobra.Command, args []string) error {
|
|||||||
dataBucket = updatedChildValue(dataBucket, paths, value)
|
dataBucket = updatedChildValue(dataBucket, paths, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dataBucket
|
return dataBucket, nil
|
||||||
}
|
}
|
||||||
return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
|
return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
|
||||||
}
|
}
|
||||||
@ -354,34 +358,18 @@ func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn)
|
|||||||
return readStream(inputFile, mapYamlDecoder(updateData, encoder))
|
return readStream(inputFile, mapYamlDecoder(updateData, encoder))
|
||||||
}
|
}
|
||||||
|
|
||||||
func write(cmd *cobra.Command, filename string, updatedData interface{}) error {
|
|
||||||
if writeInplace {
|
|
||||||
dataStr, err := yamlToString(updatedData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return ioutil.WriteFile(filename, []byte(dataStr), 0644)
|
|
||||||
}
|
|
||||||
dataStr, err := toString(updatedData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cmd.Println(dataStr)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteProperty(cmd *cobra.Command, args []string) error {
|
func deleteProperty(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return errors.New("Must provide <filename> <path_to_delete>")
|
return errors.New("Must provide <filename> <path_to_delete>")
|
||||||
}
|
}
|
||||||
var deletePath = args[1]
|
var deletePath = args[1]
|
||||||
var paths = parsePath(deletePath)
|
var paths = parsePath(deletePath)
|
||||||
var updateData = func(dataBucket interface{}, currentIndex int) interface{} {
|
var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
|
||||||
if currentIndex == docIndex {
|
if currentIndex == docIndex {
|
||||||
log.Debugf("Updating doc %v", currentIndex)
|
log.Debugf("Deleting path in doc %v", currentIndex)
|
||||||
return deleteChildValue(dataBucket, paths)
|
return deleteChildValue(dataBucket, paths), nil
|
||||||
}
|
}
|
||||||
return dataBucket
|
return dataBucket, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
|
return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
|
||||||
@ -391,28 +379,32 @@ func mergeProperties(cmd *cobra.Command, args []string) error {
|
|||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return errors.New("Must provide at least 2 yaml files")
|
return errors.New("Must provide at least 2 yaml files")
|
||||||
}
|
}
|
||||||
|
var input = args[0]
|
||||||
|
var filesToMerge = args[1:]
|
||||||
|
|
||||||
updatedData, err := mergeYaml(args)
|
var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
|
||||||
if err != nil {
|
if currentIndex == docIndex {
|
||||||
return err
|
log.Debugf("Merging doc %v", currentIndex)
|
||||||
}
|
var mergedData map[interface{}]interface{}
|
||||||
return write(cmd, args[0], updatedData)
|
if err := merge(&mergedData, dataBucket, overwriteFlag); err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
func mergeYaml(args []string) (interface{}, error) {
|
for _, f := range filesToMerge {
|
||||||
var updatedData map[interface{}]interface{}
|
var fileToMerge interface{}
|
||||||
|
if err := readData(f, 0, &fileToMerge); err != nil {
|
||||||
for _, f := range args {
|
return nil, err
|
||||||
var parsedData map[interface{}]interface{}
|
}
|
||||||
if err := readData(f, 0, &parsedData); err != nil {
|
if err := merge(&mergedData, fileToMerge, overwriteFlag); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := merge(&updatedData, parsedData, overwriteFlag); err != nil {
|
}
|
||||||
return nil, err
|
return mergedData, nil
|
||||||
}
|
}
|
||||||
|
return dataBucket, nil
|
||||||
}
|
}
|
||||||
|
yaml.DefaultMapType = reflect.TypeOf(map[interface{}]interface{}{})
|
||||||
return mapToMapSlice(updatedData), nil
|
defer func() { yaml.DefaultMapType = reflect.TypeOf(yaml.MapSlice{}) }()
|
||||||
|
return readAndUpdate(cmd.OutOrStdout(), input, updateData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readWriteCommands(args []string, expectedArgs int, badArgsMessage string) (yaml.MapSlice, error) {
|
func readWriteCommands(args []string, expectedArgs int, badArgsMessage string) (yaml.MapSlice, error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user