Print path is more accurate than keys (i think)

This commit is contained in:
Mike Farah 2020-01-11 09:07:39 +11:00
parent 854f5f0fc9
commit 728cbe991a
3 changed files with 97 additions and 22 deletions

View File

@ -1,11 +1,55 @@
# New Features # New Features
- Keeps comments and formatting (e.g. inline arrays)! - 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!
- Can specify yaml tags (e.g. !!int), quoting values no longer sufficient, need to specify the tag value instead.
- JSON output works for all commands! Yaml files with multiple documents are printed out as one JSON document per line.
# Update scripts file format has changed to be more powerful. Comments can be added, and delete commands have been introduced. - 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)
# 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 # Merge command
- New flag 'autocreates' missing entries in target by default, new flag to turn that off. - New flag 'autocreates' missing entries in target by default, new flag to turn that off.

View File

@ -96,7 +96,7 @@ func TestReadCmd(t *testing.T) {
func TestReadWithKeyAndValueCmd(t *testing.T) { func TestReadWithKeyAndValueCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -105,7 +105,7 @@ func TestReadWithKeyAndValueCmd(t *testing.T) {
func TestReadArrayCmd(t *testing.T) { func TestReadArrayCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -114,7 +114,7 @@ func TestReadArrayCmd(t *testing.T) {
func TestReadDeepSplatCmd(t *testing.T) { func TestReadDeepSplatCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -132,7 +132,7 @@ b.e.[1].value: 4
func TestReadDeepSplatWithSuffixCmd(t *testing.T) { func TestReadDeepSplatWithSuffixCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -144,7 +144,7 @@ b.e.[1].name: sam
func TestReadWithKeyCmd(t *testing.T) { func TestReadWithKeyCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -162,7 +162,7 @@ func TestReadAnchorsCmd(t *testing.T) {
func TestReadAnchorsWithKeyAndValueCmd(t *testing.T) { func TestReadAnchorsWithKeyAndValueCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -189,7 +189,7 @@ func TestReadMergeAnchorsOverrideCmd(t *testing.T) {
func TestReadMergeAnchorsPrefixMatchCmd(t *testing.T) { func TestReadMergeAnchorsPrefixMatchCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -271,7 +271,7 @@ func TestReadMultiCmd(t *testing.T) {
func TestReadMultiWithKeyAndValueCmd(t *testing.T) { func TestReadMultiWithKeyAndValueCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -292,7 +292,7 @@ third document`, result.Output)
func TestReadMultiAllWithKeyAndValueCmd(t *testing.T) { func TestReadMultiAllWithKeyAndValueCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -372,7 +372,7 @@ gather_facts: true
func TestReadCmd_ArrayYaml_SplatWithKeyAndValueCmd(t *testing.T) { func TestReadCmd_ArrayYaml_SplatWithKeyAndValueCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -394,7 +394,7 @@ func TestReadCmd_ArrayYaml_SplatWithKeyAndValueCmd(t *testing.T) {
func TestReadCmd_ArrayYaml_SplatWithKeyCmd(t *testing.T) { func TestReadCmd_ArrayYaml_SplatWithKeyCmd(t *testing.T) {
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -494,7 +494,7 @@ func TestReadCmd_Verbose(t *testing.T) {
if result.Error != nil { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
test.AssertResult(t, "2\n", result.Output) test.AssertResult(t, "2", result.Output)
} }
// func TestReadCmd_ToJson(t *testing.T) { // func TestReadCmd_ToJson(t *testing.T) {
@ -559,7 +559,7 @@ b:
defer test.RemoveTempYamlFile(filename) defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -587,7 +587,7 @@ b:
defer test.RemoveTempYamlFile(filename) defer test.RemoveTempYamlFile(filename)
cmd := getRootCommand() 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 { if result.Error != nil {
t.Error(result.Error) t.Error(result.Error)
} }
@ -871,6 +871,28 @@ func TestWriteCmdScript(t *testing.T) {
test.AssertResult(t, expectedOutput, result.Output) 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) { func TestWriteMultiCmd(t *testing.T) {
content := `b: content := `b:
c: 3 c: 3

19
yq.go
View File

@ -107,7 +107,7 @@ yq r -- things.yaml --key-starting-with-dashes.blah
RunE: readProperty, RunE: readProperty,
} }
cmdRead.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)") 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 return cmdRead
} }
@ -218,7 +218,7 @@ Note that you can give a create script to perform more sophisticated yaml. This
`, `,
RunE: newProperty, 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)") cmdNew.PersistentFlags().StringVarP(&customTag, "tag", "t", "", "set yaml tag (e.g. !!int)")
return cmdNew return cmdNew
} }
@ -323,6 +323,11 @@ func appendDocument(originalMatchingNodes []*yqlib.NodeContext, dataBucket yaml.
} }
func printValue(node *yaml.Node, cmd *cobra.Command) error { 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()) bufferedWriter := bufio.NewWriter(cmd.OutOrStdout())
defer safelyFlush(bufferedWriter) defer safelyFlush(bufferedWriter)
@ -346,12 +351,12 @@ func printResults(matchingNodes []*yqlib.NodeContext, cmd *cobra.Command) error
for index, mappedDoc := range matchingNodes { for index, mappedDoc := range matchingNodes {
switch printMode { switch printMode {
case "k": case "p":
cmd.Print(lib.PathStackToString(mappedDoc.PathStack)) cmd.Print(lib.PathStackToString(mappedDoc.PathStack))
if index < len(matchingNodes)-1 { if index < len(matchingNodes)-1 {
cmd.Print("\n") cmd.Print("\n")
} }
case "kv", "vk": case "pv", "vp":
// put it into a node and print that. // put it into a node and print that.
var parentNode = yaml.Node{Kind: yaml.MappingNode} var parentNode = yaml.Node{Kind: yaml.MappingNode}
parentNode.Content = make([]*yaml.Node, 2) 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) var updateCommands []yqlib.UpdateCommand = make([]yqlib.UpdateCommand, 0)
if writeScript != "" { if writeScript != "" {
var parsedCommands = make([]updateCommandParsed, 0) 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 return nil, err
} }
log.Debugf("Read write commands file '%v'", parsedCommands) log.Debugf("Read write commands file '%v'", parsedCommands)
for index := range parsedCommands { for index := range parsedCommands {
parsedCommand := parsedCommands[index] parsedCommand := parsedCommands[index]