Merge can allow empty merges!

This commit is contained in:
Mike Farah 2020-01-06 16:22:24 +13:00
parent e3f4eedd51
commit d8c29b26c1
2 changed files with 49 additions and 122 deletions

View File

@ -243,7 +243,7 @@ func TestReadBadDocumentIndexCmd(t *testing.T) {
if result.Error == nil { if result.Error == nil {
t.Error("Expected command to fail due to invalid path") t.Error("Expected command to fail due to invalid path")
} }
expectedOutput := `asked to process document index 1 but there are only 1 document(s)` expectedOutput := `Could not process document index 1 as there are only 1 document(s)`
test.AssertResult(t, expectedOutput, result.Error.Error()) test.AssertResult(t, expectedOutput, result.Error.Error())
} }
@ -829,18 +829,6 @@ func TestNewCmd_Error(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Error.Error()) test.AssertResult(t, expectedOutput, result.Error.Error())
} }
func TestNewCmd_Verbose(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "-v new b.c 3")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b:
c: 3
`
test.AssertResult(t, expectedOutput, result.Output)
}
func TestWriteCmd(t *testing.T) { func TestWriteCmd(t *testing.T) {
content := `b: content := `b:
c: 3 c: 3
@ -998,24 +986,6 @@ func TestWriteCmd_ErrorUnreadableFile(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Error.Error()) test.AssertResult(t, expectedOutput, result.Error.Error())
} }
func TestWriteCmd_Verbose(t *testing.T) {
content := `b:
c: 3
`
filename := test.WriteTempYamlFile(content)
defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("-v write %s b.c 7", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b:
c: 7
`
test.AssertResult(t, expectedOutput, result.Output)
}
func TestWriteCmd_Inplace(t *testing.T) { func TestWriteCmd_Inplace(t *testing.T) {
content := `b: content := `b:
c: 3 c: 3
@ -1193,7 +1163,7 @@ b:
defer test.RemoveTempYamlFile(filename) defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("delete -v %s b.hi[*].thing", filename)) result := test.RunCmd(cmd, fmt.Sprintf("delete %s b.hi[*].thing", filename))
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -1224,7 +1194,7 @@ b:
defer test.RemoveTempYamlFile(filename) defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, fmt.Sprintf("delete -v %s b.there*.c", filename)) result := test.RunCmd(cmd, fmt.Sprintf("delete %s b.there*.c", filename))
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -1315,7 +1285,7 @@ func TestMergeCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `a: simple expectedOutput := `a: simple # just the best
b: [1, 2] b: [1, 2]
c: c:
test: 1 test: 1
@ -1332,7 +1302,7 @@ func TestMergeNoAutoCreateCmd(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `a: simple expectedOutput := `a: simple # just the best
b: [1, 2] b: [1, 2]
c: c:
test: 1 test: 1
@ -1401,7 +1371,7 @@ c:
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
func xTestMergeYamlMultiAllCmd(t *testing.T) { func TestMergeYamlMultiAllCmd(t *testing.T) {
content := `b: content := `b:
c: 3 c: 3
apples: green apples: green
@ -1420,17 +1390,18 @@ something: good`
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `apples: green expectedOutput := `b:
b:
c: 3 c: 3
apples: green
something: good something: good
--- ---
something: else
apples: red apples: red
something: else` `
test.AssertResult(t, expectedOutput, strings.Trim(result.Output, "\n ")) test.AssertResult(t, expectedOutput, result.Output)
} }
func xTestMergeYamlMultiAllOverwriteCmd(t *testing.T) { func TestMergeYamlMultiAllOverwriteCmd(t *testing.T) {
content := `b: content := `b:
c: 3 c: 3
apples: green apples: green
@ -1449,17 +1420,18 @@ something: good`
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `apples: red expectedOutput := `b:
b:
c: 3 c: 3
apples: red
something: good something: good
--- ---
something: good
apples: red apples: red
something: good` `
test.AssertResult(t, expectedOutput, strings.Trim(result.Output, "\n ")) test.AssertResult(t, expectedOutput, result.Output)
} }
func xTestMergeCmd_Error(t *testing.T) { func TestMergeCmd_Error(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "merge examples/data1.yaml") result := test.RunCmd(cmd, "merge examples/data1.yaml")
if result.Error == nil { if result.Error == nil {
@ -1469,7 +1441,7 @@ func xTestMergeCmd_Error(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Error.Error()) test.AssertResult(t, expectedOutput, result.Error.Error())
} }
func xTestMergeCmd_ErrorUnreadableFile(t *testing.T) { func TestMergeCmd_ErrorUnreadableFile(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "merge examples/data1.yaml fake-unknown") result := test.RunCmd(cmd, "merge examples/data1.yaml fake-unknown")
if result.Error == nil { if result.Error == nil {
@ -1477,30 +1449,14 @@ func xTestMergeCmd_ErrorUnreadableFile(t *testing.T) {
} }
var expectedOutput string var expectedOutput string
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
expectedOutput = `Error updating document at index 0: open fake-unknown: The system cannot find the file specified.` expectedOutput = `open fake-unknown: The system cannot find the file specified.`
} else { } else {
expectedOutput = `Error updating document at index 0: open fake-unknown: no such file or directory` expectedOutput = `open fake-unknown: no such file or directory`
} }
test.AssertResult(t, expectedOutput, result.Error.Error()) test.AssertResult(t, expectedOutput, result.Error.Error())
} }
func xTestMergeCmd_Verbose(t *testing.T) { func TestMergeCmd_Inplace(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "-v merge examples/data1.yaml examples/data2.yaml")
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `a: simple
b:
- 1
- 2
c:
test: 1
`
test.AssertResult(t, expectedOutput, result.Output)
}
func xTestMergeCmd_Inplace(t *testing.T) {
filename := test.WriteTempYamlFile(test.ReadTempYamlFile("examples/data1.yaml")) filename := test.WriteTempYamlFile(test.ReadTempYamlFile("examples/data1.yaml"))
err := os.Chmod(filename, os.FileMode(int(0666))) err := os.Chmod(filename, os.FileMode(int(0666)))
if err != nil { if err != nil {
@ -1515,26 +1471,35 @@ func xTestMergeCmd_Inplace(t *testing.T) {
} }
info, _ := os.Stat(filename) info, _ := os.Stat(filename)
gotOutput := test.ReadTempYamlFile(filename) gotOutput := test.ReadTempYamlFile(filename)
expectedOutput := `a: simple expectedOutput := `a: simple # just the best
b: b: [1, 2]
- 1
- 2
c: c:
test: 1` test: 1
test.AssertResult(t, expectedOutput, strings.Trim(gotOutput, "\n ")) toast: leave
tell: 1
taco: cool
`
test.AssertResult(t, expectedOutput, gotOutput)
test.AssertResult(t, os.FileMode(int(0666)), info.Mode()) test.AssertResult(t, os.FileMode(int(0666)), info.Mode())
} }
func xTestMergeAllowEmptyCmd(t *testing.T) { func TestMergeAllowEmptyCmd(t *testing.T) {
cmd := getRootCommand() cmd := getRootCommand()
result := test.RunCmd(cmd, "merge --allow-empty examples/data1.yaml examples/empty.yaml") result := test.RunCmd(cmd, "merge --allow-empty examples/data1.yaml examples/empty.yaml")
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
expectedOutput := `a: simple expectedOutput := `a: simple # just the best
b: b: [1, 2]
- 1 c:
- 2 test: 1
` `
test.AssertResult(t, expectedOutput, result.Output) test.AssertResult(t, expectedOutput, result.Output)
} }
func TestMergeDontAllowEmptyCmd(t *testing.T) {
cmd := getRootCommand()
result := test.RunCmd(cmd, "merge examples/data1.yaml examples/empty.yaml")
expectedOutput := `Could not process document index 0 as there are only 0 document(s)`
test.AssertResult(t, expectedOutput, result.Error.Error())
}

52
yq.go
View File

@ -7,6 +7,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"strconv" "strconv"
"strings"
"github.com/mikefarah/yq/v3/pkg/yqlib" "github.com/mikefarah/yq/v3/pkg/yqlib"
@ -239,7 +240,7 @@ Note that if you set both flags only overwrite will take effect.
cmdMerge.PersistentFlags().BoolVarP(&overwriteFlag, "overwrite", "x", false, "update the yaml file by overwriting existing values") cmdMerge.PersistentFlags().BoolVarP(&overwriteFlag, "overwrite", "x", false, "update the yaml file by overwriting existing values")
cmdMerge.PersistentFlags().BoolVarP(&autoCreateFlag, "autocreate", "c", true, "automatically create any missing entries") cmdMerge.PersistentFlags().BoolVarP(&autoCreateFlag, "autocreate", "c", true, "automatically create any missing entries")
cmdMerge.PersistentFlags().BoolVarP(&appendFlag, "append", "a", false, "update the yaml file by appending array values") cmdMerge.PersistentFlags().BoolVarP(&appendFlag, "append", "a", false, "update the yaml file by appending array values")
// cmdMerge.PersistentFlags().BoolVarP(&allowEmptyFlag, "allow-empty", "e", false, "allow empty yaml files") cmdMerge.PersistentFlags().BoolVarP(&allowEmptyFlag, "allow-empty", "e", false, "allow empty yaml files")
cmdMerge.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") cmdMerge.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
return cmdMerge return cmdMerge
} }
@ -293,7 +294,7 @@ func readYamlFile(filename string, path string, updateAll bool, docIndexInt int)
func handleEOF(updateAll bool, docIndexInt int, currentIndex int) error { func handleEOF(updateAll bool, docIndexInt int, currentIndex int) error {
log.Debugf("done %v / %v", currentIndex, docIndexInt) log.Debugf("done %v / %v", currentIndex, docIndexInt)
if !updateAll && currentIndex <= docIndexInt { if !updateAll && currentIndex <= docIndexInt {
return fmt.Errorf("asked to process document index %v but there are only %v document(s)", docIndex, currentIndex) return fmt.Errorf("Could not process document index %v as there are only %v document(s)", docIndex, currentIndex)
} }
return nil return nil
} }
@ -425,13 +426,16 @@ func writeProperty(cmd *cobra.Command, args []string) error {
} }
func mergeProperties(cmd *cobra.Command, args []string) error { func mergeProperties(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return errors.New("Must provide at least 2 yaml files")
}
// first generate update commands from the file // first generate update commands from the file
var filesToMerge = args[1:] var filesToMerge = args[1:]
var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0) var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0)
for _, fileToMerge := range filesToMerge { for _, fileToMerge := range filesToMerge {
matchingNodes, errorProcessingFile := readYamlFile(fileToMerge, "**", false, 0) matchingNodes, errorProcessingFile := readYamlFile(fileToMerge, "**", false, 0)
if errorProcessingFile != nil { if errorProcessingFile != nil && (allowEmptyFlag == false || !strings.HasPrefix(errorProcessingFile.Error(), "Could not process document index")) {
return errorProcessingFile return errorProcessingFile
} }
for _, matchingNode := range matchingNodes { for _, matchingNode := range matchingNodes {
@ -569,48 +573,6 @@ func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn)
return readStream(inputFile, mapYamlDecoder(updateData, encoder)) return readStream(inputFile, mapYamlDecoder(updateData, encoder))
} }
// func mergeProperties(cmd *cobra.Command, args []string) error {
// if len(args) < 2 {
// return errors.New("Must provide at least 2 yaml files")
// }
// var input = args[0]
// var filesToMerge = args[1:]
// var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
// if errorParsingDocIndex != nil {
// return errorParsingDocIndex
// }
// var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
// if updateAll || currentIndex == docIndexInt {
// log.Debugf("Merging doc %v", currentIndex)
// var mergedData map[interface{}]interface{}
// // merge only works for maps, so put everything in a temporary
// // map
// var mapDataBucket = make(map[interface{}]interface{})
// mapDataBucket["root"] = dataBucket
// if err := lib.Merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil {
// return nil, err
// }
// for _, f := range filesToMerge {
// var fileToMerge interface{}
// if err := readData(f, 0, &fileToMerge); err != nil {
// if allowEmptyFlag && err == io.EOF {
// continue
// }
// return nil, err
// }
// mapDataBucket["root"] = fileToMerge
// if err := lib.Merge(&mergedData, mapDataBucket, overwriteFlag, appendFlag); err != nil {
// return nil, err
// }
// }
// return mergedData["root"], nil
// }
// return dataBucket, nil
// }
// return readAndUpdate(cmd.OutOrStdout(), input, updateData)
// }
type updateCommandParsed struct { type updateCommandParsed struct {
Command string Command string
Path string Path string