diff --git a/Upgrade Notes b/Upgrade Notes index 38f97368..9f2eecdf 100644 --- a/Upgrade Notes +++ b/Upgrade Notes @@ -1,11 +1,55 @@ # New Features - - Keeps comments and formatting (e.g. inline arrays)! - - Handles anchors! + - Keeps yaml comments and formatting (string blocks are saved, number formatting is preserved, so it won't drop off trailing 0s for values like 0.10, which is important when that's a version entry ) + + - Handles anchors! (doc link) - Can specify yaml tags (e.g. !!int), quoting values no longer sufficient, need to specify the tag value instead. + - Can print out matching paths and values when splatting (doc link) - JSON output works for all commands! Yaml files with multiple documents are printed out as one JSON document per line. + - Deep splat (**) to match arbitrary paths, (doc link) -# Update scripts file format has changed to be more powerful. Comments can be added, and delete commands have been introduced. + +# Breaking changes + +## Update scripts file format has changed to be more powerful. +Comments can be added, and delete commands have been introduced. + +## Reading and splatting, matching results are printed once per line. + e.g: + +```json +parent: + childA: + no: matches here + childB: + there: matches + hi: no match + there2: also matches +``` + +yq r sample.yaml 'parent.*.there*' + +old +```yaml +- null +- - matches + - also matches +``` + +new +```yaml +matches +also matches +``` + +and you can print the matching paths: + +yq r --printMode pv sample.yaml 'parent.*.there*' + +```yaml +parent.childB.there: matches +parent.childB.there2: also matches +``` # Merge command - New flag 'autocreates' missing entries in target by default, new flag to turn that off. diff --git a/commands_test.go b/commands_test.go index bf0b438f..487bb7fd 100644 --- a/commands_test.go +++ b/commands_test.go @@ -96,7 +96,7 @@ func TestReadCmd(t *testing.T) { func TestReadWithKeyAndValueCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p kv examples/sample.yaml b.c") + result := test.RunCmd(cmd, "read -p pv examples/sample.yaml b.c") if result.Error != nil { t.Error(result.Error) } @@ -105,7 +105,7 @@ func TestReadWithKeyAndValueCmd(t *testing.T) { func TestReadArrayCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p kv examples/sample.yaml b.e.1.name") + result := test.RunCmd(cmd, "read -p pv examples/sample.yaml b.e.1.name") if result.Error != nil { t.Error(result.Error) } @@ -114,7 +114,7 @@ func TestReadArrayCmd(t *testing.T) { func TestReadDeepSplatCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p kv examples/sample.yaml b.**") + result := test.RunCmd(cmd, "read -p pv examples/sample.yaml b.**") if result.Error != nil { t.Error(result.Error) } @@ -132,7 +132,7 @@ b.e.[1].value: 4 func TestReadDeepSplatWithSuffixCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p kv examples/sample.yaml b.**.name") + result := test.RunCmd(cmd, "read -p pv examples/sample.yaml b.**.name") if result.Error != nil { t.Error(result.Error) } @@ -144,7 +144,7 @@ b.e.[1].name: sam func TestReadWithKeyCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p k examples/sample.yaml b.c") + result := test.RunCmd(cmd, "read -p p examples/sample.yaml b.c") if result.Error != nil { t.Error(result.Error) } @@ -162,7 +162,7 @@ func TestReadAnchorsCmd(t *testing.T) { func TestReadAnchorsWithKeyAndValueCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p kv examples/simple-anchor.yaml foobar.a") + result := test.RunCmd(cmd, "read -p pv examples/simple-anchor.yaml foobar.a") if result.Error != nil { t.Error(result.Error) } @@ -189,7 +189,7 @@ func TestReadMergeAnchorsOverrideCmd(t *testing.T) { func TestReadMergeAnchorsPrefixMatchCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "r -p kv examples/merge-anchor.yaml foobar.th*") + result := test.RunCmd(cmd, "r -p pv examples/merge-anchor.yaml foobar.th*") if result.Error != nil { t.Error(result.Error) } @@ -271,7 +271,7 @@ func TestReadMultiCmd(t *testing.T) { func TestReadMultiWithKeyAndValueCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p vk -d 1 examples/multiple_docs.yaml another.document") + result := test.RunCmd(cmd, "read -p vp -d 1 examples/multiple_docs.yaml another.document") if result.Error != nil { t.Error(result.Error) } @@ -292,7 +292,7 @@ third document`, result.Output) func TestReadMultiAllWithKeyAndValueCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p kv -d* examples/multiple_docs.yaml commonKey") + result := test.RunCmd(cmd, "read -p pv -d* examples/multiple_docs.yaml commonKey") if result.Error != nil { t.Error(result.Error) } @@ -372,7 +372,7 @@ gather_facts: true func TestReadCmd_ArrayYaml_SplatWithKeyAndValueCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p kv examples/array.yaml [*]") + result := test.RunCmd(cmd, "read -p pv examples/array.yaml [*]") if result.Error != nil { t.Error(result.Error) } @@ -394,7 +394,7 @@ func TestReadCmd_ArrayYaml_SplatWithKeyAndValueCmd(t *testing.T) { func TestReadCmd_ArrayYaml_SplatWithKeyCmd(t *testing.T) { cmd := getRootCommand() - result := test.RunCmd(cmd, "read -p k examples/array.yaml [*]") + result := test.RunCmd(cmd, "read -p p examples/array.yaml [*]") if result.Error != nil { t.Error(result.Error) } @@ -494,7 +494,7 @@ func TestReadCmd_Verbose(t *testing.T) { if result.Error != nil { t.Error(result.Error) } - test.AssertResult(t, "2\n", result.Output) + test.AssertResult(t, "2", result.Output) } // func TestReadCmd_ToJson(t *testing.T) { @@ -559,7 +559,7 @@ b: defer test.RemoveTempYamlFile(filename) cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -p kv %s b.there*.c", filename)) + result := test.RunCmd(cmd, fmt.Sprintf("read -p pv %s b.there*.c", filename)) if result.Error != nil { t.Error(result.Error) } @@ -587,7 +587,7 @@ b: defer test.RemoveTempYamlFile(filename) cmd := getRootCommand() - result := test.RunCmd(cmd, fmt.Sprintf("read -p k %s b.there*.c", filename)) + result := test.RunCmd(cmd, fmt.Sprintf("read -p p %s b.there*.c", filename)) if result.Error != nil { t.Error(result.Error) } @@ -871,6 +871,28 @@ func TestWriteCmdScript(t *testing.T) { test.AssertResult(t, expectedOutput, result.Output) } +func TestWriteCmdEmptyScript(t *testing.T) { + content := `b: + c: 3 +` + filename := test.WriteTempYamlFile(content) + defer test.RemoveTempYamlFile(filename) + + updateScript := `` + scriptFilename := test.WriteTempYamlFile(updateScript) + defer test.RemoveTempYamlFile(scriptFilename) + + cmd := getRootCommand() + result := test.RunCmd(cmd, fmt.Sprintf("write --script %s %s", scriptFilename, filename)) + if result.Error != nil { + t.Error(result.Error) + } + expectedOutput := `b: + c: 3 +` + test.AssertResult(t, expectedOutput, result.Output) +} + func TestWriteMultiCmd(t *testing.T) { content := `b: c: 3 diff --git a/yq.go b/yq.go index 5df65ffe..59d26480 100644 --- a/yq.go +++ b/yq.go @@ -107,7 +107,7 @@ yq r -- things.yaml --key-starting-with-dashes.blah RunE: readProperty, } cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") - cmdRead.PersistentFlags().StringVarP(&printMode, "printMode", "p", "v", "print mode (v (values, default), k (keys), kv (key and value pairs)") + cmdRead.PersistentFlags().StringVarP(&printMode, "printMode", "p", "v", "print mode (v (values, default), p (paths), pv (path and value pairs)") return cmdRead } @@ -218,7 +218,7 @@ Note that you can give a create script to perform more sophisticated yaml. This `, RunE: newProperty, } - cmdNew.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for updating yaml") + cmdNew.PersistentFlags().StringVarP(&writeScript, "script", "s", "", "yaml script for creating yaml") cmdNew.PersistentFlags().StringVarP(&customTag, "tag", "t", "", "set yaml tag (e.g. !!int)") return cmdNew } @@ -323,6 +323,11 @@ func appendDocument(originalMatchingNodes []*yqlib.NodeContext, dataBucket yaml. } func printValue(node *yaml.Node, cmd *cobra.Command) error { + if node.Kind == yaml.ScalarNode { + cmd.Print(node.Value) + return nil + } + bufferedWriter := bufio.NewWriter(cmd.OutOrStdout()) defer safelyFlush(bufferedWriter) @@ -346,12 +351,12 @@ func printResults(matchingNodes []*yqlib.NodeContext, cmd *cobra.Command) error for index, mappedDoc := range matchingNodes { switch printMode { - case "k": + case "p": cmd.Print(lib.PathStackToString(mappedDoc.PathStack)) if index < len(matchingNodes)-1 { cmd.Print("\n") } - case "kv", "vk": + case "pv", "vp": // put it into a node and print that. var parentNode = yaml.Node{Kind: yaml.MappingNode} parentNode.Content = make([]*yaml.Node, 2) @@ -601,9 +606,13 @@ func readUpdateCommands(args []string, expectedArgs int, badArgsMessage string) var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0) if writeScript != "" { var parsedCommands = make([]updateCommandParsed, 0) - if err := readData(writeScript, 0, &parsedCommands); err != nil { + + err := readData(writeScript, 0, &parsedCommands) + + if err != nil && err != io.EOF { return nil, err } + log.Debugf("Read write commands file '%v'", parsedCommands) for index := range parsedCommands { parsedCommand := parsedCommands[index]